Skip to content

Commit

Permalink
Implemented introspection-based schema materializer. Closes #21
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegIlyenko committed Feb 28, 2016
1 parent 062467d commit b27f8fe
Show file tree
Hide file tree
Showing 12 changed files with 709 additions and 40 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Upcoming

* `SchemaRenderer.renderSchema` is not able to render `Schema` objects and only introspection results (#114). This can be useful if you already
* `SchemaRenderer.renderSchema` is now able to render `Schema` objects and only introspection results (#114). This can be useful if you already
have schema in memory and don't want to execute an introspection query against the schema in order to render it.
* Query validation rule: Unique variable names (#112)
* Add suggested types to incorrect field message (#111)
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ libraryDependencies ++= Seq(
"org.parboiled" %% "parboiled" % "2.1.1",

// marshalling
"org.sangria-graphql" %% "sangria-marshalling-api" % "0.1.0",
"org.sangria-graphql" %% "sangria-marshalling-api" % "0.1.1",

// testing
"org.scalatest" %% "scalatest" % "2.2.6" % "test",
"org.sangria-graphql" %% "sangria-marshalling-testkit" % "0.1.1" % "test",
"org.sangria-graphql" %% "sangria-spray-json" % "0.1.0" % "test"
"org.sangria-graphql" %% "sangria-marshalling-testkit" % "0.1.2" % "test",
"org.sangria-graphql" %% "sangria-spray-json" % "0.2.0" % "test"
)

git.remoteRepo := "git@github.com:sangria-graphql/sangria.git"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ object IntrospectionParser {
directives = mapFieldOpt(schema, "directives") map um.getListValue getOrElse Vector.empty map (i parseDirective(i, path :+ "directives")))

private def parseNamedTypeRef[In : InputUnmarshaller](in: In, path: Vector[String]) =
IntrospectionNamedTypeRef(mapStringFieldOpt(in, "kind", path) getOrElse "OBJECT", mapStringField(in, "name", path))
IntrospectionNamedTypeRef(mapStringFieldOpt(in, "kind", path) map TypeKind.fromString getOrElse TypeKind.Object, mapStringField(in, "name", path))

private def parseTypeRef[In : InputUnmarshaller](in: In, path: Vector[String]): IntrospectionTypeRef =
mapStringField(in, "kind", path) match {
Expand All @@ -128,13 +128,13 @@ object IntrospectionParser {
}

private def stringValue[In : InputUnmarshaller](value: In, path: Vector[String]) =
um.getScalarValue(value) match {
um.getScalaScalarValue(value) match {
case s: String s
case _ error(s"Expected String but got '${um.render(value)}' at path ${path mkString "."}")
}

private def booleanValue[In : InputUnmarshaller](value: In, path: Vector[String]) =
um.getScalarValue(value) match {
um.getScalaScalarValue(value) match {
case b: Boolean b
case _ error(s"Expected Boolean but got '${um.render(value)}' at path ${path mkString "."}")
}
Expand Down
22 changes: 11 additions & 11 deletions src/main/scala/sangria/introspection/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,52 @@ case class IntrospectionSchema(
directives: Seq[IntrospectionDirective])

sealed trait IntrospectionType {
def kind: String
def kind: TypeKind.Value
def name: String
def description: Option[String]
}

case class IntrospectionScalarType(
name: String,
description: Option[String]) extends IntrospectionType {
val kind = "SCALAR"
val kind = TypeKind.Scalar
}

case class IntrospectionObjectType(
name: String,
description: Option[String],
fields: Seq[IntrospectionField],
interfaces: Seq[IntrospectionNamedTypeRef]) extends IntrospectionType {
val kind = "OBJECT"
val kind = TypeKind.Object
}

case class IntrospectionInputObjectType(
name: String,
description: Option[String],
inputFields: Seq[IntrospectionInputValue]) extends IntrospectionType {
val kind = "INPUT_OBJECT"
val kind = TypeKind.InputObject
}

case class IntrospectionInterfaceType(
name: String,
description: Option[String],
fields: Seq[IntrospectionField],
possibleTypes: Seq[IntrospectionNamedTypeRef]) extends IntrospectionType {
val kind = "INTERFACE"
val kind = TypeKind.Interface
}

case class IntrospectionUnionType(
name: String,
description: Option[String],
possibleTypes: Seq[IntrospectionNamedTypeRef]) extends IntrospectionType {
val kind = "UNION"
val kind = TypeKind.Union
}

case class IntrospectionEnumType(
name: String,
description: Option[String],
enumValues: Seq[IntrospectionEnumValue]) extends IntrospectionType {
val kind = "ENUM"
val kind = TypeKind.Enum
}

case class IntrospectionField(
Expand All @@ -77,17 +77,17 @@ case class IntrospectionInputValue(
defaultValue: Option[String])

sealed trait IntrospectionTypeRef {
def kind: String
def kind: TypeKind.Value
}

case class IntrospectionNamedTypeRef(kind: String, name: String) extends IntrospectionTypeRef
case class IntrospectionNamedTypeRef(kind: TypeKind.Value, name: String) extends IntrospectionTypeRef

case class IntrospectionListTypeRef(ofType: IntrospectionTypeRef) extends IntrospectionTypeRef {
val kind = "LIST"
val kind = TypeKind.List
}

case class IntrospectionNonNullTypeRef(ofType: IntrospectionTypeRef) extends IntrospectionTypeRef {
val kind = "NON_NULL"
val kind = TypeKind.NonNull
}

case class IntrospectionDirective(
Expand Down
17 changes: 17 additions & 0 deletions src/main/scala/sangria/introspection/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import scala.util.Success
package object introspection {
object TypeKind extends Enumeration {
val Scalar, Object, Interface, Union, Enum, InputObject, List, NonNull = Value

def fromString(kind: String): TypeKind.Value = kind match {
case "SCALAR" Scalar
case "OBJECT" Object
case "INTERFACE" Interface
case "UNION" Union
case "ENUM" Enum
case "INPUT_OBJECT" InputObject
case "LIST" List
case "NON_NULL" NonNull
}
}

val __TypeKind = EnumType("__TypeKind", Some("An enum describing what kind of type a given `__Type` is."), List(
Expand Down Expand Up @@ -244,6 +255,12 @@ package object introspection {

val MetaFieldNames = Set(SchemaMetaField.name, TypeMetaField.name, TypeNameMetaField.name)

val IntrospectionTypes: List[Type with Named] =
__Schema :: __TypeKind :: __Type :: __Field :: __InputValue :: __EnumValue :: __Directive :: Nil

val IntrospectionTypesByName: Map[String, Type with Named] =
IntrospectionTypes.groupBy(_.name).mapValues(_.head)

lazy val Success(introspectionQuery) = QueryParser.parse(
"""
|query IntrospectionQuery {
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/sangria/marshalling/queryAst.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sangria.marshalling

import sangria.ast
import sangria.parser.QueryParser
import sangria.renderer.QueryRenderer

object queryAst {
Expand All @@ -26,6 +27,10 @@ object queryAst {

implicit def queryAstFromInput[T <: ast.Value]: FromInput[T] =
QueryAstFromInput.asInstanceOf[FromInput[T]]

implicit object QueryAstInputParser extends InputParser[ast.Value] {
def parse(str: String) = QueryParser.parseInput(str)
}
}

class QueryAstInputUnmarshaller extends InputUnmarshaller[ast.Value] {
Expand Down
13 changes: 4 additions & 9 deletions src/main/scala/sangria/renderer/SchemaRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ object SchemaRenderer {
else
""

private def renderTypeName(tpe: IntrospectionTypeRef): String =
def renderTypeName(tpe: IntrospectionTypeRef): String =
tpe match {
case IntrospectionListTypeRef(ofType) s"[${renderTypeName(ofType)}]"
case IntrospectionNonNullTypeRef(ofType) s"${renderTypeName(ofType)}!"
Expand Down Expand Up @@ -191,19 +191,14 @@ object SchemaRenderer {
schema.typeList filterNot isBuiltInType sortBy (_.name) map renderType mkString TypeSeparator

def renderIntrospectionSchema(introspectionSchema: IntrospectionSchema): String =
introspectionSchema.types filter (tpe isIntrospectionType(tpe.name)) sortBy (_.name) map renderType mkString TypeSeparator
introspectionSchema.types filter (tpe Schema.isIntrospectionType(tpe.name)) sortBy (_.name) map renderType mkString TypeSeparator

def renderIntrospectionSchema[T: InputUnmarshaller](introspectionResult: T): String =
renderIntrospectionSchema(IntrospectionParser parse introspectionResult)

private def isBuiltIn(tpe: IntrospectionType) =
isIntrospectionType(tpe.name) || isBuiltInScalar(tpe.name)
Schema.isBuiltInType(tpe.name)

private def isBuiltInType(tpe: Type with Named) =
isIntrospectionType(tpe.name) || isBuiltInScalar(tpe.name)

def isIntrospectionType(tpeName: String) = tpeName startsWith "__"

def isBuiltInScalar(tpeName: String) =
sangria.schema.BuiltinScalars.exists(_.name == tpeName)
Schema.isBuiltInType(tpe.name)
}
Loading

0 comments on commit b27f8fe

Please sign in to comment.