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

caliban.tools.RemoteSchema.scala Maven / Gradle / Ivy

There is a newer version: 2.9.0
Show newest version
package caliban.tools

import caliban.parsing.adt._
import caliban.Value.StringValue
import caliban.introspection.adt._
import caliban.parsing.adt.Definition.TypeSystemDefinition.TypeDefinition._
import caliban.parsing.adt.Definition.TypeSystemDefinition._

object RemoteSchema {

  /**
   * Turns an introspection schema into a caliban.parsing.adt.__Schema
   * which can in turn be used for more advanced use cases such as schema
   * stitching.
   */
  def parseRemoteSchema(doc: Document): Option[__Schema] = {
    val queries = doc.schemaDefinition
      .flatMap(_.query)
      .flatMap(doc.objectTypeDefinition)

    val mutations = doc.schemaDefinition
      .flatMap(_.mutation)
      .flatMap(doc.objectTypeDefinition)

    queries
      .map(queries =>
        __Schema(
          description = doc.schemaDefinition.flatMap(_.description),
          queryType = toObjectType(queries, doc.typeDefinitions),
          mutationType = mutations.map(toObjectType(_, doc.typeDefinitions)),
          subscriptionType = None,
          types = doc.typeDefinitions.map(toTypeDefinition(_, doc.typeDefinitions)),
          directives = doc.directiveDefinitions.map(toDirective(_, doc.typeDefinitions))
        )
      )
  }

  private def toObjectType(
    definition: Definition.TypeSystemDefinition.TypeDefinition.ObjectTypeDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Type =
    __Type(
      kind = __TypeKind.OBJECT,
      name = Some(definition.name),
      description = definition.description,
      interfaces = toInterfaces(definition.implements, definitions),
      directives = toDirectives(definition.directives),
      fields = (args: __DeprecatedArgs) =>
        if (definition.fields.nonEmpty)
          Some(
            definition.fields
              .map(toField(_, definitions))
              .filter(filterDeprecated(_, args))
          )
        else None
    )

  private def toField(
    definition: Definition.TypeSystemDefinition.TypeDefinition.FieldDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Field =
    __Field(
      name = definition.name,
      description = definition.description,
      args = (args: __DeprecatedArgs) =>
        definition.args
          .map(toInputValue(_, definitions))
          .filter(filterDeprecated(_, args)),
      `type` = toType(definition.ofType, definitions),
      isDeprecated = isDeprecated(definition.directives),
      deprecationReason = deprecationReason(definition.directives),
      directives = toDirectives(definition.directives)
    )

  private def toType(
    definition: Type,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): () => __Type = { () =>
    definition match {
      case Type.ListType(t, nonNull) =>
        if (nonNull)
          __Type(
            kind = __TypeKind.NON_NULL,
            ofType = Some(
              __Type(
                kind = __TypeKind.LIST,
                ofType = Some(
                  toType(t, definitions)()
                )
              )
            )
          )
        else
          __Type(
            kind = __TypeKind.LIST,
            ofType = Some(
              toType(t, definitions)()
            )
          )

      case Type.NamedType(name, nonNull) =>
        if (nonNull)
          __Type(
            kind = __TypeKind.NON_NULL,
            ofType = Some(
              toType(name, definitions)
            )
          )
        else
          toType(name, definitions)
    }
  }

  private def toType(
    name: String,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ) =
    definitions.find(_.name == name) match {
      case Some(value) =>
        value match {
          case e: EnumTypeDefinition        => toEnumType(e)
          case s: ScalarTypeDefinition      => toScalar(s)
          case u: UnionTypeDefinition       => toUnionType(u, definitions)
          case o: ObjectTypeDefinition      => toObjectType(o, definitions)
          case i: InputObjectTypeDefinition =>
            toInputObjectType(i, definitions)
          case i: InterfaceTypeDefinition   =>
            toInterfaceType(i, definitions)
        }
      case None        => __Type(kind = __TypeKind.SCALAR, name = Some(name))
    }

  private def toDirectives(directives: List[Directive]): Option[List[Directive]] = {
    val filtered = directives.filter(_.name != "deprecated")

    if (filtered.nonEmpty) Some(filtered)
    else None
  }

  private def toInterfaces(
    interfaces: List[Type.NamedType],
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): () => Some[List[__Type]] = { () =>
    Some(
      interfaces
        .map(t => toType(t.name, definitions))
    )
  }

  private def toInterfaceType(
    definition: Definition.TypeSystemDefinition.TypeDefinition.InterfaceTypeDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Type = {
    val implementations = definitions.collect {
      case t @ ObjectTypeDefinition(_, _, implements, _, _) if implements.map(_.name).toSet.contains(definition.name) =>
        toObjectType(t, definitions)
    }

    __Type(
      kind = __TypeKind.INTERFACE,
      name = Some(definition.name),
      description = definition.description,
      possibleTypes = Some(implementations),
      fields = (args: __DeprecatedArgs) =>
        if (definition.fields.nonEmpty)
          Some(
            definition.fields
              .map(t => toField(t, definitions))
              .filter(filterDeprecated(_, args))
          )
        else None,
      directives = toDirectives(definition.directives)
    )
  }

  private def toInputValue(
    definition: Definition.TypeSystemDefinition.TypeDefinition.InputValueDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __InputValue =
    __InputValue(
      name = definition.name,
      description = definition.description,
      `type` = toType(definition.ofType, definitions),
      defaultValue = definition.defaultValue.map(_.toInputString),
      directives = toDirectives(definition.directives)
    )

  private def toEnumType(
    definition: Definition.TypeSystemDefinition.TypeDefinition.EnumTypeDefinition
  ): __Type =
    __Type(
      kind = __TypeKind.ENUM,
      name = Some(definition.name),
      enumValues = (args: __DeprecatedArgs) =>
        if (definition.enumValuesDefinition.nonEmpty)
          Some(definition.enumValuesDefinition.map(toEnumValue).filter(filterDeprecated(_, args)))
        else None,
      directives = toDirectives(definition.directives)
    )

  private def toEnumValue(
    definition: Definition.TypeSystemDefinition.TypeDefinition.EnumValueDefinition
  ): __EnumValue =
    __EnumValue(
      name = definition.enumValue,
      description = definition.description,
      isDeprecated = isDeprecated(definition.directives),
      deprecationReason = deprecationReason(definition.directives),
      directives = toDirectives(definition.directives)
    )

  private def toInputObjectType(
    definition: Definition.TypeSystemDefinition.TypeDefinition.InputObjectTypeDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Type =
    __Type(
      kind = __TypeKind.INPUT_OBJECT,
      name = Some(definition.name),
      description = definition.description,
      inputFields = (args: __DeprecatedArgs) =>
        if (definition.fields.nonEmpty)
          Some(
            definition.fields
              .map(toInputValue(_, definitions))
              .filter(filterDeprecated(_, args))
          )
        else None,
      directives = toDirectives(definition.directives)
    )

  private def toUnionType(
    definition: Definition.TypeSystemDefinition.TypeDefinition.UnionTypeDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Type =
    __Type(
      kind = __TypeKind.UNION,
      name = Some(definition.name),
      description = definition.description,
      possibleTypes =
        if (definition.memberTypes.nonEmpty)
          Some(
            definition.memberTypes
              .map(t => toType(t, definitions))
          )
        else None,
      directives = toDirectives(definition.directives)
    )

  private def toScalar(
    definition: Definition.TypeSystemDefinition.TypeDefinition.ScalarTypeDefinition
  ): __Type =
    __Type(
      kind = __TypeKind.SCALAR,
      name = Some(definition.name),
      description = definition.description,
      directives = toDirectives(definition.directives)
    )

  private def toTypeDefinition(
    definition: Definition.TypeSystemDefinition.TypeDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Type =
    definition match {
      case o: ObjectTypeDefinition      => toObjectType(o, definitions)
      case s: ScalarTypeDefinition      => toScalar(s)
      case e: EnumTypeDefinition        => toEnumType(e)
      case u: UnionTypeDefinition       => toUnionType(u, definitions)
      case i: InterfaceTypeDefinition   => toInterfaceType(i, definitions)
      case i: InputObjectTypeDefinition =>
        toInputObjectType(i, definitions)
    }

  private def toDirective(
    definition: Definition.TypeSystemDefinition.DirectiveDefinition,
    definitions: List[Definition.TypeSystemDefinition.TypeDefinition]
  ): __Directive =
    __Directive(
      name = definition.name,
      description = definition.description,
      args = (args: __DeprecatedArgs) =>
        definition.args
          .map(toInputValue(_, definitions))
          .filter(filterDeprecated(_, args)),
      isRepeatable = definition.isRepeatable,
      locations = definition.locations.map(toDirectiveLocation)
    )

  private def toDirectiveLocation(loc: DirectiveLocation): __DirectiveLocation =
    loc match {
      case DirectiveLocation.ExecutableDirectiveLocation.QUERY                  => __DirectiveLocation.QUERY
      case DirectiveLocation.ExecutableDirectiveLocation.MUTATION               => __DirectiveLocation.MUTATION
      case DirectiveLocation.ExecutableDirectiveLocation.SUBSCRIPTION           => __DirectiveLocation.SUBSCRIPTION
      case DirectiveLocation.ExecutableDirectiveLocation.FIELD                  => __DirectiveLocation.FIELD
      case DirectiveLocation.ExecutableDirectiveLocation.FRAGMENT_DEFINITION    => __DirectiveLocation.FRAGMENT_DEFINITION
      case DirectiveLocation.ExecutableDirectiveLocation.FRAGMENT_SPREAD        => __DirectiveLocation.FRAGMENT_SPREAD
      case DirectiveLocation.ExecutableDirectiveLocation.INLINE_FRAGMENT        => __DirectiveLocation.INLINE_FRAGMENT
      case DirectiveLocation.TypeSystemDirectiveLocation.SCHEMA                 => __DirectiveLocation.SCHEMA
      case DirectiveLocation.TypeSystemDirectiveLocation.SCALAR                 => __DirectiveLocation.SCALAR
      case DirectiveLocation.TypeSystemDirectiveLocation.OBJECT                 => __DirectiveLocation.OBJECT
      case DirectiveLocation.TypeSystemDirectiveLocation.FIELD_DEFINITION       => __DirectiveLocation.FIELD_DEFINITION
      case DirectiveLocation.TypeSystemDirectiveLocation.ARGUMENT_DEFINITION    => __DirectiveLocation.ARGUMENT_DEFINITION
      case DirectiveLocation.TypeSystemDirectiveLocation.INTERFACE              => __DirectiveLocation.INTERFACE
      case DirectiveLocation.TypeSystemDirectiveLocation.UNION                  => __DirectiveLocation.UNION
      case DirectiveLocation.TypeSystemDirectiveLocation.ENUM                   => __DirectiveLocation.ENUM
      case DirectiveLocation.TypeSystemDirectiveLocation.ENUM_VALUE             => __DirectiveLocation.ENUM_VALUE
      case DirectiveLocation.TypeSystemDirectiveLocation.INPUT_OBJECT           => __DirectiveLocation.INPUT_OBJECT
      case DirectiveLocation.TypeSystemDirectiveLocation.INPUT_FIELD_DEFINITION =>
        __DirectiveLocation.INPUT_FIELD_DEFINITION
      case DirectiveLocation.TypeSystemDirectiveLocation.VARIABLE_DEFINITION    => __DirectiveLocation.VARIABLE_DEFINITION
    }

  private def filterDeprecated(x: __Field, deprecated: __DeprecatedArgs): Boolean =
    if (deprecated.includeDeprecated.getOrElse(true)) true
    else !x.isDeprecated

  private def filterDeprecated(x: __EnumValue, deprecated: __DeprecatedArgs): Boolean =
    if (deprecated.includeDeprecated.getOrElse(true)) true
    else !x.isDeprecated

  private def filterDeprecated(x: __InputValue, deprecated: __DeprecatedArgs): Boolean =
    if (deprecated.includeDeprecated.getOrElse(true)) true
    else !x.isDeprecated

  private def isDeprecated(directives: List[Directive]): Boolean =
    deprecationReason(directives).isDefined

  private def deprecationReason(directives: List[Directive]): Option[String] =
    directives.collectFirst {
      case d if d.name == "deprecated" =>
        d.arguments
          .get("reason")
          .collect { case StringValue(value) =>
            value
          }
          .getOrElse("deprecated")
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy