Skip to content

Commit

Permalink
Add Parser support for Type System Extensions (#266)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorge-vasquez-2301 authored Mar 12, 2020
1 parent 7e82950 commit 7d5ded8
Show file tree
Hide file tree
Showing 4 changed files with 773 additions and 15 deletions.
160 changes: 154 additions & 6 deletions core/src/main/scala/caliban/parsing/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import caliban.parsing.adt.Definition.ExecutableDefinition._
import caliban.parsing.adt.Definition.TypeSystemDefinition.DirectiveLocation._
import caliban.parsing.adt.Definition.TypeSystemDefinition._
import caliban.parsing.adt.Definition.TypeSystemDefinition.TypeDefinition._
import caliban.parsing.adt.Definition.TypeSystemExtension._
import caliban.parsing.adt.Definition.TypeSystemExtension.TypeExtension._
import caliban.parsing.adt.Selection._
import caliban.parsing.adt.Type._
import caliban.parsing.adt._
Expand Down Expand Up @@ -116,7 +118,7 @@ object Parser {
private def argument[_: P]: P[(String, InputValue)] = P(name ~ ":" ~ value)
private def arguments[_: P]: P[Map[String, InputValue]] = P("(" ~/ argument.rep ~ ")").map(_.toMap)

private def directive[_: P]: P[Directive] = P(Index ~ "@" ~/ name ~ arguments.?).map {
private def directive[_: P]: P[Directive] = P(Index ~ "@" ~ name ~ arguments.?).map {
case (index, name, arguments) => Directive(name, arguments.getOrElse(Map()), index)
}
private def directives[_: P]: P[List[Directive]] = P(directive.rep).map(_.toList)
Expand Down Expand Up @@ -231,11 +233,10 @@ object Parser {
EnumValueDefinition(description.map(_.value), enumValue, directives.getOrElse(Nil))
}

private def enumName[_: P]: P[String] = name.filter(s => s != "true" && s != "false" && s != "null")

private def enumTypeDefinition[_: P]: P[EnumTypeDefinition] =
P(
stringValue.? ~ "enum" ~/ name
.filter(s => s != "true" && s != "false" && s != "null") ~ directives.? ~ "{" ~ enumValueDefinition.rep ~ "}"
).map {
P(stringValue.? ~ "enum" ~/ enumName ~ directives.? ~ "{" ~ enumValueDefinition.rep ~ "}").map {
case (description, name, directives, enumValuesDefinition) =>
EnumTypeDefinition(description.map(_.value), name, directives.getOrElse(Nil), enumValuesDefinition.toList)
}
Expand Down Expand Up @@ -266,6 +267,142 @@ object Parser {
)
}

private def schemaExtensionWithOptionalDirectivesAndOperations[_: P]: P[SchemaExtension] =
P(directives.? ~ "{" ~ rootOperationTypeDefinition.rep ~ "}").map {
case (directives, ops) =>
val opsMap = ops.toMap
SchemaExtension(
directives.getOrElse(Nil),
opsMap.get(OperationType.Query).map(_.name),
opsMap.get(OperationType.Mutation).map(_.name),
opsMap.get(OperationType.Subscription).map(_.name)
)
}

private def schemaExtensionWithDirectives[_: P]: P[SchemaExtension] =
P(directives).map(SchemaExtension(_, None, None, None))

private def schemaExtension[_: P]: P[SchemaExtension] =
P("extend schema" ~/ (schemaExtensionWithOptionalDirectivesAndOperations | schemaExtensionWithDirectives))

private def scalarTypeExtension[_: P]: P[ScalarTypeExtension] =
P("extend scalar" ~/ name ~ directives).map {
case (name, directives) =>
ScalarTypeExtension(name, directives)
}

private def objectTypeExtensionWithOptionalInterfacesOptionalDirectivesAndFields[_: P]: P[ObjectTypeExtension] =
P(name ~ implements.? ~ directives.? ~ "{" ~ fieldDefinition.rep ~ "}").map {
case (name, implements, directives, fields) =>
ObjectTypeExtension(
name,
implements.getOrElse(Nil),
directives.getOrElse(Nil),
fields.toList
)
}

private def objectTypeExtensionWithOptionalInterfacesAndDirectives[_: P]: P[ObjectTypeExtension] =
P(name ~ implements.? ~ directives ~ !("{" ~ fieldDefinition.rep ~ "}")).map {
case (name, implements, directives) =>
ObjectTypeExtension(
name,
implements.getOrElse(Nil),
directives,
Nil
)
}

private def objectTypeExtensionWithInterfaces[_: P]: P[ObjectTypeExtension] =
P(name ~ implements).map {
case (name, implements) =>
ObjectTypeExtension(
name,
implements,
Nil,
Nil
)
}

private def objectTypeExtension[_: P]: P[ObjectTypeExtension] =
P(
"extend type" ~/ (
objectTypeExtensionWithOptionalInterfacesOptionalDirectivesAndFields |
objectTypeExtensionWithOptionalInterfacesAndDirectives |
objectTypeExtensionWithInterfaces
)
)

private def interfaceTypeExtensionWithOptionalDirectivesAndFields[_: P]: P[InterfaceTypeExtension] =
P(name ~ directives.? ~ "{" ~ fieldDefinition.rep ~ "}").map {
case (name, directives, fields) =>
InterfaceTypeExtension(name, directives.getOrElse(Nil), fields.toList)
}

private def interfaceTypeExtensionWithDirectives[_: P]: P[InterfaceTypeExtension] =
P(name ~ directives).map {
case (name, directives) =>
InterfaceTypeExtension(name, directives, Nil)
}

private def interfaceTypeExtension[_: P]: P[InterfaceTypeExtension] =
P(
"extend interface" ~/ (
interfaceTypeExtensionWithOptionalDirectivesAndFields |
interfaceTypeExtensionWithDirectives
)
)

private def unionTypeExtensionWithOptionalDirectivesAndUnionMembers[_: P]: P[UnionTypeExtension] =
P(name ~ directives.? ~ "=" ~ ("|".? ~ namedType) ~ ("|" ~ namedType).rep).map {
case (name, directives, m, ms) =>
UnionTypeExtension(name, directives.getOrElse(Nil), (m :: ms.toList).map(_.name))
}

private def unionTypeExtensionWithDirectives[_: P]: P[UnionTypeExtension] =
P(name ~ directives).map {
case (name, directives) =>
UnionTypeExtension(name, directives, Nil)
}

private def unionTypeExtension[_: P]: P[UnionTypeExtension] =
P("extend union" ~/ (unionTypeExtensionWithOptionalDirectivesAndUnionMembers | unionTypeExtensionWithDirectives))

private def enumTypeExtensionWithOptionalDirectivesAndValues[_: P]: P[EnumTypeExtension] =
P(enumName ~ directives.? ~ "{" ~ enumValueDefinition.rep ~ "}").map {
case (name, directives, enumValuesDefinition) =>
EnumTypeExtension(name, directives.getOrElse(Nil), enumValuesDefinition.toList)
}

private def enumTypeExtensionWithDirectives[_: P]: P[EnumTypeExtension] =
P(enumName ~ directives).map {
case (name, directives) =>
EnumTypeExtension(name, directives, Nil)
}

private def enumTypeExtension[_: P]: P[EnumTypeExtension] =
P("extend enum" ~/ (enumTypeExtensionWithOptionalDirectivesAndValues | enumTypeExtensionWithDirectives))

private def inputObjectTypeExtensionWithOptionalDirectivesAndFields[_: P]: P[InputObjectTypeExtension] =
P(name ~ directives.? ~ "{" ~ argumentDefinition.rep ~ "}").map {
case (name, directives, fields) =>
InputObjectTypeExtension(name, directives.getOrElse(Nil), fields.toList)
}

private def inputObjectTypeExtensionWithDirectives[_: P]: P[InputObjectTypeExtension] =
P(name ~ directives).map {
case (name, directives) =>
InputObjectTypeExtension(name, directives, Nil)
}

private def inputObjectTypeExtension[_: P]: P[InputObjectTypeExtension] =
P(
"extend input" ~/ (
inputObjectTypeExtensionWithOptionalDirectivesAndFields |
inputObjectTypeExtensionWithDirectives
)
)

private def directiveLocation[_: P]: P[DirectiveLocation] =
P(
StringIn(
Expand Down Expand Up @@ -331,7 +468,18 @@ object Parser {
private def executableDefinition[_: P]: P[ExecutableDefinition] =
P(operationDefinition | fragmentDefinition)

private def definition[_: P]: P[Definition] = executableDefinition | typeSystemDefinition
private def typeExtension[_: P]: P[TypeExtension] =
objectTypeExtension |
interfaceTypeExtension |
inputObjectTypeExtension |
enumTypeExtension |
unionTypeExtension |
scalarTypeExtension

private def typeSystemExtension[_: P]: P[TypeSystemExtension] =
schemaExtension | typeExtension

private def definition[_: P]: P[Definition] = executableDefinition | typeSystemDefinition | typeSystemExtension

private def document[_: P]: P[ParsedDocument] =
P(Start ~ ignored ~ definition.rep ~ ignored ~ End).map(seq => ParsedDocument(seq.toList))
Expand Down
59 changes: 58 additions & 1 deletion core/src/main/scala/caliban/parsing/adt/Definition.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package caliban.parsing.adt

import caliban.InputValue
import caliban.parsing.adt.Definition.TypeSystemDefinition.TypeDefinition.InputValueDefinition
import caliban.parsing.adt.Definition.TypeSystemDefinition.TypeDefinition.{
EnumValueDefinition,
FieldDefinition,
InputValueDefinition
}
import caliban.parsing.adt.Type.NamedType

sealed trait Definition
Expand Down Expand Up @@ -133,4 +137,57 @@ object Definition {
}

}

sealed trait TypeSystemExtension extends Definition

object TypeSystemExtension {

case class SchemaExtension(
directives: List[Directive],
query: Option[String],
mutation: Option[String],
subscription: Option[String]
) extends TypeSystemExtension

sealed trait TypeExtension extends TypeSystemExtension

object TypeExtension {

case class ScalarTypeExtension(
name: String,
directives: List[Directive]
) extends TypeExtension

case class ObjectTypeExtension(
name: String,
implements: List[NamedType],
directives: List[Directive],
fields: List[FieldDefinition]
) extends TypeExtension

case class InterfaceTypeExtension(
name: String,
directives: List[Directive],
fields: List[FieldDefinition]
) extends TypeExtension

case class UnionTypeExtension(
name: String,
directives: List[Directive],
memberTypes: List[String]
) extends TypeExtension

case class EnumTypeExtension(
name: String,
directives: List[Directive],
enumValuesDefinition: List[EnumValueDefinition]
) extends TypeExtension

case class InputObjectTypeExtension(
name: String,
directives: List[Directive],
fields: List[InputValueDefinition]
) extends TypeExtension
}
}
}
25 changes: 18 additions & 7 deletions core/src/main/scala/caliban/validation/Validator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import caliban.introspection.Introspector
import caliban.introspection.adt._
import caliban.parsing.SourceMapper
import caliban.parsing.adt.Definition.ExecutableDefinition.{ FragmentDefinition, OperationDefinition }
import caliban.parsing.adt.Definition.TypeSystemDefinition
import caliban.parsing.adt.Definition.{ TypeSystemDefinition, TypeSystemExtension }
import caliban.parsing.adt.OperationType._
import caliban.parsing.adt.Selection.{ Field, FragmentSpread, InlineFragment }
import caliban.parsing.adt.Type.NamedType
Expand Down Expand Up @@ -101,7 +101,7 @@ object Validator {
}

private def check(document: Document, rootType: RootType): IO[ValidationError, Map[String, FragmentDefinition]] = {
val (operations, fragments, _) = collectDefinitions(document)
val (operations, fragments, _, _) = collectDefinitions(document)
for {
fragmentMap <- validateFragments(fragments)
selectionSets = collectSelectionSets(operations.flatMap(_.selectionSet) ++ fragments.flatMap(_.selectionSet))
Expand All @@ -118,13 +118,23 @@ object Validator {

private def collectDefinitions(
document: Document
): (List[OperationDefinition], List[FragmentDefinition], List[TypeSystemDefinition]) =
): (List[OperationDefinition], List[FragmentDefinition], List[TypeSystemDefinition], List[TypeSystemExtension]) =
document.definitions.foldLeft(
(List.empty[OperationDefinition], List.empty[FragmentDefinition], List.empty[TypeSystemDefinition])
(
List.empty[OperationDefinition],
List.empty[FragmentDefinition],
List.empty[TypeSystemDefinition],
List.empty[TypeSystemExtension]
)
) {
case ((operations, fragments, types), o: OperationDefinition) => (o :: operations, fragments, types)
case ((operations, fragments, types), f: FragmentDefinition) => (operations, f :: fragments, types)
case ((operations, fragments, types), t: TypeSystemDefinition) => (operations, fragments, t :: types)
case ((operations, fragments, types, extensions), o: OperationDefinition) =>
(o :: operations, fragments, types, extensions)
case ((operations, fragments, types, extensions), f: FragmentDefinition) =>
(operations, f :: fragments, types, extensions)
case ((operations, fragments, types, extensions), t: TypeSystemDefinition) =>
(operations, fragments, t :: types, extensions)
case ((operations, fragments, types, extensions), e: TypeSystemExtension) =>
(operations, fragments, types, e :: extensions)
}

private def collectVariablesUsed(context: Context, selectionSet: List[Selection]): Set[String] = {
Expand Down Expand Up @@ -309,6 +319,7 @@ object Validator {
}
case _: FragmentDefinition => IO.unit
case _: TypeSystemDefinition => IO.unit
case _: TypeSystemExtension => IO.unit
}
.unit

Expand Down
Loading

0 comments on commit 7d5ded8

Please sign in to comment.