Skip to content

Commit

Permalink
make query validation check variables #major
Browse files Browse the repository at this point in the history
QueryValidator needs access to variables now, so this causes a breaking change.
  • Loading branch information
Amanda Steinwedel committed Jul 19, 2024
1 parent cf734e4 commit 01da0f8
Show file tree
Hide file tree
Showing 14 changed files with 547 additions and 229 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class OverlappingFieldsCanBeMergedBenchmark {
bh.consume(doValidate(validator, deepAbstractConcrete))

private def doValidate(validator: QueryValidator, document: Document): Vector[Violation] = {
val result = validator.validateQuery(schema, document, None)
val result = validator.validateQuery(schema, document, Map.empty, None)
require(result.isEmpty)
result
}
Expand Down
341 changes: 180 additions & 161 deletions modules/core/src/main/scala/sangria/execution/Executor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,107 +29,116 @@ case class Executor[Ctx, Root](
operationName: Option[String] = None,
variables: Input = emptyMapVars
)(implicit um: InputUnmarshaller[Input]): Future[PreparedQuery[Ctx, Root, Input]] = {
val (violations, validationTiming) =
TimeMeasurement.measure(queryValidator.validateQuery(schema, queryAst, errorsLimit))
val scalarMiddleware = Middleware.composeFromScalarMiddleware(middleware, userContext)
val valueCollector = new ValueCollector[Ctx, Input](
schema,
variables,
queryAst.sourceMapper,
deprecationTracker,
userContext,
exceptionHandler,
scalarMiddleware,
false)(um)

if (violations.nonEmpty)
Future.failed(ValidationError(violations, exceptionHandler))
else {
val scalarMiddleware = Middleware.composeFromScalarMiddleware(middleware, userContext)
val valueCollector = new ValueCollector[Ctx, Input](
schema,
variables,
queryAst.sourceMapper,
deprecationTracker,
userContext,
exceptionHandler,
val operationCtx = for {
operation <- Executor.getOperation(exceptionHandler, queryAst, operationName)
unmarshalledVariables <- valueCollector.getVariableValues(
operation.variables,
scalarMiddleware,
false)(um)

val executionResult = for {
operation <- Executor.getOperation(exceptionHandler, queryAst, operationName)
unmarshalledVariables <- valueCollector.getVariableValues(
operation.variables,
scalarMiddleware,
errorsLimit
)
fieldCollector = new FieldCollector[Ctx, Root](
schema,
queryAst,
unmarshalledVariables,
queryAst.sourceMapper,
valueCollector,
exceptionHandler)
tpe <- Executor.getOperationRootType(
schema,
exceptionHandler,
operation,
queryAst.sourceMapper)
fields <- fieldCollector.collectFields(ExecutionPath.empty, tpe, Vector(operation))
} yield {
val preparedFields = fields.fields.flatMap {
case CollectedField(_, astField, Success(_)) =>
val allFields =
tpe.getField(schema, astField.name).asInstanceOf[Vector[Field[Ctx, Root]]]
val field = allFields.head
val args = valueCollector.getFieldArgumentValues(
ExecutionPath.empty.add(astField, tpe),
Some(astField),
field.arguments,
astField.arguments,
unmarshalledVariables)
errorsLimit
)
} yield (operation, unmarshalledVariables)

args.toOption.map(PreparedField(field, _))
case _ => None
}
operationCtx match {
case Failure(error) => Future.failed(error)
case Success((operation, unmarshalledVariables)) =>
val (violations, validationTiming) =
TimeMeasurement.measure(
queryValidator.validateQuery(schema, queryAst, unmarshalledVariables, errorsLimit))

QueryReducerExecutor
.reduceQuery(
schema,
queryReducers,
exceptionHandler,
fieldCollector,
valueCollector,
unmarshalledVariables,
tpe,
fields,
userContext)
.map { case (newCtx, timing) =>
new PreparedQuery[Ctx, Root, Input](
queryAst,
if (violations.nonEmpty)
Future.failed(ValidationError(violations, exceptionHandler))
else {
val executionResult = for {
tpe <- Executor.getOperationRootType(
schema,
exceptionHandler,
operation,
tpe,
newCtx,
root,
preparedFields,
(c: Ctx, r: Root, m: ResultMarshaller, scheme: ExecutionScheme) =>
executeOperation(
queryAst.sourceMapper)
fieldCollector = new FieldCollector[Ctx, Root](
schema,
queryAst,
unmarshalledVariables,
queryAst.sourceMapper,
valueCollector,
exceptionHandler)
fields <- fieldCollector.collectFields(ExecutionPath.empty, tpe, Vector(operation))
} yield {
val preparedFields = fields.fields.flatMap {
case CollectedField(_, astField, Success(_)) =>
val allFields =
tpe.getField(schema, astField.name).asInstanceOf[Vector[Field[Ctx, Root]]]
val field = allFields.head
val args = valueCollector.getFieldArgumentValues(
ExecutionPath.empty.add(astField, tpe),
Some(astField),
field.arguments,
astField.arguments,
unmarshalledVariables)

args.toOption.map(PreparedField(field, _))
case _ => None
}

QueryReducerExecutor
.reduceQuery(
schema,
queryReducers,
exceptionHandler,
fieldCollector,
valueCollector,
unmarshalledVariables,
tpe,
fields,
userContext)
.map { case (newCtx, timing) =>
new PreparedQuery[Ctx, Root, Input](
queryAst,
operationName,
variables,
um,
operation,
queryAst.sourceMapper,
valueCollector,
fieldCollector,
m,
unmarshalledVariables,
tpe,
fields,
c,
r,
scheme,
validationTiming,
timing
newCtx,
root,
preparedFields,
(c: Ctx, r: Root, m: ResultMarshaller, scheme: ExecutionScheme) =>
executeOperation(
queryAst,
operationName,
variables,
um,
operation,
queryAst.sourceMapper,
valueCollector,
fieldCollector,
m,
unmarshalledVariables,
tpe,
fields,
c,
r,
scheme,
validationTiming,
timing
)
)
)
}
}
}

executionResult match {
case Success(future) => future
case Failure(error) => Future.failed(error)
}
executionResult match {
case Success(future) => future
case Failure(error) => Future.failed(error)
}
}

}
}

Expand All @@ -143,82 +152,92 @@ case class Executor[Ctx, Root](
marshaller: ResultMarshaller,
um: InputUnmarshaller[Input],
scheme: ExecutionScheme): scheme.Result[Ctx, marshaller.Node] = {
val (violations, validationTiming) =
TimeMeasurement.measure(queryValidator.validateQuery(schema, queryAst, errorsLimit))

if (violations.nonEmpty)
scheme.failed(ValidationError(violations, exceptionHandler))
else {
val scalarMiddleware = Middleware.composeFromScalarMiddleware(middleware, userContext)
val valueCollector = new ValueCollector[Ctx, Input](
schema,
variables,
queryAst.sourceMapper,
deprecationTracker,
userContext,
exceptionHandler,
val scalarMiddleware = Middleware.composeFromScalarMiddleware(middleware, userContext)
val valueCollector = new ValueCollector[Ctx, Input](
schema,
variables,
queryAst.sourceMapper,
deprecationTracker,
userContext,
exceptionHandler,
scalarMiddleware,
false)(um)

val operationCtx = for {
operation <- Executor.getOperation(exceptionHandler, queryAst, operationName)
unmarshalledVariables <- valueCollector.getVariableValues(
operation.variables,
scalarMiddleware,
false)(um)
errorsLimit
)
} yield (operation, unmarshalledVariables)

val executionResult = for {
operation <- Executor.getOperation(exceptionHandler, queryAst, operationName)
unmarshalledVariables <- valueCollector.getVariableValues(
operation.variables,
scalarMiddleware,
errorsLimit
)
fieldCollector = new FieldCollector[Ctx, Root](
schema,
queryAst,
unmarshalledVariables,
queryAst.sourceMapper,
valueCollector,
exceptionHandler)
tpe <- Executor.getOperationRootType(
schema,
exceptionHandler,
operation,
queryAst.sourceMapper)
fields <- fieldCollector.collectFields(ExecutionPath.empty, tpe, Vector(operation))
} yield {
val reduced = QueryReducerExecutor.reduceQuery(
schema,
queryReducers,
exceptionHandler,
fieldCollector,
valueCollector,
unmarshalledVariables,
tpe,
fields,
userContext)
scheme.flatMapFuture(reduced) { case (newCtx, timing) =>
executeOperation(
queryAst,
operationName,
variables,
um,
operation,
queryAst.sourceMapper,
valueCollector,
fieldCollector,
marshaller,
unmarshalledVariables,
tpe,
fields,
newCtx,
root,
scheme,
validationTiming,
timing
)
}
}
operationCtx match {
case Failure(error) => scheme.failed(error)
case Success((operation, unmarshalledVariables)) =>
val (violations, validationTiming) =
TimeMeasurement.measure(
queryValidator.validateQuery(schema, queryAst, unmarshalledVariables, errorsLimit))

executionResult match {
case Success(result) => result
case Failure(error) => scheme.failed(error)
}
if (violations.nonEmpty)
scheme.failed(ValidationError(violations, exceptionHandler))
else {
val executionResult = for {
tpe <- Executor.getOperationRootType(
schema,
exceptionHandler,
operation,
queryAst.sourceMapper)
fieldCollector = new FieldCollector[Ctx, Root](
schema,
queryAst,
unmarshalledVariables,
queryAst.sourceMapper,
valueCollector,
exceptionHandler)
fields <- fieldCollector.collectFields(ExecutionPath.empty, tpe, Vector(operation))
} yield {
val reduced = QueryReducerExecutor.reduceQuery(
schema,
queryReducers,
exceptionHandler,
fieldCollector,
valueCollector,
unmarshalledVariables,
tpe,
fields,
userContext)
scheme.flatMapFuture(reduced) { case (newCtx, timing) =>
executeOperation(
queryAst,
operationName,
variables,
um,
operation,
queryAst.sourceMapper,
valueCollector,
fieldCollector,
marshaller,
unmarshalledVariables,
tpe,
fields,
newCtx,
root,
scheme,
validationTiming,
timing
)
}
}

executionResult match {
case Success(result) => result
case Failure(error) => scheme.failed(error)
}
}
}

}

private def executeOperation[Input](
Expand Down
Loading

0 comments on commit 01da0f8

Please sign in to comment.