sangria.ast.Document Scala Examples

The following examples show how to use sangria.ast.Document. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: DocumentInstancesSpec.scala    From sbt-graphql   with Apache License 2.0 5 votes vote down vote up
package rocks.muki.graphql.instances

import cats._
import cats.kernel.laws.discipline.MonoidTests
import cats.tests.CatsSuite
import org.scalacheck.{Arbitrary, Gen}
import rocks.muki.graphql.codegen.style.sangria.TestSchema
import sangria.ast.Document

class DocumentInstancesSpec extends CatsSuite {
  implicit private val arbDocument: Arbitrary[Document] =
    Arbitrary(
      Gen.oneOf(
        Document(Vector.empty),
        Document.emptyStub,
        Document(TestSchema.StarWarsSchema.toAst.definitions.take(3))
      )
    )
  implicit private val eqDocument: Eq[Document] =
    Eq.fromUniversalEquals[Document]

  checkAll("Monoid[sangria.ast.Document]", MonoidTests[Document].monoid)
} 
Example 2
Source File: SchemaLoader.scala    From graphql-gateway   with Apache License 2.0 5 votes vote down vote up
package sangria.gateway.schema

import better.files.File
import sangria.ast.Document
import sangria.execution.Executor
import sangria.execution.deferred.DeferredResolver
import sangria.gateway.AppConfig
import sangria.gateway.file.FileUtil
import sangria.gateway.http.client.HttpClient
import sangria.gateway.schema.materializer.{GatewayContext, GatewayMaterializer}
import sangria.gateway.util.Logging
import sangria.parser.QueryParser
import sangria.schema.Schema
import sangria.marshalling.circe._

import scala.concurrent.{ExecutionContext, Future}
import scala.util.control.NonFatal
import scala.util.{Failure, Success}

class SchemaLoader(config: AppConfig, client: HttpClient, mat: GatewayMaterializer)(implicit ec: ExecutionContext) extends Logging {
  def loadSchema: Future[Option[SchemaInfo[GatewayContext, Any]]] = {
    val files = FileUtil.loadFiles(config.watch.allFiles, config.watch.allGlobs)

    if (files.nonEmpty) {
      val parsed =
        files.map {
          case (path, content) ⇒ path → QueryParser.parse(content)
        }

      val failed = parsed.collect {case (path, Failure(e)) ⇒ path → e}

      if (failed.nonEmpty) {
        failed.foreach { case (path, error) ⇒
          logger.error(s"Can't parse file '$path':\n${error.getMessage}")
        }

        Future.successful(None)
      } else {
        val successful = parsed.collect {case (path, Success(doc)) ⇒ path → doc}
        val document = Document.merge(successful.map(_._2))

        try {
          val info =
            for {
              ctx ← GatewayContext.loadContext(config, client, document)
              schema = Schema.buildFromAst(document, mat.schemaBuilder(ctx).validateSchemaWithException(document))
              intro ← executeIntrospection(schema, ctx)
            } yield Some(SchemaInfo(
              schema,
              ctx,
              (),
              Nil,
              DeferredResolver.empty,
              schema.renderPretty,
              intro,
              files.map(_._1)))

          info.recover(handleError(files))
        } catch {
          case e if handleError(files).isDefinedAt(e) ⇒
            Future.successful(handleError(files)(e))
        }
      }
    } else {
      logger.error("No schema files found!")
      Future.successful(None)
    }
  }

  private def handleError(files: Vector[(File, String)]): PartialFunction[Throwable, Option[SchemaInfo[GatewayContext, Any]]] = {
    case NonFatal(e) ⇒
      logger.error(s"Can't create the schema from files: ${files.map(_._1).mkString(", ")}. " + e.getMessage)
      None
  }

  private def executeIntrospection(schema: Schema[GatewayContext, Any], ctx: GatewayContext) =
    Executor.execute(schema, sangria.introspection.introspectionQuery(schemaDescription = false, directiveRepeatableFlag = false), ctx)
} 
Example 3
Source File: GraphQLRequestUnmarshaller.scala    From graphql-gateway   with Apache License 2.0 5 votes vote down vote up
package sangria.gateway.http

import java.nio.charset.Charset

import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.Accept
import akka.http.scaladsl.server.Directive0
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshaller}
import akka.util.ByteString
import sangria.ast.Document
import sangria.parser.QueryParser
import sangria.renderer.{QueryRenderer, QueryRendererConfig}

import scala.collection.immutable.Seq

object GraphQLRequestUnmarshaller {
  val `application/graphql` = MediaType.applicationWithFixedCharset("graphql", HttpCharsets.`UTF-8`, "graphql")

  def explicitlyAccepts(mediaType: MediaType): Directive0 =
    headerValuePF {
      case Accept(ranges) if ranges.exists(range ⇒ !range.isWildcard && range.matches(mediaType)) ⇒ ranges
    }.flatMap(_ ⇒ pass)

  def includeIf(include: Boolean): Directive0 =
    if (include) pass
    else reject

  def unmarshallerContentTypes: Seq[ContentTypeRange] =
    mediaTypes.map(ContentTypeRange.apply)

  def mediaTypes: Seq[MediaType.WithFixedCharset] =
    List(`application/graphql`)

  implicit final def documentMarshaller(implicit config: QueryRendererConfig = QueryRenderer.Compact): ToEntityMarshaller[Document] =
    Marshaller.oneOf(mediaTypes: _*) { mediaType ⇒
      Marshaller.withFixedContentType(ContentType(mediaType)) { json ⇒
        HttpEntity(mediaType, QueryRenderer.render(json, config))
      }
    }

  
  implicit final val documentUnmarshaller: FromEntityUnmarshaller[Document] =
    Unmarshaller.byteStringUnmarshaller
      .forContentTypes(unmarshallerContentTypes: _*)
      .map {
        case ByteString.empty ⇒ throw Unmarshaller.NoContentException
        case data ⇒
          import sangria.parser.DeliveryScheme.Throw

          QueryParser.parse(data.decodeString(Charset.forName("UTF-8")))
      }
} 
Example 4
Source File: QueryGQL.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.http

import cats.Show
import cats.syntax.show._

import com.github.agourlay.cornichon.json.CornichonJson.parseDslJsonUnsafe

import io.circe.{ Encoder, Json }

import sangria.ast.Document
import sangria.renderer.QueryRenderer

case class QueryGQL(
    url: String,
    query: Document,
    operationName: Option[String],
    variables: Option[Map[String, Json]],
    params: Seq[(String, String)],
    headers: Seq[(String, String)]) {

  def withParams(params: (String, String)*) = copy(params = params)
  def addParams(params: (String, String)*) = copy(params = this.params ++ params)

  def withHeaders(headers: (String, String)*) = copy(headers = headers)
  def addHeaders(headers: (String, String)*) = copy(headers = this.headers ++ headers)

  def withQuery(query: Document) = copy(query = query)

  def withOperationName(operationName: String) = copy(operationName = Some(operationName))

  def withVariables(newVariables: (String, VarValue)*) = {
    val vars: Map[String, Json] = newVariables.map { case (k, v) => k -> parseDslJsonUnsafe(v.value)(v.encoder, v.show) }.toMap
    copy(variables = variables.fold(Some(vars))(v => Some(v ++ vars)))
  }

  lazy val querySource = query.source.getOrElse(QueryRenderer.render(query, QueryRenderer.Pretty))

  lazy val payload: String = {
    import io.circe.generic.auto._
    import io.circe.syntax._

    GqlPayload(querySource, operationName, variables).asJson.show
  }

  private case class GqlPayload(query: String, operationName: Option[String], variables: Option[Map[String, Json]])

}

trait VarValue {
  type Value
  def value: Value
  def encoder: Encoder[Value]
  def show: Show[Value]
}

object VarValue {
  implicit def fromEncoderShow[A: Encoder: Show](a: A): VarValue = new VarValue {
    type Value = A
    def value = a
    def encoder = Encoder[A]
    def show = Show[A]
  }
}

object QueryGQL {
  val emptyDocument = Document(Vector.empty)
} 
Example 5
Source File: UpdateTypeAndFieldPositions.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.system.mutactions.internal

import cool.graph.Types.Id
import cool.graph._
import cool.graph.shared.models.{Client, Project}
import cool.graph.system.database.finder.{CachedProjectResolver, ProjectQueries, ProjectResolver}
import sangria.ast.{Document, ObjectTypeDefinition, TypeDefinition}
import scaldi.{Injectable, Injector}
import slick.dbio.DBIOAction
import slick.jdbc.MySQLProfile.backend.DatabaseDef

import scala.collection.immutable.Seq
import scala.collection.mutable
import scala.concurrent.Future

case class UpdateTypeAndFieldPositions(
    project: Project,
    client: Client,
    newSchema: Document,
    internalDatabase: DatabaseDef,
    projectQueries: ProjectQueries
)(implicit inj: Injector)
    extends SystemSqlMutaction
    with Injectable {
  import scala.concurrent.ExecutionContext.Implicits.global

  implicit val projectResolver = inject[ProjectResolver](identified by "uncachedProjectResolver")

  val mutactions: mutable.Buffer[SystemSqlMutaction] = mutable.Buffer.empty

  override def execute: Future[SystemSqlStatementResult[Any]] =
    refreshProject.flatMap { project =>
      val newTypePositions: Seq[Id] = newSchema.definitions.collect {
        case typeDef: TypeDefinition =>
          project
            .getModelByName(typeDef.name)
            .orElse(project.getEnumByName(typeDef.name))
            .map(_.id)
      }.flatten

      mutactions += UpdateProject(
        client = client,
        oldProject = project,
        project = project.copy(typePositions = newTypePositions.toList),
        internalDatabase = internalDatabase,
        projectQueries = projectQueries,
        bumpRevision = false
      )

      mutactions ++= newSchema.definitions.collect {
        case typeDef: ObjectTypeDefinition =>
          project.getModelByName(typeDef.name).map { model =>
            val newFieldPositions = typeDef.fields.flatMap { fieldDef =>
              model.getFieldByName(fieldDef.name).map(_.id)
            }.toList
            UpdateModel(project = project, oldModel = model, model = model.copy(fieldPositions = newFieldPositions))
          }
      }.flatten

      val y = mutactions.map(_.execute)
      Future.sequence(y).map { statementResults =>
        val asSingleAction = DBIOAction.sequence(statementResults.toList.map(_.sqlAction))
        SystemSqlStatementResult(sqlAction = asSingleAction)
      }
    }

  def refreshProject: Future[Project] = {
    projectResolver.resolve(project.id).map(_.get)
  }

  override def rollback: Option[Future[SystemSqlStatementResult[Any]]] = mutactions.map(_.rollback).headOption.flatten

} 
Example 6
Source File: SchemaDiff.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.system.migration.dataSchema

import sangria.ast.Document

import scala.util.Try

object SchemaDiff {
  def apply(oldSchema: String, newSchema: String): Try[SchemaDiff] = {
    for {
      oldDocParsed <- SdlSchemaParser.parse(oldSchema)
      newDocParsed <- SdlSchemaParser.parse(newSchema)
    } yield SchemaDiff(oldDocParsed, newDocParsed)
  }
}
case class SchemaDiff(
    oldSchema: Document,
    newSchema: Document
) {
  import DataSchemaAstExtensions._

  val addedTypes: Vector[String]   = newSchema.oldTypeNames diff oldSchema.typeNames
  val removedTypes: Vector[String] = oldSchema.typeNames diff newSchema.oldTypeNames

  val updatedTypes: Vector[UpdatedType] = {
    val x = for {
      typeInNewSchema <- newSchema.objectTypes
      typeInOldSchema <- oldSchema.objectTypes.find(_.name == typeInNewSchema.oldName)
    } yield {
      val addedFields = typeInNewSchema.fields.filter(fieldInNewType => typeInOldSchema.fields.forall(_.name != fieldInNewType.oldName))

      val removedFields = typeInOldSchema.fields.filter(fieldInOldType => typeInNewSchema.fields.forall(_.oldName != fieldInOldType.name))

      val updatedFields = (typeInNewSchema.fields diff addedFields).map { updatedField =>
        UpdatedField(updatedField.name, updatedField.oldName, updatedField.fieldType.namedType.name)
      }

      UpdatedType(
        name = typeInNewSchema.name,
        oldName = typeInNewSchema.oldName,
        addedFields = addedFields.map(_.name).toList,
        removedFields = removedFields.map(_.name).toList,
        updatedFields = updatedFields.toList
      )
    }
    x.filter(_.hasChanges)
  }

  val addedEnums: Vector[String]   = newSchema.oldEnumNames diff oldSchema.enumNames
  val removedEnums: Vector[String] = oldSchema.enumNames diff newSchema.oldEnumNames
  val updatedEnums: Vector[UpdatedEnum] = {
    for {
      typeInNewSchema <- newSchema.enumTypes
      typeInOldSchema <- oldSchema.enumTypes.find(_.name == typeInNewSchema.oldName)
    } yield UpdatedEnum(name = typeInNewSchema.name, oldName = typeInOldSchema.name)
  }
}
case class UpdatedType(
    name: String,
    oldName: String,
    addedFields: List[String],
    removedFields: List[String],
    updatedFields: List[UpdatedField]
) {
  def hasChanges: Boolean = addedFields.nonEmpty || removedFields.nonEmpty || updatedFields.nonEmpty
}
case class UpdatedField(
    name: String,
    oldName: String,
    newType: String
)

case class UpdatedEnum(
    name: String,
    oldName: String
) 
Example 7
Source File: RelationDiff.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.system.migration.dataSchema

import cool.graph.Types.Id
import cool.graph.shared.models.{Project, Relation}
import sangria.ast.{Document, StringValue}

object RelationDiff {
  // a schema is said to contain a relation if a @relation directive exists with correct name, or
  // a @relation with different name links the same fields
  def schemaContainsRelation(project: Project, schema: Document, relation: Relation): Boolean = {

    import DataSchemaAstExtensions._

    if (schema.containsRelation(relation.name)) {
      true
    } else {
      try {
        val leftModel = schema.objectType_!(relation.getModelA_!(project).name)
        val leftFieldRelationDirectiveName =
          leftModel
            .field_!(relation.getModelAField_!(project).name)
            .directive_!("relation")
            .argument_!("name")
            .value

        val rightModel = schema.objectType_!(relation.getModelB_!(project).name)
        val rightFieldRelationDirectiveName =
          rightModel
            .field_!(relation.getModelBField_!(project).name)
            .directive_!("relation")
            .argument_!("name")
            .value

        leftFieldRelationDirectiveName
          .asInstanceOf[StringValue]
          .value == rightFieldRelationDirectiveName.asInstanceOf[StringValue].value
      } catch {
        case e: Throwable => false
      }
    }
  }
  // project is said to contain relation if a relation with the name already exists
  // or the two fields are already linked by a relation with other name
  def projectContainsRelation(project: Project, addRelation: AddRelationAction): Boolean = {
    project.relations.exists { relation =>
      if (relation.name == addRelation.input.name) {
        true
      } else {
        try {
          val leftModelRelationId: Option[Id] = project
            .getModelById_!(addRelation.input.leftModelId)
            .getFieldByName_!(addRelation.input.fieldOnLeftModelName)
            .relation
            .map(_.id)
          val rightModelRelationId: Option[Id] = project
            .getModelById_!(addRelation.input.rightModelId)
            .getFieldByName_!(addRelation.input.fieldOnRightModelName)
            .relation
            .map(_.id)

          leftModelRelationId == rightModelRelationId
        } catch {
          case e: Throwable => false
        }
      }
    }
  }
} 
Example 8
Source File: UserContext.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.client

import cool.graph.client.database.ProjectDataresolver
import cool.graph.shared.models.{AuthenticatedRequest, AuthenticatedUser, Project, ProjectWithClientId}
import cool.graph.RequestContextTrait
import sangria.ast.Document
import scaldi.{Injectable, Injector}

case class UserContext(project: Project,
                       authenticatedRequest: Option[AuthenticatedRequest],
                       requestId: String,
                       requestIp: String,
                       clientId: String,
                       log: Function[String, Unit],
                       override val queryAst: Option[Document] = None,
                       alwaysQueryMasterDatabase: Boolean = false)(implicit inj: Injector)
    extends RequestContextTrait
    with UserContextTrait
    with Injectable {
  override val projectId: Option[String] = Some(project.id)

  val userId = authenticatedRequest.map(_.id)

  val queryDataResolver =
    new ProjectDataresolver(project = project, requestContext = this)

  val mutationDataresolver = {
    val resolver = new ProjectDataresolver(project = project, requestContext = this)
    resolver.enableMasterDatabaseOnlyMode
    resolver
  }

  def dataResolver =
    if (alwaysQueryMasterDatabase) {
      mutationDataresolver
    } else {
      queryDataResolver
    }
}

object UserContext {

  def load(
      project: Project,
      requestId: String,
      requestIp: String,
      clientId: String,
      log: Function[String, Unit],
      queryAst: Option[Document] = None
  )(implicit inj: Injector): UserContext = {

    UserContext(project, None, requestId, requestIp, clientId, log, queryAst = queryAst)
  }

  def fetchUserProjectWithClientId(
      project: ProjectWithClientId,
      authenticatedRequest: Option[AuthenticatedRequest],
      requestId: String,
      requestIp: String,
      log: Function[String, Unit],
      queryAst: Option[Document]
  )(implicit inj: Injector): UserContext = {
    fetchUser(project.project, authenticatedRequest, requestId, requestIp, project.clientId, log, queryAst)
  }

  def fetchUser(
      project: Project,
      authenticatedRequest: Option[AuthenticatedRequest],
      requestId: String,
      requestIp: String,
      clientId: String,
      log: Function[String, Unit],
      queryAst: Option[Document] = None
  )(implicit inj: Injector): UserContext = {
    val userContext = UserContext(project, authenticatedRequest, requestId, requestIp, clientId, log, queryAst = queryAst)

    if (authenticatedRequest.isDefined && authenticatedRequest.get.isInstanceOf[AuthenticatedUser]) {
      userContext.addFeatureMetric(FeatureMetric.Authentication)
    }

    userContext
  }
}

trait UserContextTrait {
  val project: Project
  val authenticatedRequest: Option[AuthenticatedRequest]
  val requestId: String
  val clientId: String
  val log: Function[String, Unit]
  val queryAst: Option[Document] = None
} 
Example 9
Source File: SubscriptionQueryValidator.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.subscriptions.schemas

import cool.graph.shared.models.{Model, ModelMutationType, Project}
import org.scalactic.{Bad, Good, Or}
import sangria.ast.Document
import sangria.parser.QueryParser
import sangria.validation.QueryValidator
import scaldi.Injector

import scala.util.{Failure, Success}

case class SubscriptionQueryError(errorMessage: String)

case class SubscriptionQueryValidator(project: Project)(implicit inj: Injector) {

  def validate(query: String): Model Or Seq[SubscriptionQueryError] = {
    queryDocument(query).flatMap(validate)
  }

  def validate(queryDoc: Document): Model Or Seq[SubscriptionQueryError] = {
    for {
      modelName <- modelName(queryDoc)
      model     <- modelFor(modelName)
      _         <- validateSubscriptionQuery(queryDoc, model)
    } yield model
  }

  def queryDocument(query: String): Document Or Seq[SubscriptionQueryError] = QueryParser.parse(query) match {
    case Success(doc) => Good(doc)
    case Failure(_)   => Bad(Seq(SubscriptionQueryError("The subscription query is invalid GraphQL.")))
  }

  def modelName(queryDoc: Document): String Or Seq[SubscriptionQueryError] =
    QueryTransformer.getModelNameFromSubscription(queryDoc) match {
      case Some(modelName) => Good(modelName)
      case None =>
        Bad(Seq(SubscriptionQueryError("The provided query doesn't include any known model name. Please check for the latest subscriptions API.")))
    }

  def modelFor(model: String): Model Or Seq[SubscriptionQueryError] = project.getModelByName(model) match {
    case Some(model) => Good(model)
    case None        => Bad(Seq(SubscriptionQueryError("The provided query doesn't include any known model name. Please check for the latest subscriptions API.")))
  }

  def validateSubscriptionQuery(queryDoc: Document, model: Model): Unit Or Seq[SubscriptionQueryError] = {
    val schema     = SubscriptionSchema(model, project, None, ModelMutationType.Created, None, true).build
    val violations = QueryValidator.default.validateQuery(schema, queryDoc)
    if (violations.nonEmpty) {
      Bad(violations.map(v => SubscriptionQueryError(v.errorMessage)))
    } else Good(())
  }
} 
Example 10
Source File: SubscriptionUserContext.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.subscriptions

import cool.graph.RequestContextTrait
import cool.graph.client.UserContextTrait
import cool.graph.deprecated.actions.schemas.MutationMetaData
import cool.graph.client.database.ProjectDataresolver
import cool.graph.shared.models.{AuthenticatedRequest, Project}
import sangria.ast.Document
import scaldi.{Injectable, Injector}

case class SubscriptionUserContext(nodeId: String,
                                   mutation: MutationMetaData,
                                   project: Project,
                                   authenticatedRequest: Option[AuthenticatedRequest],
                                   requestId: String,
                                   clientId: String,
                                   log: Function[String, Unit],
                                   override val queryAst: Option[Document] = None)(implicit inj: Injector)
    extends UserContextTrait
    with RequestContextTrait
    with Injectable {

  override val isSubscription: Boolean   = true
  override val projectId: Option[String] = Some(project.id)

  val dataResolver =
    new ProjectDataresolver(project = project, requestContext = this)
  override val requestIp: String = "subscription-callback-ip" // todo: get the correct ip from server
} 
Example 11
Source File: GraphQLHandler.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.navigator

import akka.actor.ActorRef
import akka.http.scaladsl.model.StatusCode
import akka.http.scaladsl.model.StatusCodes._
import com.daml.navigator.graphql._
import com.daml.navigator.graphql.SprayMarshallers._
import com.daml.navigator.model.PartyState
import com.daml.navigator.store.Store.StoreException
import com.typesafe.scalalogging.LazyLogging
import sangria.ast.Document
import sangria.execution._
import sangria.parser.QueryParser
import sangria.renderer.SchemaRenderer
import sangria.schema.Schema
import spray.json._

import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try

case class ParseResult(ast: Document, operationName: Option[String], variables: JsValue)


trait GraphQLHandler {
  def schema: Schema[GraphQLContext, Unit]
  def parse(request: String): Try[ParseResult]
  def parse(request: JsValue): Try[ParseResult]
  def executeQuery(parsed: ParseResult, party: PartyState): Future[(StatusCode, JsValue)]
  def renderSchema: String
}

object GraphQLHandler {
  type ParseQuery = JsValue => Try[ParseResult]
  type ExecuteQuery = (ParseResult, PartyState) => Future[(StatusCode, JsValue)]
  type CustomEndpoints = Set[CustomEndpoint[_]]
}

case class DefaultGraphQLHandler(
    customEndpoints: GraphQLHandler.CustomEndpoints,
    platformStore: Option[ActorRef])(
    implicit executionContext: ExecutionContext
) extends GraphQLHandler
    with LazyLogging {

  def schema: Schema[GraphQLContext, Unit] = new GraphQLSchema(customEndpoints).QuerySchema

  def parse(request: String): Try[ParseResult] =
    Try(request.parseJson).flatMap(parse)

  def parse(request: JsValue): Try[ParseResult] =
    for {
      fields <- Try(request.asJsObject.fields)
      JsString(query) <- Try(fields("query"))
      operationName = fields.get("operationName").collect {
        case JsString(value) => value
      }
      vars: JsValue = fields.get("variables") match {
        case Some(obj: JsObject) => obj
        case _ => JsObject.empty
      }
      ast <- QueryParser.parse(query)
    } yield ParseResult(ast, operationName, vars)

  def executeQuery(parsed: ParseResult, party: PartyState): Future[(StatusCode, JsValue)] = {
    platformStore.fold[Future[(StatusCode, JsValue)]](
      Future.successful(InternalServerError -> JsString("Platform store not available"))
    )(store => {
      val context = GraphQLContext(party, store)
      Executor
        .execute(
          schema,
          parsed.ast,
          context,
          variables = parsed.variables,
          operationName = parsed.operationName,
          exceptionHandler = ExceptionHandler {
            case (_, StoreException(message)) => HandledException(message)
          }
        )
        .map(OK -> _)
        .recover {
          case error: QueryAnalysisError =>
            logger.warn(s"GraphQL analysis error ${error.getMessage}.")
            BadRequest -> error.resolveError
          case error: ErrorWithResolver =>
            logger.error("Failed to execute GraphQL query", error)
            InternalServerError -> error.resolveError
        }
    })
  }

  def renderSchema: String = SchemaRenderer.renderSchema(schema)
} 
Example 12
Source File: DocumentLoader.scala    From sbt-graphql   with Apache License 2.0 5 votes vote down vote up
package rocks.muki.graphql.codegen

import java.io.File

import cats.implicits._
import rocks.muki.graphql.instances.monoidDocument
import sangria.ast.Document
import sangria.parser.QueryParser
import sangria.schema._
import sangria.validation.QueryValidator

import scala.io.Source

object DocumentLoader {

  
  def single(schema: Schema[_, _], file: File): Result[Document] =
    for {
      document <- parseDocument(file)
      violations = QueryValidator.default.validateQuery(schema, document)
      _ <- Either.cond(
        violations.isEmpty,
        document,
        Failure(s"Invalid query in ${file.getAbsolutePath}:\n${violations.map(_.errorMessage).mkString(", ")}")
      )
    } yield document

  private def parseSchema(file: File): Result[Schema[_, _]] =
    for {
      document <- parseDocument(file)
      schema <- Either.catchNonFatal(Schema.buildFromAst(document)).leftMap { error =>
        Failure(s"Failed to read schema $file: ${error.getMessage}")
      }
    } yield schema

  private def parseDocument(file: File): Result[Document] =
    for {
      input <- Either.catchNonFatal(Source.fromFile(file).mkString).leftMap { error =>
        Failure(s"Failed to read $file: ${error.getMessage}")
      }
      document <- Either.fromTry(QueryParser.parse(input)).leftMap { error =>
        Failure(s"Failed to parse $file: ${error.getMessage}")
      }
    } yield document
} 
Example 13
Source File: instances.scala    From sbt-graphql   with Apache License 2.0 5 votes vote down vote up
package rocks.muki.graphql

import cats.Monoid
import io.circe.Json
import sangria.ast.Document
import sangria.marshalling.InputUnmarshaller

package object instances {

  
  implicit object CirceInputUnmarshaller extends InputUnmarshaller[Json] {
    def getRootMapValue(node: Json, key: String) = node.asObject.get(key)

    def isMapNode(node: Json) = node.isObject
    def getMapValue(node: Json, key: String) = node.asObject.get(key)
    def getMapKeys(node: Json) = node.asObject.get.keys

    def isListNode(node: Json) = node.isArray
    def getListValue(node: Json) = node.asArray.get

    def isDefined(node: Json) = !node.isNull
    def getScalarValue(node: Json) = {
      def invalidScalar =
        throw new IllegalStateException(s"$node is not a scalar value")

      node.fold(
        jsonNull = invalidScalar,
        jsonBoolean = identity,
        jsonNumber = num => num.toBigInt orElse num.toBigDecimal getOrElse invalidScalar,
        jsonString = identity,
        jsonArray = _ => invalidScalar,
        jsonObject = _ => invalidScalar
      )
    }

    def getScalaScalarValue(node: Json) = getScalarValue(node)

    def isEnumNode(node: Json) = node.isString

    def isScalarNode(node: Json) =
      node.isBoolean || node.isNumber || node.isString

    def isVariableNode(node: Json) = false
    def getVariableName(node: Json) =
      throw new IllegalArgumentException("variables are not supported")

    def render(node: Json) = node.noSpaces
  }
} 
Example 14
Source File: GraphQlController.scala    From tap   with Apache License 2.0 5 votes vote down vote up
package controllers

import javax.inject.Inject
import models.GraphqlSchema
import models.graphql.GraphqlActions
import play.api.Logger
import play.api.libs.json.{JsObject, JsValue, Json}
import play.api.mvc.{Action, AnyContent, InjectedController, Result}
import sangria.ast.Document
import sangria.execution.{ErrorWithResolver, Executor, QueryAnalysisError}
import sangria.marshalling.playJson.{PlayJsonInputUnmarshallerJObject, PlayJsonResultMarshaller}
import sangria.parser.{QueryParser, SyntaxError}
import sangria.schema.Schema
import views.GraphiqlPage

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}


class GraphQlController @Inject() (assets: AssetsFinder, gqlSchema: GraphqlSchema, actions: GraphqlActions) extends InjectedController {

  val schema:Schema[GraphqlActions,Unit] = gqlSchema.create

  def graphiql:Action[AnyContent] = Action {
    request => Logger.info("Got Any content request from:" + request.remoteAddress)
    //Ok(views.html.graphiql(assets))
    Ok(GraphiqlPage.render("Explore TAP with GraphiQL"))
  }

  def graphql:Action[JsValue] = Action.async(parse.json) { request =>
    val query = (request.body \ "query").as[String]
    val operation = (request.body \ "operationName").asOpt[String]
    val variables = (request.body \ "variables").asOpt[JsObject].getOrElse(Json.obj())
    Logger.info(s"Query received from ${request.remoteAddress} >>> ${operation.getOrElse("No query")}")
    Logger.info(s"Variables: $variables")
    process(query,operation,variables)
  }

  def process(query:String,name:Option[String],variables:JsObject):Future[Result] = QueryParser.parse(query) match {
    case Success(queryAst) => executeGraphQLQuery(queryAst, name, variables)
    case Failure(error: SyntaxError) => Future.successful(BadRequest(error.getMessage))
    case _ => Future.successful(BadRequest("There was a problem with the request to TAP graphql."))
  }

  def executeGraphQLQuery(query: Document, name: Option[String], vars: JsObject):Future[Result] = {
     Executor.execute(schema, query, actions, operationName = name, variables = vars)
      .map(Ok(_))
      .recover {
        case error: QueryAnalysisError => BadRequest(error.resolveError)
        case error: ErrorWithResolver => InternalServerError(error.resolveError)
      }

  }
} 
Example 15
Source File: DeliverySchemeSpec.scala    From sangria   with Apache License 2.0 5 votes vote down vote up
package sangria.parser

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class DeliverySchemeSpec extends AnyWordSpec with Matchers {
  "DeliveryScheme" should {
    "by default support `Try`" in {
      import scala.util.{Success, Failure}

      QueryParser.parse("{ field }") shouldBe a [Success[_]]
      QueryParser.parse("}") shouldBe a [Failure[_]]
    }

    "support `Either`" in {
      import sangria.parser.DeliveryScheme.Either
      import scala.util.{Left, Right}

      QueryParser.parse("{ field }") shouldBe a [Right[_, _]]
      QueryParser.parse("}") shouldBe a [Left[_, _]]
    }

    "support exception throwing" in {
      import sangria.parser.DeliveryScheme.Throw
      import sangria.ast.Document

      QueryParser.parse("{ field }") shouldBe a [Document]
      a [SyntaxError] should be thrownBy QueryParser.parse("}")
    }
  }
} 
Example 16
Source File: package.scala    From sangria   with Apache License 2.0 5 votes vote down vote up
package sangria

import scala.language.experimental.{macros => `scalac, please just let me do it!`}
import sangria.ast.{Document, InputDocument, Value}

package object macros {
  implicit class LiteralGraphQLStringContext(val sc: StringContext) extends AnyVal {
    def gql(args: Any*): Document = macro ParseMacro.impl
    def gqlInp(args: Any*): Value = macro ParseMacro.implInput
    def gqlInpDoc(args: Any*): InputDocument = macro ParseMacro.implInputDoc

    def graphql(args: Any*): Document = macro ParseMacro.impl
    def graphqlInput(args: Any*): Value = macro ParseMacro.implInput
    def graphqlInputDoc(args: Any*): InputDocument = macro ParseMacro.implInputDoc
  }

} 
Example 17
Source File: package.scala    From drunk   with Apache License 2.0 5 votes vote down vote up
package com.github.jarlakxen.drunk

import sangria.ast.{ Document, Field, FragmentDefinition, FragmentSpread, OperationDefinition, Selection }

package object ast {

  val TypenameFieldName = "__typename"

  val TypenameField = Field(
    None,
    TypenameFieldName,
    Vector.empty,
    Vector.empty,
    Vector.empty)

  
  def addTypename(doc: Document): Document = {
    val newDefinitions = doc.definitions.map {
      case op: OperationDefinition => addTypename(op)
      case frag: FragmentDefinition => addTypename(frag)
      case other => other
    }

    doc.copy(definitions = newDefinitions)
  }

  private def addTypename(op: OperationDefinition): OperationDefinition = {
    val newSelections = op.selections.map(addTypename)
    op.copy(selections = newSelections)
  }

  private def addTypename(frag: FragmentDefinition): FragmentDefinition = {
    val newSelections = frag.selections.map(addTypename)

    if (newSelections.exists(s => s.isInstanceOf[Field] && s.asInstanceOf[Field].name == TypenameFieldName)) {
      frag.copy(selections = newSelections)
    } else {
      frag.copy(selections = TypenameField +: newSelections)
    }
  }

  private def addTypename(select: Selection): Selection = {
    select match {
      case field: Field if field.selections.nonEmpty => addTypename(field)
      case other => other
    }
  }

  private def addTypename(field: Field): Field = {
    var hasTypename = false
    val newSelections = field.selections.map {
      case field: Field if field.selections.nonEmpty =>
        if (field.name == TypenameFieldName) {
          hasTypename = true
        }
        addTypename(field)
      case frag: FragmentSpread =>
        hasTypename = true
        frag
      case other => other
    }

    if (!hasTypename) {
      field.copy(selections = TypenameField +: newSelections)
    } else {
      field.copy(selections = newSelections)
    }

  }

} 
Example 18
Source File: QueryComplexityFilter.scala    From naptime   with Apache License 2.0 5 votes vote down vote up
package org.coursera.naptime.ari.graphql.controllers.filters

import javax.inject.Inject
import javax.inject.Singleton

import com.typesafe.scalalogging.StrictLogging
import org.coursera.naptime.ari.Response
import org.coursera.naptime.ari.graphql.GraphqlSchemaProvider
import org.coursera.naptime.ari.graphql.SangriaGraphQlContext
import org.coursera.naptime.ari.graphql.controllers.GraphQLController
import org.coursera.naptime.ari.graphql.marshaller.NaptimeMarshaller._
import org.coursera.naptime.ari.graphql.resolvers.NaptimeResolver
import org.coursera.naptime.ari.graphql.resolvers.NoopResolver
import play.api.libs.json.JsObject
import play.api.libs.json.Json
import play.api.mvc.Results
import sangria.ast.Document
import sangria.execution.ErrorWithResolver
import sangria.execution.Executor
import sangria.execution.QueryAnalysisError
import sangria.execution.QueryReducer

import scala.concurrent.ExecutionContext
import scala.concurrent.Future

@Singleton
class QueryComplexityFilter @Inject()(
    graphqlSchemaProvider: GraphqlSchemaProvider,
    configuration: ComplexityFilterConfiguration)(implicit executionContext: ExecutionContext)
    extends Filter
    with Results
    with StrictLogging {

  val MAX_COMPLEXITY = configuration.maxComplexity

  def apply(nextFilter: FilterFn): FilterFn = { incoming =>
    computeComplexity(incoming.document, incoming.variables)
      .flatMap { complexity =>
        if (complexity > MAX_COMPLEXITY) {
          Future.successful(
            OutgoingQuery(
              response = Json.obj("error" -> "Query is too complex.", "complexity" -> complexity),
              ariResponse = None))
        } else {
          nextFilter.apply(incoming)
        }
      }
      .recover {
        case error: QueryAnalysisError =>
          OutgoingQuery(error.resolveError.as[JsObject], None)
        case error: ErrorWithResolver =>
          OutgoingQuery(error.resolveError.as[JsObject], None)
        case error: Exception =>
          OutgoingQuery(Json.obj("errors" -> Json.arr(error.getMessage)), None)
      }
  }

  private[graphql] def computeComplexity(queryAst: Document, variables: JsObject)(
      implicit executionContext: ExecutionContext): Future[Double] = {
    // TODO(bryan): is there a way around this var?
    var complexity = 0D
    val complReducer = QueryReducer.measureComplexity[SangriaGraphQlContext] { (c, ctx) =>
      complexity = c
      ctx
    }
    val executorFut = Executor.execute(
      graphqlSchemaProvider.schema,
      queryAst,
      SangriaGraphQlContext(null, null, executionContext, debugMode = false),
      variables = variables,
      exceptionHandler = GraphQLController.exceptionHandler(logger),
      queryReducers = List(complReducer),
      deferredResolver = new NoopResolver())

    executorFut.map { _ =>
      complexity
    }

  }
}

case class ComplexityFilterConfiguration(maxComplexity: Int)

object ComplexityFilterConfiguration {
  val DEFAULT = ComplexityFilterConfiguration(100000)
} 
Example 19
Source File: SangriaGraphQLSupport.scala    From incubator-s2graph   with Apache License 2.0 5 votes vote down vote up
package org.apache.s2graph.http

import java.nio.charset.Charset

import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshaller}
import akka.util.ByteString
import sangria.ast.Document
import sangria.parser.QueryParser
import sangria.renderer.{QueryRenderer, QueryRendererConfig}

trait SangriaGraphQLSupport {
  private val mediaTypes: Seq[MediaType.WithFixedCharset] =
    Seq(MediaType.applicationWithFixedCharset("graphql", HttpCharsets.`UTF-8`, "graphql"))

  private val unmarshallerContentTypes: Seq[ContentTypeRange] = mediaTypes.map(ContentTypeRange.apply)

  implicit def documentMarshaller(implicit config: QueryRendererConfig = QueryRenderer.Compact): ToEntityMarshaller[Document] = {
    Marshaller.oneOf(mediaTypes: _*) {
      mediaType ⇒
        Marshaller.withFixedContentType(ContentType(mediaType)) {
          json ⇒ HttpEntity(mediaType, QueryRenderer.render(json, config))
        }
    }
  }

  implicit val documentUnmarshaller: FromEntityUnmarshaller[Document] = {
    Unmarshaller.byteStringUnmarshaller
      .forContentTypes(unmarshallerContentTypes: _*)
      .map {
        case ByteString.empty ⇒ throw Unmarshaller.NoContentException
        case data ⇒
          import sangria.parser.DeliveryScheme.Throw
          QueryParser.parse(data.decodeString(Charset.forName("UTF-8")))
      }
  }
}