All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sangria.introspection.package.scala Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
package sangria

import sangria.parser.QueryParser
import sangria.parser.DeliveryScheme.Throw
import sangria.schema._

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(
    EnumValue("SCALAR", value = TypeKind.Scalar, description = Some("Indicates this type is a scalar.")),
    EnumValue("OBJECT", value = TypeKind.Object, description = Some(
      "Indicates this type is an object. " +
      "`fields` and `interfaces` are valid fields.")),
    EnumValue("INTERFACE", value = TypeKind.Interface, description = Some(
      "Indicates this type is an interface. " +
      "`fields` and `possibleTypes` are valid fields.")),
    EnumValue("UNION", value = TypeKind.Union, description = Some(
      "Indicates this type is a union. " +
      "`possibleTypes` is a valid field.")),
    EnumValue("ENUM", value = TypeKind.Enum, description = Some(
      "Indicates this type is an enum. " +
      "`enumValues` is a valid field.")),
    EnumValue("INPUT_OBJECT", value = TypeKind.InputObject, description = Some(
      "Indicates this type is an input object. " +
      "`inputFields` is a valid field.")),
    EnumValue("LIST", value = TypeKind.List, description = Some(
      "Indicates this type is a list. " +
      "`ofType` is a valid field.")),
    EnumValue("NON_NULL", value = TypeKind.NonNull, description = Some(
      "Indicates this type is a non-null. " +
      "`ofType` is a valid field."))
  ))

  val __DirectiveLocation = EnumType("__DirectiveLocation",
    Some("A Directive can be adjacent to many parts of the GraphQL language, a " +
         "__DirectiveLocation describes one such possible adjacencies."),
    List(
      EnumValue("QUERY", value = DirectiveLocation.Query,
        description = Some("Location adjacent to a query operation.")),
      EnumValue("MUTATION", value = DirectiveLocation.Mutation,
        description = Some("Location adjacent to a mutation operation.")),
      EnumValue("SUBSCRIPTION", value = DirectiveLocation.Subscription,
        description = Some("Location adjacent to a subscription operation.")),
      EnumValue("FIELD", value = DirectiveLocation.Field,
        description = Some("Location adjacent to a field.")),
      EnumValue("FRAGMENT_DEFINITION", value = DirectiveLocation.FragmentDefinition,
        description = Some("Location adjacent to a fragment definition.")),
      EnumValue("FRAGMENT_SPREAD", value = DirectiveLocation.FragmentSpread,
        description = Some("Location adjacent to a fragment spread.")),
      EnumValue("INLINE_FRAGMENT", value = DirectiveLocation.InlineFragment,
        description = Some("Location adjacent to an inline fragment.")),
      EnumValue("VARIABLE_DEFINITION", value = DirectiveLocation.VariableDefinition,
        description = Some("Location adjacent to a variable definition.")),

      EnumValue("SCHEMA", value = DirectiveLocation.Schema,
        description = Some("Location adjacent to a schema definition.")),
      EnumValue("SCALAR", value = DirectiveLocation.Scalar,
        description = Some("Location adjacent to a scalar definition.")),
      EnumValue("OBJECT", value = DirectiveLocation.Object,
        description = Some("Location adjacent to an object type definition.")),
      EnumValue("FIELD_DEFINITION", value = DirectiveLocation.FieldDefinition,
        description = Some("Location adjacent to a field definition.")),
      EnumValue("ARGUMENT_DEFINITION", value = DirectiveLocation.ArgumentDefinition,
        description = Some("Location adjacent to an argument definition.")),
      EnumValue("INTERFACE", value = DirectiveLocation.Interface,
        description = Some("Location adjacent to an interface definition.")),
      EnumValue("UNION", value = DirectiveLocation.Union,
        description = Some("Location adjacent to a union definition.")),
      EnumValue("ENUM", value = DirectiveLocation.Enum,
        description = Some("Location adjacent to an enum definition.")),
      EnumValue("ENUM_VALUE", value = DirectiveLocation.EnumValue,
        description = Some("Location adjacent to an enum value definition.")),
      EnumValue("INPUT_OBJECT", value = DirectiveLocation.InputObject,
        description = Some("INPUT_OBJECT")),
      EnumValue("INPUT_FIELD_DEFINITION", value = DirectiveLocation.InputFieldDefinition,
        description = Some("Location adjacent to an input object field definition."))))

  val __Field = ObjectType(
    name = "__Field",
    description =
      "Object and Interface types are described by a list of Fields, each of " +
      "which has a name, potentially a list of arguments, and a return type.",
    fieldsFn = () => List[Field[Unit, Field[_, _]]](
      Field("name", StringType, resolve = _.value.name),
      Field("description", OptionType(StringType), resolve = _.value.description),
      Field("args", ListType(__InputValue), resolve = _.value.arguments),
      Field("type", __Type, resolve = false -> _.value.fieldType),
      Field("isDeprecated", BooleanType, resolve = _.value.deprecationReason.isDefined),
      Field("deprecationReason", OptionType(StringType), resolve = _.value.deprecationReason))
  )


  val includeDeprecated = Argument("includeDeprecated", OptionInputType(BooleanType), false)

  private def getKind(value: (Boolean, Type)) = {
    def identifyKind(t: Type, optional: Boolean): TypeKind.Value = t match {
      case OptionType(ofType) => identifyKind(ofType, true)
      case OptionInputType(ofType) => identifyKind(ofType, true)
      case _ if !optional => TypeKind.NonNull
      case _: ScalarType[_] => TypeKind.Scalar
      case _: ScalarAlias[_, _] => TypeKind.Scalar
      case _: ObjectType[_, _] => TypeKind.Object
      case _: InterfaceType[_, _] => TypeKind.Interface
      case _: UnionType[_] => TypeKind.Union
      case _: EnumType[_] => TypeKind.Enum
      case _: InputObjectType[_] => TypeKind.InputObject
      case _: ListType[_] | _: ListInputType[_] => TypeKind.List
    }

    val (fromTypeList, tpe) = value

    identifyKind(tpe, fromTypeList)
  }

  private def findNamed(tpe: Type): Option[Type with Named] = tpe match {
    case o: OptionType[_] => findNamed(o.ofType)
    case o: OptionInputType[_] => findNamed(o.ofType)
    case l: ListType[_] => findNamed(l.ofType)
    case l: ListInputType[_] => findNamed(l.ofType)
    case n: Type with Named => Some(n)
    case _ => None
  }

  private def findListType(tpe: Type): Option[Type] = tpe match {
    case o: OptionType[_] => findListType(o.ofType)
    case o: OptionInputType[_] => findListType(o.ofType)
    case l: ListType[_] => Some(l.ofType)
    case l: ListInputType[_] => Some(l.ofType)
    case _ => None
  }

  val __Type: ObjectType[Unit, (Boolean, Type)] = ObjectType(
    name = "__Type",
    description =
      "The fundamental unit of any GraphQL Schema is the type. There are " +
      "many kinds of types in GraphQL as represented by the `__TypeKind` enum." +
      "\n\nDepending on the kind of a type, certain fields describe " +
      "information about that type. Scalar types provide no information " +
      "beyond a name and description, while Enum types provide their values. " +
      "Object and Interface types provide the fields they describe. Abstract " +
      "types, Union and Interface, provide the Object types possible " +
      "at runtime. List and NonNull types compose other types.",
    fieldsFn = () => List[Field[Unit, (Boolean, Type)]](
      Field("kind", __TypeKind, resolve = ctx => getKind(ctx.value)),
      Field("name", OptionType(StringType), resolve = ctx => getKind(ctx.value) match {
        case TypeKind.NonNull | TypeKind.List => None
        case _ => findNamed(ctx.value._2) map (_.name)
      }),
      Field("description", OptionType(StringType), resolve = ctx => getKind(ctx.value) match {
        case TypeKind.NonNull | TypeKind.List => None
        case _ => findNamed(ctx.value._2) flatMap (_.description)
      }),
      Field("fields", OptionType(ListType(__Field)),
        arguments = includeDeprecated :: Nil,
        resolve = ctx => {
          val incDep = ctx.arg(includeDeprecated)
          val (_, tpe) = ctx.value

          tpe match {
            case t: ObjectLikeType[_, _] if incDep => Some(t.uniqueFields.asInstanceOf[Vector[Field[_, _]]])
            case t: ObjectLikeType[_, _] => Some(t.uniqueFields.asInstanceOf[Vector[Field[_, _]]].filter(_.deprecationReason.isEmpty))
            case _ => None
          }
        }),
      Field("interfaces", OptionType(ListType(__Type)), resolve = _.value._2 match {
        case t: ObjectType[_, _] => Some(t.allInterfaces.asInstanceOf[Vector[Type]] map (true -> _))
        case _ => None
      }),
      Field("possibleTypes", OptionType(ListType(__Type)), resolve = ctx => ctx.value._2 match {
        case t: AbstractType => ctx.schema.possibleTypes.get(t.name) map { tpe =>
          t match {
            case _: UnionType[_] => tpe map (true -> _)
            case _ => tpe sortBy (_.name) map (true -> _)
          }
        }
        case _ => None
      }),
      Field("enumValues", OptionType(ListType(__EnumValue)),
        arguments = includeDeprecated :: Nil,
        resolve = ctx => {
          val incDep = ctx.arg(includeDeprecated)

          ctx.value._2 match {
            case enum: EnumType[_] if incDep => Some(enum.values)
            case enum: EnumType[_] => Some(enum.values.filter(_.deprecationReason.isEmpty))
            case _ => None
          }
        }),
      Field("inputFields", OptionType(ListType(__InputValue)), resolve = _.value._2 match {
        case io: InputObjectType[_] => Some(io.fields)
        case _ => None
      }),
      Field("ofType", OptionType(__Type), resolve = ctx => getKind(ctx.value) match {
        case TypeKind.NonNull => Some(true -> ctx.value._2)
        case TypeKind.List => findListType(ctx.value._2) map (false -> _)
        case _ => None
      }))
  )

  val __InputValue: ObjectType[Unit, InputValue[_]] = ObjectType(
    name = "__InputValue",
    description =
      "Arguments provided to Fields or Directives and the input fields of an " +
      "InputObject are represented as Input Values which describe their type " +
      "and optionally a default value.",
    fields = List[Field[Unit, InputValue[_]]](
      Field("name", StringType, resolve = _.value.name),
      Field("description", OptionType(StringType), resolve = _.value.description),
      Field("type", __Type, resolve = false -> _.value.inputValueType),
      Field("defaultValue", OptionType(StringType),
        description = Some("A GraphQL-formatted string representing the default value for this input value."),
        resolve = ctx => ctx.value.defaultValue.flatMap(ctx.renderInputValueCompact(_, ctx.value.inputValueType)))
    ))

  val __EnumValue: ObjectType[Unit, EnumValue[_]] = ObjectType(
    name = "__EnumValue",
    description =
      "One possible value for a given Enum. Enum values are unique values, not " +
      "a placeholder for a string or numeric value. However an Enum value is " +
      "returned in a JSON response as a string.",
    fields = List[Field[Unit, EnumValue[_]]](
      Field("name", StringType, resolve = _.value.name),
      Field("description", OptionType(StringType), resolve = _.value.description),
      Field("isDeprecated", BooleanType, resolve = _.value.deprecationReason.isDefined),
      Field("deprecationReason", OptionType(StringType), resolve = _.value.deprecationReason)
    ))

  val __Directive = ObjectType(
    name = "__Directive",
    description =
      "A Directive provides a way to describe alternate runtime execution and " +
      "type validation behavior in a GraphQL document." +
      "\n\nIn some cases, you need to provide options to alter GraphQL’s " +
      "execution behavior in ways field arguments will not suffice, such as " +
      "conditionally including or skipping a field. Directives provide this by " +
      "describing additional information to the executor.",
    fields = fields[Unit, Directive](
      Field("name", StringType, resolve = _.value.name),
      Field("description", OptionType(StringType), resolve = _.value.description),
      Field("locations", ListType(__DirectiveLocation), resolve = _.value.locations.toVector.sorted),
      Field("args", ListType(__InputValue), resolve = _.value.arguments)))

  val __Schema = ObjectType(
    name = "__Schema",
    description =
      "A GraphQL Schema defines the capabilities of a GraphQL " +
      "server. It exposes all available types and directives on " +
      "the server, as well as the entry points for query, mutation, and subscription operations.",
    fields = List[Field[Unit, Schema[Any, Any]]](
      Field("description", OptionType(StringType), resolve = _.value.description),
      Field("types", ListType(__Type), Some("A list of all types supported by this server."),
        resolve = _.value.typeList map (true -> _)),
      Field("queryType", __Type, Some("The type that query operations will be rooted at."),
        resolve = true -> _.value.query),
      Field("mutationType", OptionType(__Type),
        Some("If this server supports mutation, the type that mutation operations will be rooted at."),
        resolve = _.value.mutation map (true -> _)),
      Field("subscriptionType", OptionType(__Type),
        Some("If this server support subscription, the type that subscription operations will be rooted at."),
        resolve = _.value.subscription map (true -> _)),
      Field("directives", ListType(__Directive),
        Some("A list of all directives supported by this server."), resolve = _.value.directives)))

  val SchemaMetaField: Field[Unit, Unit] = Field(
    name = "__schema",
    fieldType = __Schema,
    description = Some("Access the current type schema of this server."),
    resolve = _.schema.asInstanceOf[Schema[Any, Any]])

  val TypeMetaField: Field[Unit, Unit] = Field(
    name = "__type",
    fieldType = OptionType(__Type),
    description = Some("Request the type information of a single type."),
    arguments = Argument("name", StringType) :: Nil,
    resolve = ctx => ctx.schema.types get ctx.arg[String]("name") map (true -> _._2))

  val TypeNameMetaField: Field[Unit, Unit] = Field(
    name = "__typename",
    fieldType = StringType,
    description = Some("The name of the current Object type at runtime."),
    resolve = ctx => ctx.parentType.name)

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

  def isIntrospection(tpe: CompositeType[_], field: Field[_, _]): Boolean =
    Schema.isIntrospectionType(tpe.name) || MetaFieldNames.contains(field.name)

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

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

  def introspectionQuery: ast.Document = introspectionQuery()

  def introspectionQuery(schemaDescription: Boolean = true): ast.Document =
    QueryParser.parse(introspectionQueryString(schemaDescription))

  def introspectionQueryString(schemaDescription: Boolean = true): String =
    s"""query IntrospectionQuery {
       |  __schema {
       |    queryType { name }
       |    mutationType { name }
       |    subscriptionType { name }
       |    types {
       |      ...FullType
       |    }
       |    directives {
       |      name
       |      description
       |      locations
       |      args {
       |        ...InputValue
       |      }
       |    }
       |    ${if (schemaDescription) "description" else ""}
       |  }
       |}
       |fragment FullType on __Type {
       |  kind
       |  name
       |  description
       |  fields(includeDeprecated: true) {
       |    name
       |    description
       |    args {
       |      ...InputValue
       |    }
       |    type {
       |      ...TypeRef
       |    }
       |    isDeprecated
       |    deprecationReason
       |  }
       |  inputFields {
       |    ...InputValue
       |  }
       |  interfaces {
       |    ...TypeRef
       |  }
       |  enumValues(includeDeprecated: true) {
       |    name
       |    description
       |    isDeprecated
       |    deprecationReason
       |  }
       |  possibleTypes {
       |    ...TypeRef
       |  }
       |}
       |fragment InputValue on __InputValue {
       |  name
       |  description
       |  type { ...TypeRef }
       |  defaultValue
       |}
       |fragment TypeRef on __Type {
       |  kind
       |  name
       |  ofType {
       |    kind
       |    name
       |    ofType {
       |      kind
       |      name
       |      ofType {
       |        kind
       |        name
       |        ofType {
       |          kind
       |          name
       |          ofType {
       |            kind
       |            name
       |            ofType {
       |              kind
       |              name
       |              ofType {
       |                kind
       |                name
       |              }
       |            }
       |          }
       |        }
       |      }
       |    }
       |  }
       |}""".stripMargin
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy