Skip to content

Commit

Permalink
Parse type/schema extensions (#258)
Browse files Browse the repository at this point in the history
* Parse type/schema extensions

* Use Type.Named in place of Name

* Translate AST extensions into Schema

* Add type extension logic

* renames

* feedback

* Add extension rendering

* Fixes and test

* Extensions use names rather than TypeRefs

* Add validation, test

* Validate that type exists to extend

* Add scaladoc

* fix rebase error

* Fix rebase issues

* Whitespace

* Removed redundant trait TypeSystemDefinitionOrExtension

* Renamed unExtendedTypes to baseTypes

* TypeExtension tweaks

+ Renamed unextended to baseType
+ Simplified WithFields structures
+ Extension type formatting

* Extension validation now part of schema validation; consistent use of baseType

* Added and reorganised tests; improved validation; removed descriptions from extensions

* Tweaked schema rendering and added round-trip tests for extensions

* Bumped tlBaseVersion to keep MiMa happy

* Removed stray SBT launcher

---------

Co-authored-by: Miles Sabin <[email protected]>
  • Loading branch information
keirlawson and milessabin authored Nov 4, 2023
1 parent d03e237 commit 6aedb58
Show file tree
Hide file tree
Showing 8 changed files with 1,422 additions and 62 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ ThisBuild / scalaVersion := Scala2
ThisBuild / crossScalaVersions := Seq(Scala2, Scala3)
ThisBuild / tlJdkRelease := Some(11)

ThisBuild / tlBaseVersion := "0.15"
ThisBuild / tlBaseVersion := "0.16"
ThisBuild / organization := "org.typelevel"
ThisBuild / organizationName := "Association of Universities for Research in Astronomy, Inc. (AURA)"
ThisBuild / startYear := Some(2019)
Expand Down
47 changes: 47 additions & 0 deletions modules/core/src/main/scala/ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ object Ast {

sealed trait ExecutableDefinition extends Definition
sealed trait TypeSystemDefinition extends Definition
sealed trait TypeSystemExtension extends Definition

sealed trait TypeExtension extends TypeSystemExtension {
def baseType: Type.Named
}

sealed abstract class OperationType(val name: String)
object OperationType {
Expand Down Expand Up @@ -115,6 +120,11 @@ object Ast {
directives: List[Directive]
) extends TypeSystemDefinition

case class SchemaExtension(
rootOperationTypes: List[RootOperationTypeDefinition],
directives: List[Directive]
) extends TypeSystemExtension

case class RootOperationTypeDefinition(
operationType: OperationType,
tpe: Type.Named,
Expand Down Expand Up @@ -200,6 +210,43 @@ object Ast {
locations: List[DirectiveLocation]
) extends TypeSystemDefinition

case class ScalarTypeExtension(
baseType: Type.Named,
directives: List[Directive]
) extends TypeExtension

case class ObjectTypeExtension(
baseType: Type.Named,
fields: List[FieldDefinition],
interfaces: List[Type.Named],
directives: List[Directive]
) extends TypeExtension

case class InterfaceTypeExtension(
baseType: Type.Named,
fields: List[FieldDefinition],
interfaces: List[Type.Named],
directives: List[Directive]
) extends TypeExtension

case class UnionTypeExtension(
baseType: Type.Named,
directives: List[Directive],
members: List[Type.Named]
) extends TypeExtension

case class EnumTypeExtension(
baseType: Type.Named,
directives: List[Directive],
values: List[EnumValueDefinition]
) extends TypeExtension

case class InputObjectTypeExtension(
baseType: Type.Named,
directives: List[Directive],
fields: List[InputValueDefinition],
) extends TypeExtension

sealed trait DirectiveLocation
object DirectiveLocation {
case object QUERY extends DirectiveLocation
Expand Down
52 changes: 51 additions & 1 deletion modules/core/src/main/scala/parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object GraphQLParser {
(whitespace.void | comment).rep0 *> Definition.rep0 <* Parser.end

lazy val Definition: Parser[Ast.Definition] =
ExecutableDefinition | TypeSystemDefinition // | TypeSystemExtension
ExecutableDefinition | TypeSystemDefinition | TypeSystemExtension

lazy val TypeSystemDefinition: Parser[Ast.TypeSystemDefinition] = {
val SchemaDefinition: Parser[Ast.SchemaDefinition] =
Expand Down Expand Up @@ -92,6 +92,56 @@ object GraphQLParser {
}
}

lazy val TypeSystemExtension: Parser[Ast.TypeSystemExtension] = {

val SchemaExtension: Parser[Ast.SchemaExtension] =
((keyword("schema") *> Directives.?) ~ braces(RootOperationTypeDefinition.rep0).?).map {
case (dirs, rootdefs) => Ast.SchemaExtension(rootdefs.getOrElse(Nil), dirs.getOrElse(Nil))
}

val TypeExtension: Parser[Ast.TypeExtension] = {

val ScalarTypeExtension: Parser[Ast.ScalarTypeExtension] =
((keyword("scalar") *> NamedType) ~ Directives.?).map {
case (((name), dirs)) => Ast.ScalarTypeExtension(name, dirs.getOrElse(Nil))
}

val ObjectTypeExtension: Parser[Ast.ObjectTypeExtension] =
((keyword("type") *> NamedType) ~ ImplementsInterfaces.? ~ Directives.? ~ FieldsDefinition.?).map {
case (((name, ifs), dirs), fields) => Ast.ObjectTypeExtension(name, fields.getOrElse(Nil), ifs.getOrElse(Nil), dirs.getOrElse(Nil))
}

val InterfaceTypeExtension: Parser[Ast.InterfaceTypeExtension] =
((keyword("interface") *> NamedType) ~ ImplementsInterfaces.? ~ Directives.? ~ FieldsDefinition.?).map {
case (((name, ifs), dirs), fields) => Ast.InterfaceTypeExtension(name, fields.getOrElse(Nil), ifs.getOrElse(Nil), dirs.getOrElse(Nil))
}

val UnionTypeExtension: Parser[Ast.UnionTypeExtension] =
((keyword("union") *> NamedType) ~ Directives.? ~ UnionMemberTypes.?).map {
case (((name), dirs), members) => Ast.UnionTypeExtension(name, dirs.getOrElse(Nil), members.getOrElse(Nil))
}

val EnumTypeExtension: Parser[Ast.EnumTypeExtension] =
((keyword("enum") *> NamedType) ~ Directives.? ~ EnumValuesDefinition.?).map {
case (((name), dirs), values) => Ast.EnumTypeExtension(name, dirs.getOrElse(Nil), values.getOrElse(Nil))
}

val InputObjectTypeExtension: Parser[Ast.InputObjectTypeExtension] =
((keyword("input") *> NamedType) ~ Directives.? ~ InputFieldsDefinition.?).map {
case (((name), dirs), fields) => Ast.InputObjectTypeExtension(name, dirs.getOrElse(Nil), fields.getOrElse(Nil))
}

ScalarTypeExtension|
ObjectTypeExtension|
InterfaceTypeExtension|
UnionTypeExtension|
EnumTypeExtension|
InputObjectTypeExtension
}

keyword("extend") *> (SchemaExtension | TypeExtension)
}

lazy val RootOperationTypeDefinition: Parser[Ast.RootOperationTypeDefinition] =
(OperationType ~ keyword(":") ~ NamedType ~ Directives).map {
case (((optpe, _), tpe), dirs) => Ast.RootOperationTypeDefinition(optpe, tpe, dirs)
Expand Down
Loading

0 comments on commit 6aedb58

Please sign in to comment.