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

sangria.execution.InputDocumentMaterializer.scala Maven / Gradle / Ivy

package sangria.execution

import sangria.ast.{AstVisitor, InputDocument, VariableDefinition}
import sangria.ast
import sangria.execution
import sangria.marshalling.{FromInput, InputUnmarshaller}
import sangria.renderer.SchemaRenderer
import sangria.schema._
import sangria.visitor.VisitorCommand
import sangria.marshalling.queryAst._
import sangria.validation.QueryValidator

import scala.collection.mutable
import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}

case class InputDocumentMaterializer[Vars](
    schema: Schema[_, _],
    variables: Vars = InputUnmarshaller.emptyMapVars)(implicit iu: InputUnmarshaller[Vars]) {
  def to[T](document: InputDocument, inputType: InputType[T])(implicit
      fromInput: FromInput[T]): Try[Vector[T]] = {
    val collector = new ValueCollector[Unit, Vars](
      schema,
      variables,
      document.sourceMapper,
      None,
      (),
      ExceptionHandler.empty,
      None,
      false)(iu)

    val variableDefinitions = inferVariableDefinitions(document, inputType)

    collector.getVariableValues(variableDefinitions, None) match {
      case Failure(e) =>
        // return validation errors without variables first if variables is what failed
        val violations =
          QueryValidator.default.validateInputDocument(
            schema,
            document,
            inputType,
            Map.empty[String, execution.VariableValue]
          )

        if (violations.nonEmpty)
          Failure(InputDocumentMaterializationError(violations, ExceptionHandler.empty))
        else
          Failure(e)
      case Success(vars) =>
        val violations =
          QueryValidator.default.validateInputDocument(schema, document, inputType, vars)

        if (violations.nonEmpty)
          Failure(InputDocumentMaterializationError(violations, ExceptionHandler.empty))
        else {
          try
            Success(document.values.flatMap { value =>
              collector.coercionHelper.coerceInputValue(
                inputType,
                Nil,
                value,
                None,
                Some(vars),
                fromInput.marshaller,
                fromInput.marshaller,
                isArgument = false) match {
                case Left(vs) => throw InputDocumentMaterializationError(vs, ExceptionHandler.empty)
                case Right(coerced) => coerced.toOption.map(res => fromInput.fromResult(res))
              }
            })
          catch {
            case NonFatal(e) => Failure(e)
          }
        }
    }
  }

  def inferVariableDefinitions[T](
      document: InputDocument,
      inputType: InputType[T]): Vector[VariableDefinition] =
    document.values.flatMap { v =>
      AstVisitor
        .visitAstWithState(schema, v, new mutable.HashMap[String, VariableDefinition]) {
          (typeInfo, state) =>
            typeInfo.withInputType(inputType)

            AstVisitor {
              case v: ast.VariableValue if typeInfo.inputType.isDefined =>
                val parentType = typeInfo.inputType.get
                val parentTypeAst = SchemaRenderer.renderTypeNameAst(parentType)

                state.get(v.name) match {
                  case None =>
                    state(v.name) = ast.VariableDefinition(v.name, parentTypeAst, None)
                    VisitorCommand.Continue
                  case _ => VisitorCommand.Continue
                }
            }
        }
        .values
        .toVector
    }
}

object InputDocumentMaterializer {
  def to[T](
      schema: Schema[_, _],
      document: InputDocument,
      inputType: InputType[T]
  )(implicit fromInput: FromInput[T]): Try[Vector[T]] =
    InputDocumentMaterializer(schema, InputUnmarshaller.emptyMapVars).to(document, inputType)

  def to[T, Vars](
      schema: Schema[_, _],
      document: InputDocument,
      inputType: InputType[T],
      variables: Vars
  )(implicit iu: InputUnmarshaller[Vars], fromInput: FromInput[T]): Try[Vector[T]] =
    InputDocumentMaterializer(schema, variables).to(document, inputType)

  def to[T](
      document: InputDocument,
      inputType: InputType[T]
  )(implicit fromInput: FromInput[T]): Try[Vector[T]] =
    to(emptyStubSchema(inputType), document, inputType, InputUnmarshaller.emptyMapVars)

  def to[T, Vars](document: InputDocument, inputType: InputType[T], variables: Vars)(implicit
      iu: InputUnmarshaller[Vars],
      fromInput: FromInput[T]): Try[Vector[T]] =
    to(emptyStubSchema(inputType), document, inputType, variables)

  private def emptyStubSchema[T: FromInput](inputType: InputType[T]): Schema[_, _] =
    Schema(
      ObjectType(
        "Query",
        fields[Unit, Unit](
          Field(
            "stub",
            StringType,
            arguments = Argument("stub", inputType) :: Nil,
            resolve = _ => "stub"))))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy