sangria.parser.QueryParser Scala Examples

The following examples show how to use sangria.parser.QueryParser. 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: 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 2
Source File: GraphQLSchemaSpec.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.graphql

import org.scalatest.{Matchers, WordSpec}
import sangria.parser.QueryParser
import sangria.schema.SchemaChange.DescriptionChange
import sangria.schema.Schema

import scala.io.Source

class GraphQLSchemaSpec extends WordSpec with Matchers {
  "The rendered schema" should {
    "match the expected schema definition" in {
      val idl =
        Source.fromInputStream(getClass.getResourceAsStream("/schema.graphql"), "UTF-8").mkString
      val schema = Schema.buildFromAst(QueryParser.parse(idl).get)

      // Compare schemata but ignore description changes.
      val changes = schema
        .compare(new GraphQLSchema(Set()).QuerySchema)
        .filter(!_.isInstanceOf[DescriptionChange])

      if (changes.nonEmpty) {
        fail(
          s"Schema definition does not match:\n- ${changes.map(_.description).mkString("\n- ")}\n")
      }
    }
  }
} 
Example 3
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")))
      }
  }
} 
Example 4
Source File: FilterTest.scala    From naptime   with Apache License 2.0 5 votes vote down vote up
package org.coursera.naptime.ari.graphql.controllers.filters

import org.coursera.naptime.ari.graphql.GraphqlSchemaProvider
import org.coursera.naptime.ari.graphql.Models
import org.coursera.naptime.ari.graphql.SangriaGraphQlContext
import org.coursera.naptime.ari.graphql.SangriaGraphQlSchemaBuilder
import org.coursera.naptime.ari.graphql.models.MergedCourse
import org.coursera.naptime.ari.graphql.models.MergedInstructor
import org.coursera.naptime.ari.graphql.models.MergedPartner
import org.mockito.Mockito.when
import org.scalatest.concurrent.IntegrationPatience
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.junit.AssertionsForJUnit
import org.scalatest.mockito.MockitoSugar
import play.api.libs.json.Json
import play.api.test.FakeRequest
import sangria.parser.QueryParser
import sangria.schema.Schema

import scala.concurrent.Future

trait FilterTest
    extends AssertionsForJUnit
    with MockitoSugar
    with ScalaFutures
    with IntegrationPatience {

  val baseOutgoingQuery = OutgoingQuery(Json.obj(), None)

  def noopFilter(incomingQuery: IncomingQuery) = {
    Future.successful(baseOutgoingQuery)
  }

  def exceptionThrowingFilter(incomingQuery: IncomingQuery): Future[OutgoingQuery] = {
    assert(false, "This filter should not be run")
    Future.successful(baseOutgoingQuery)
  }

  val filter: Filter

  val defaultQuery =
    """
      |query {
      |  __schema {
      |    queryType {
      |      name
      |    }
      |  }
      |}
    """.stripMargin

  val graphqlSchemaProvider = mock[GraphqlSchemaProvider]

  val allResources = Set(Models.courseResource, Models.instructorResource, Models.partnersResource)

  val schemaTypes = Map(
    "org.coursera.naptime.ari.graphql.models.MergedCourse" -> MergedCourse.SCHEMA,
    "org.coursera.naptime.ari.graphql.models.MergedPartner" -> MergedPartner.SCHEMA,
    "org.coursera.naptime.ari.graphql.models.MergedInstructor" -> MergedInstructor.SCHEMA)
  val builder = new SangriaGraphQlSchemaBuilder(allResources, schemaTypes)

  val schema = builder.generateSchema().data.asInstanceOf[Schema[SangriaGraphQlContext, Any]]
  when(graphqlSchemaProvider.schema).thenReturn(schema)

  def generateIncomingQuery(query: String = defaultQuery) = {
    val document = QueryParser.parse(query).get
    val header = FakeRequest("POST", s"/graphql").withBody(query)
    val variables = Json.obj()
    val operation = None
    IncomingQuery(document, header, variables, operation, debugMode = false)
  }

  def run(incomingQuery: IncomingQuery): Future[OutgoingQuery] = {
    filter.apply(noopFilter)(incomingQuery)
  }

  def ensureNotPropagated(incomingQuery: IncomingQuery): Future[OutgoingQuery] = {
    filter.apply(exceptionThrowingFilter)(incomingQuery)
  }
} 
Example 5
Source File: TestExecutorHelper.scala    From naptime   with Apache License 2.0 5 votes vote down vote up
package org.coursera.naptime.ari.graphql.schema

import com.linkedin.data.DataMap
import org.coursera.naptime.ari.graphql.Models
import org.coursera.naptime.ari.graphql.SangriaGraphQlContext
import org.coursera.naptime.ari.graphql.SangriaGraphQlSchemaBuilder
import org.coursera.naptime.ari.graphql.marshaller.NaptimeMarshaller._
import org.coursera.naptime.ari.graphql.models.RecordWithUnionTypes
import org.coursera.naptime.ari.graphql.models.MergedCourse
import org.coursera.naptime.ari.graphql.models.MergedInstructor
import org.coursera.naptime.ari.graphql.models.MergedPartner
import org.coursera.naptime.ari.graphql.resolvers.NaptimeResolver
import play.api.libs.json.JsObject
import sangria.execution.Executor
import sangria.parser.QueryParser
import sangria.schema.Schema

import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration

class TestExecutorHelper {

  def executeQuery(
      queryString: String,
      resourceData: Map[String, Map[String, List[DataMap]]]): JsObject = {
    val schemaTypes = Map(
      "org.coursera.naptime.ari.graphql.models.MergedCourse" -> MergedCourse.SCHEMA,
      "org.coursera.naptime.ari.graphql.models.FakeModel" -> RecordWithUnionTypes.SCHEMA,
      "org.coursera.naptime.ari.graphql.models.MergedPartner" -> MergedPartner.SCHEMA,
      "org.coursera.naptime.ari.graphql.models.MergedInstructor" -> MergedInstructor.SCHEMA)
    val allResources =
      Set(
        Models.courseResource,
        Models.instructorResource,
        Models.partnersResource,
        Models.fakeModelResource)
    val builder = new SangriaGraphQlSchemaBuilder(allResources, schemaTypes)
    val schema = builder.generateSchema().data.asInstanceOf[Schema[SangriaGraphQlContext, Any]]

    val queryAst = QueryParser.parse(queryString).get

    val context = SangriaGraphQlContext(
      FakeFetcherApi(resourceData),
      null,
      ExecutionContext.global,
      debugMode = true)

    Await
      .result(
        Executor
          .execute(
            schema,
            queryAst,
            context,
            variables = JsObject(Map.empty[String, JsObject]),
            deferredResolver = new NaptimeResolver()),
        Duration.Inf)
      .asInstanceOf[JsObject]
  }

} 
Example 6
Source File: ParseMacro.scala    From sangria   with Apache License 2.0 5 votes vote down vote up
package sangria.macros

import sangria.parser.{SyntaxError, QueryParser}

import scala.reflect.macros.blackbox

class ParseMacro(context: blackbox.Context) extends {
  val c = context
} with MacroAstLiftable {

  import c.universe._

  def impl(args: Expr[Any]*) =
    if (args.nonEmpty)
      c.abort(c.enclosingPosition, "String interpolation is not supported for `graphql`/`gql` macro at the moment.")
    else
      c.prefix.tree match {
        // Expects a string interpolation that doesn't contain any
        // expressions, thus containing only a single tree
        case Apply(_, List(Apply(_, t :: Nil))) =>
          val q"${gql: String}" = t

          try {
            q"${QueryParser.parse(gql).get}"
          } catch {
            case error: SyntaxError => syntaxError(error)
          }
        case _ =>
          c.abort(c.enclosingPosition, "Invalid `graphql` invocation syntax.")
      }

  def implInput(args: Expr[Any]*) =
    if (args.nonEmpty)
      c.abort(c.enclosingPosition, "String interpolation is not supported for `graphqlInput`/`gqlInp` macro at the moment.")
    else
      c.prefix.tree match {
        // Expects a string interpolation that doesn't contain any
        // expressions, thus containing only a single tree
        case Apply(_, List(Apply(_, t :: Nil))) =>
          val q"${gql: String}" = t

          try {
            q"${QueryParser.parseInput(gql).get}"
          } catch {
            case error: SyntaxError => syntaxError(error)
          }
        case _ =>
          c.abort(c.enclosingPosition, "Invalid `graphql` invocation syntax.")
      }

  def implInputDoc(args: Expr[Any]*) =
    if (args.nonEmpty)
      c.abort(c.enclosingPosition, "String interpolation is not supported for `gqlInpDoc` macro at the moment.")
    else
      c.prefix.tree match {
        // Expects a string interpolation that doesn't contain any
        // expressions, thus containing only a single tree
        case Apply(_, List(Apply(_, t :: Nil))) =>
          val q"${gql: String}" = t

          try {
            q"${QueryParser.parseInputDocument(gql).get}"
          } catch {
            case error: SyntaxError => syntaxError(error)
          }
        case _ =>
          c.abort(c.enclosingPosition, "Invalid `graphql` invocation syntax.")
      }

  def syntaxError(error: SyntaxError) = {
    val errorPos = error.originalError.position
    val enclosingCol = if (errorPos.line == 1) calcStringStart else 0
    val source = c.enclosingPosition.source
    val line = source.lineToOffset(c.enclosingPosition.line + (errorPos.line - 2))
    val col = line + enclosingCol + (errorPos.column - 1)
    val pos = c.enclosingPosition.withPoint(col)

    c.abort(pos, error.formattedError(showPosition = false))
  }

  def calcStringStart: Int = {
    val source = c.enclosingPosition.source
    val content = source.lineToString(c.enclosingPosition.line - 1)
    val contentStart = content.substring(c.enclosingPosition.column - 1)
    val offset = "(\\w+\"+)".r.findFirstMatchIn(contentStart).fold(0)(_.end)

    c.enclosingPosition.column - 1 + offset
  }
} 
Example 7
Source File: ContextPassingSpec.scala    From sangria   with Apache License 2.0 5 votes vote down vote up
package sangria.execution

import sangria.parser.QueryParser
import sangria.schema._
import sangria.util.FutureResultSupport

import scala.util.Success

import scala.concurrent.ExecutionContext.Implicits.global
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class ContextPassingSpec extends AnyWordSpec with Matchers with FutureResultSupport {
  trait ColorComponent {
    def color = "green"
  }

  trait NameComponent {
    def name = "foo"
  }

  trait PersonComponent {
    this: NameComponent =>

    def fullName = name + " bar"
  }

  class Cake extends ColorComponent with NameComponent with PersonComponent

  val ColorType = ObjectType("Color", fields[ColorComponent with NameComponent, Unit](
    Field("colorName", StringType, resolve = _.ctx.color),
    Field("name", StringType, resolve = _.ctx.name)))

  val NameType = ObjectType("Name", fields[NameComponent, Unit](
    Field("name", StringType, resolve = _.ctx.name)))

  val PersonType = ObjectType("Person", fields[PersonComponent, Unit](
    Field("fullName", StringType, resolve = _.ctx.fullName),
    Field("name", NameType, resolve = _ => ())))

  def colorField[Ctx <: ColorComponent with NameComponent] =
    Field("color", ColorType, None, resolve = (ctx: Context[Ctx, Unit]) => ())

  val QueryType = ObjectType("Query", fields[Cake, Unit](
    colorField,
    Field("person", PersonType, resolve = _ => ())
  ))

  val schema = Schema(QueryType)

  "Context" should {
    "should respect inheritance" in {
      val Success(doc) = QueryParser.parse("""
        {
          color {name, colorName}
          person {
            name {name}
            fullName
          }
        }
        """)

      Executor.execute(schema, doc, userContext = new Cake).await should be (Map(
        "data" -> Map(
          "color" -> Map(
            "name" -> "foo",
            "colorName" -> "green"),
          "person" -> Map(
            "name" -> Map("name" -> "foo"),
            "fullName" -> "foo bar"))))
    }
  }

} 
Example 8
Source File: FileUtil.scala    From sangria   with Apache License 2.0 5 votes vote down vote up
package sangria.util

import java.io.File

import io.github.classgraph.ClassGraph
import sangria.parser.QueryParser
import sangria.parser.DeliveryScheme.Throw
import spray.json._

import scala.io.Source
import net.jcazevedo.moultingyaml._

import scala.collection.JavaConverters._


object FileUtil extends StringMatchers {
  def loadQuery(name: String) =
    loadResource("queries/" + name)

  def loadYaml(name: String, root: String = "scenarios") =
    loadResource(root + "/" + name).parseYaml

  def loadScenarios(path: String, root: String = "scenarios") = this.synchronized {
    val yamlResources = new ClassGraph()
      .whitelistPackages(root + "." + path)
      .scan()
      .getResourcesWithExtension("yaml")
      .asScala
      .groupBy(_.getPath).mapValues(_.head) // deduplicate (`ClassGraph` gives duplicates for some reason)
      .values
      .toVector

    yamlResources.map { resource =>
      val name = resource.getPath.substring(resource.getPath.lastIndexOf("/") + 1)
      val relativePath = resource.getPathRelativeToClasspathElement
      val stream = this.getClass.getResourceAsStream("/" + relativePath)
      val contents = Source.fromInputStream(stream, "UTF-8").mkString.parseYaml

      ScenarioFile(name, relativePath, contents)
    }
  }

  def loadSchema(path: String) =
    QueryParser.parse(loadResource(path))

  def loadTestData(path: String): Either[YamlValue, JsValue] = {
    val text = loadResource(path)

    if (path endsWith ".yaml") Left(text.parseYaml)
    else if (path endsWith ".json") Right(text.parseJson)
    else throw new IllegalArgumentException(s"Unsupported file format for test data '$path'. Only `*.json` and `*.yaml` files are supported.")
  }

  def loadResource(path: String) =
    Option(this.getClass.getResourceAsStream("/" + path)) match {
      case Some(res) => stripCarriageReturns(Source.fromInputStream(res, "UTF-8").mkString)
      case None => throw new IllegalArgumentException("Resource not found: /" + path)
    }

  case class ScenarioFile(fileName: String, path: String, scenario: YamlValue) {
    def folder = path.substring(0, path.lastIndexOf("/"))
  }
} 
Example 9
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 10
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 11
Source File: MutationCallbackSchemaExecutor.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.deprecated.actions

import com.typesafe.scalalogging.LazyLogging
import cool.graph.client.ClientInjector
import cool.graph.client.database.{DeferredResolverProvider, SimpleManyModelDeferredResolver, SimpleToManyDeferredResolver}
import cool.graph.cuid.Cuid.createCuid
import cool.graph.deprecated.actions.schemas.{ActionUserContext, MutationMetaData}
import cool.graph.shared.models.{Model, Project}
import cool.graph.shared.schema.JsonMarshalling._
import sangria.execution.Executor
import sangria.parser.QueryParser
import sangria.schema.Schema
import spray.json.{JsObject, JsString}

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

case class Event(id: String, url: String, payload: Option[JsObject])

class MutationCallbackSchemaExecutor(project: Project,
                                     model: Model,
                                     schema: Schema[ActionUserContext, Unit],
                                     nodeId: String,
                                     fragment: String,
                                     url: String,
                                     mutationId: String)(implicit injector: ClientInjector)
    extends LazyLogging {
  def execute: Future[Event] = {
    implicit val inj = injector.toScaldi

    val dataFut = QueryParser.parse(fragment) match {
      case Success(queryAst) =>
        Executor.execute(
          schema,
          queryAst,
          deferredResolver = new DeferredResolverProvider(
            new SimpleToManyDeferredResolver,
            new SimpleManyModelDeferredResolver,
            skipPermissionCheck = true
          ),
          userContext = ActionUserContext(
            requestId = "",
            project = project,
            nodeId = nodeId,
            mutation = MutationMetaData(id = mutationId, _type = "Create"),
            log = (x: String) => logger.info(x)
          )
        )
      case Failure(error) =>
        Future.successful(JsObject("error" -> JsString(error.getMessage)))
    }

    dataFut
      .map {
        case JsObject(dataMap) => Event(id = createCuid(), url = url, payload = Some(dataMap("data").asJsObject))
        case json              => sys.error(s"Must only receive JsObjects here. But got instead: ${json.compactPrint}")
      }

  }
} 
Example 12
Source File: QueryPermissionValidator.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.client.authorization.queryPermissions

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import cool.graph.client.database.{DeferredResolverProvider, SimpleManyModelDeferredResolver, SimpleToManyDeferredResolver}
import cool.graph.client.{ClientInjector, UserContext}
import cool.graph.metrics.ClientSharedMetrics
import cool.graph.shared.errors.UserAPIErrors.InsufficientPermissions
import cool.graph.shared.models.{AuthenticatedRequest, Project}
import cool.graph.shared.queryPermissions.PermissionSchemaResolver
import sangria.ast._
import sangria.execution.deferred.DeferredResolver
import sangria.execution.{DeprecationTracker, Executor}
import sangria.marshalling.queryAst._
import sangria.marshalling.{InputUnmarshaller, QueryAstResultMarshaller}
import sangria.parser.QueryParser
import sangria.schema.Schema
import sangria.validation.QueryValidator

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

class QueryPermissionValidator(project: Project)(implicit injector: ClientInjector, system: ActorSystem, materializer: ActorMaterializer) {

  lazy val schema: Schema[UserContext, Unit] = PermissionSchemaResolver.permissionSchema(project)(injector.toScaldi)

  lazy val deferredResolverProvider: DeferredResolver[Any] =
    new DeferredResolverProvider(new SimpleToManyDeferredResolver, new SimpleManyModelDeferredResolver, skipPermissionCheck = true)
      .asInstanceOf[DeferredResolver[Any]]

  lazy val executor = Executor(
    schema = schema.asInstanceOf[Schema[Any, Any]],
    queryValidator = QueryValidator.default,
    deferredResolver = deferredResolverProvider,
    exceptionHandler = PartialFunction.empty,
    deprecationTracker = DeprecationTracker.empty,
    middleware = Nil,
    maxQueryDepth = None,
    queryReducers = Nil
  )

  def validate(
      query: String,
      variables: Map[String, Any],
      authenticatedRequest: Option[AuthenticatedRequest],
      alwaysQueryMasterDatabase: Boolean
  ): Future[Boolean] = {
    injector.onPermissionQuery(project.id)
    ClientSharedMetrics.queryPermissionCounter.inc(project.id)

    val context = new UserContext(
      project = project,
      authenticatedRequest = authenticatedRequest,
      requestId = "grap-permission-query",
      requestIp = "graph-permission-query",
      project.ownerId,
      (x: String) => Unit,
      alwaysQueryMasterDatabase = alwaysQueryMasterDatabase
    )(injector.toScaldi)

    val dataFut: Future[QueryAstResultMarshaller#Node] =
      QueryParser.parse(query) match {
        case Success(_queryAst) =>
          executor
            .execute(queryAst = _queryAst, userContext = context, root = (), variables = InputUnmarshaller.mapVars(variables))
            .recover {
              case e: Throwable => throw InsufficientPermissions(s"Permission Query is invalid. Could not be executed. Error Message: ${e.getMessage}")
            }
        case Failure(error) =>
          throw InsufficientPermissions(s"Permission Query is invalid. Could not be parsed. Error Message: ${error.getMessage}")
      }

    dataFut.map(traverseAndCheckForLeafs)
  }

  private def traverseAndCheckForLeafs(root: AstNode): Boolean = {
    root match {
      case ObjectValue(fields, _, _)   => fields.forall(field => traverseAndCheckForLeafs(field))
      case ObjectField(_, value, _, _) => traverseAndCheckForLeafs(value)
      case x: BooleanValue             => x.value
      case _                           => sys.error(s"Received unknown type of AstNode. Could not handle: $root") //triggered by NullValue(Vector(),None)
    }
  }
} 
Example 13
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 14
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 15
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 16
Source File: StarWarsMutationSpec.scala    From sangria-relay   with Apache License 2.0 5 votes vote down vote up
package sangria.relay.starWars

import sangria.execution.Executor
import sangria.parser.QueryParser
import sangria.relay.starWars.StarWarsData.ShipRepo
import sangria.relay.util.AwaitSupport
import sangria.marshalling.InputUnmarshaller.mapVars

import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class StarWarsMutationSpec extends AnyWordSpec with Matchers with AwaitSupport {
  "Mutation" should {
    "Correctly mutates the data set" in {
      val Success(doc) = QueryParser.parse(
        """
          mutation AddBWingQuery($input: IntroduceShipInput!) {
            introduceShip(input: $input) {
              ship {
                id
                name
              }
              faction {
                name
              }
              clientMutationId
            }
          }
        """)

      val vars = mapVars(
        "input" -> Map(
          "shipName" -> "B-Wing",
          "factionId" -> "RmFjdGlvbjox",
          "clientMutationId" -> "abcde"
        )
      )
      
      Executor.execute(StarWarsSchema.schema, doc, variables = vars, userContext = new ShipRepo).await should be(
        Map(
          "data" -> Map(
            "introduceShip" -> Map(
              "ship" -> Map(
                "id" -> "U2hpcDo5",
                "name" -> "B-Wing"
              ),
              "faction" -> Map(
                "name" -> "Alliance to Restore the Republic"
              ),
              "clientMutationId" -> "abcde"
            ))))
    }
  }
}