From bdbbd96820c5b731a485a555d1a17dab0e424e4a Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Wed, 9 Jan 2019 17:09:18 +1100 Subject: [PATCH 1/6] Some initial code to get started --- src/main/java/graphql/GraphQL.java | 31 +- src/main/java/graphql/ParseResult.java | 34 ++ .../execution/ExecutionIdProvider.java | 3 + .../nextgen/Instrumentation.java | 44 +++ .../InstrumentationCreateStateParameters.java | 27 ++ .../InstrumentationExecutionParameters.java | 75 +++++ .../InstrumentationValidationParameters.java | 38 +++ .../instrumentation/nextgen/package-info.java | 4 + .../java/graphql/nextgen/ExecutionInput.java | 162 ++++++++++ src/main/java/graphql/nextgen/GraphQL.java | 293 ++++++++++++++++++ .../java/graphql/nextgen/package-info.java | 4 + 11 files changed, 685 insertions(+), 30 deletions(-) create mode 100644 src/main/java/graphql/ParseResult.java create mode 100644 src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java create mode 100644 src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java create mode 100644 src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java create mode 100644 src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java create mode 100644 src/main/java/graphql/execution/instrumentation/nextgen/package-info.java create mode 100644 src/main/java/graphql/nextgen/ExecutionInput.java create mode 100644 src/main/java/graphql/nextgen/GraphQL.java create mode 100644 src/main/java/graphql/nextgen/package-info.java diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 8ee04b57fb..ace8f10fba 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -40,6 +40,7 @@ import static graphql.Assert.assertNotNull; import static graphql.InvalidSyntaxError.toInvalidSyntaxError; +import static graphql.execution.ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER; /** * This class is where all graphql-java query execution begins. It combines the objects that are needed @@ -89,7 +90,6 @@ public class GraphQL { private static final Logger log = LoggerFactory.getLogger(GraphQL.class); - private static final ExecutionIdProvider DEFAULT_EXECUTION_ID_PROVIDER = (query, operationName, context) -> ExecutionId.generate(); private final GraphQLSchema graphQLSchema; private final ExecutionStrategy queryStrategy; @@ -592,33 +592,4 @@ private CompletableFuture execute(ExecutionInput executionInput return future; } - private static class ParseResult { - private final Document document; - private final Exception exception; - - private ParseResult(Document document, Exception exception) { - this.document = document; - this.exception = exception; - } - - private boolean isFailure() { - return document == null; - } - - private Document getDocument() { - return document; - } - - private Exception getException() { - return exception; - } - - private static ParseResult of(Document document) { - return new ParseResult(document, null); - } - - private static ParseResult ofError(Exception e) { - return new ParseResult(null, e); - } - } } diff --git a/src/main/java/graphql/ParseResult.java b/src/main/java/graphql/ParseResult.java new file mode 100644 index 0000000000..90f0f0ae48 --- /dev/null +++ b/src/main/java/graphql/ParseResult.java @@ -0,0 +1,34 @@ +package graphql; + +import graphql.language.Document; + +@Internal +public class ParseResult { + private final Document document; + private final Exception exception; + + public ParseResult(Document document, Exception exception) { + this.document = document; + this.exception = exception; + } + + public boolean isFailure() { + return document == null; + } + + public Document getDocument() { + return document; + } + + public Exception getException() { + return exception; + } + + public static ParseResult of(Document document) { + return new ParseResult(document, null); + } + + public static ParseResult ofError(Exception e) { + return new ParseResult(null, e); + } +} diff --git a/src/main/java/graphql/execution/ExecutionIdProvider.java b/src/main/java/graphql/execution/ExecutionIdProvider.java index a9d8702166..7f4848c249 100644 --- a/src/main/java/graphql/execution/ExecutionIdProvider.java +++ b/src/main/java/graphql/execution/ExecutionIdProvider.java @@ -5,6 +5,9 @@ */ public interface ExecutionIdProvider { + ExecutionIdProvider DEFAULT_EXECUTION_ID_PROVIDER = (query, operationName, context) -> ExecutionId.generate(); + + /** * Allows provision of a unique identifier per query execution. * diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java new file mode 100644 index 0000000000..a45d40d614 --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java @@ -0,0 +1,44 @@ +package graphql.execution.instrumentation.nextgen; + +import graphql.ExecutionResult; +import graphql.Internal; +import graphql.execution.instrumentation.InstrumentationContext; +import graphql.execution.instrumentation.InstrumentationState; +import graphql.execution.instrumentation.SimpleInstrumentationContext; +import graphql.language.Document; +import graphql.nextgen.ExecutionInput; +import graphql.schema.GraphQLSchema; + +@Internal +public interface Instrumentation { + + default InstrumentationState createState(InstrumentationCreateStateParameters parameters) { + return new InstrumentationState() { + }; + } + + default ExecutionInput instrumentExecutionInput(ExecutionInput executionInput, InstrumentationExecutionParameters parameters) { + return executionInput; + } + + default GraphQLSchema instrumentSchema(GraphQLSchema graphQLSchema, InstrumentationExecutionParameters parameters) { + return graphQLSchema; + } + + default Document instrumentDocument(Document document, InstrumentationExecutionParameters parameters) { + // TODO - + return document; + } + + default ExecutionResult instrumentExecutionResult(ExecutionResult result, InstrumentationExecutionParameters parameters) { + return result; + } + + default InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters) { + return new SimpleInstrumentationContext<>(); + } + + default InstrumentationContext beginParse(InstrumentationExecutionParameters parameters) { + return new SimpleInstrumentationContext<>(); + } +} diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java new file mode 100644 index 0000000000..3ff9eb79f3 --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java @@ -0,0 +1,27 @@ +package graphql.execution.instrumentation.nextgen; + +import graphql.Internal; +import graphql.nextgen.ExecutionInput; +import graphql.schema.GraphQLSchema; + +/** + * Parameters sent to {@link graphql.execution.instrumentation.nextgen.Instrumentation} methods + */ +@Internal +public class InstrumentationCreateStateParameters { + private final GraphQLSchema schema; + private final ExecutionInput executionInput; + + public InstrumentationCreateStateParameters(GraphQLSchema schema, ExecutionInput executionInput) { + this.schema = schema; + this.executionInput = executionInput; + } + + public GraphQLSchema getSchema() { + return schema; + } + + public ExecutionInput getExecutionInput() { + return executionInput; + } +} diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java new file mode 100644 index 0000000000..57ba6d4178 --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java @@ -0,0 +1,75 @@ +package graphql.execution.instrumentation.nextgen; + +import graphql.Internal; +import graphql.execution.instrumentation.InstrumentationState; +import graphql.nextgen.ExecutionInput; +import graphql.schema.GraphQLSchema; + +import java.util.Collections; +import java.util.Map; + +/** + * Parameters sent to {@link graphql.execution.instrumentation.nextgen.Instrumentation} methods + */ +@Internal +public class InstrumentationExecutionParameters { + private final ExecutionInput executionInput; + private final String query; + private final String operation; + private final Object context; + private final Map variables; + private final InstrumentationState instrumentationState; + private final GraphQLSchema schema; + + public InstrumentationExecutionParameters(ExecutionInput executionInput, GraphQLSchema schema, InstrumentationState instrumentationState) { + this.executionInput = executionInput; + this.query = executionInput.getQuery(); + this.operation = executionInput.getOperationName(); + this.context = executionInput.getContext(); + this.variables = executionInput.getVariables() != null ? executionInput.getVariables() : Collections.emptyMap(); + this.instrumentationState = instrumentationState; + this.schema = schema; + } + + /** + * Returns a cloned parameters object with the new state + * + * @param instrumentationState the new state for this parameters object + * + * @return a new parameters object with the new state + */ + public InstrumentationExecutionParameters withNewState(InstrumentationState instrumentationState) { + return new InstrumentationExecutionParameters(this.getExecutionInput(), this.schema, instrumentationState); + } + + public ExecutionInput getExecutionInput() { + return executionInput; + } + + public String getQuery() { + return query; + } + + public String getOperation() { + return operation; + } + + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) + public T getContext() { + return (T) context; + } + + public Map getVariables() { + return variables; + } + + @SuppressWarnings("TypeParameterUnusedInFormals") + public T getInstrumentationState() { + //noinspection unchecked + return (T) instrumentationState; + } + + public GraphQLSchema getSchema() { + return this.schema; + } +} diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java new file mode 100644 index 0000000000..cf2bb9f737 --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java @@ -0,0 +1,38 @@ +package graphql.execution.instrumentation.nextgen; + +import graphql.Internal; +import graphql.execution.instrumentation.InstrumentationState; +import graphql.language.Document; +import graphql.nextgen.ExecutionInput; +import graphql.schema.GraphQLSchema; + +/** + * Parameters sent to {@link graphql.execution.instrumentation.nextgen.Instrumentation} methods + */ +@Internal +public class InstrumentationValidationParameters extends InstrumentationExecutionParameters { + private final Document document; + + public InstrumentationValidationParameters(ExecutionInput executionInput, Document document, GraphQLSchema schema, InstrumentationState instrumentationState) { + super(executionInput, schema, instrumentationState); + this.document = document; + } + + /** + * Returns a cloned parameters object with the new state + * + * @param instrumentationState the new state for this parameters object + * + * @return a new parameters object with the new state + */ + @Override + public InstrumentationValidationParameters withNewState(InstrumentationState instrumentationState) { + return new InstrumentationValidationParameters( + this.getExecutionInput(), document, getSchema(), instrumentationState); + } + + + public Document getDocument() { + return document; + } +} diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/package-info.java b/src/main/java/graphql/execution/instrumentation/nextgen/package-info.java new file mode 100644 index 0000000000..2f08b060ba --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/nextgen/package-info.java @@ -0,0 +1,4 @@ +/** + * WARNING: All code in this package is a work in progress for a new execution engine. + */ +package graphql.execution.instrumentation.nextgen; diff --git a/src/main/java/graphql/nextgen/ExecutionInput.java b/src/main/java/graphql/nextgen/ExecutionInput.java new file mode 100644 index 0000000000..85ec164c14 --- /dev/null +++ b/src/main/java/graphql/nextgen/ExecutionInput.java @@ -0,0 +1,162 @@ +package graphql.nextgen; + +import graphql.GraphQLContext; +import graphql.Internal; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +/** + * This represents the series of values that can be input on a graphql query execution + */ +@Internal +public class ExecutionInput { + private final String query; + private final String operationName; + private final Object context; + private final Object root; + private final Map variables; + + + @Internal + private ExecutionInput(Builder builder) { + this.query = builder.query; + this.operationName = builder.operationName; + this.context = builder.context; + this.root = builder.root; + this.variables = builder.variables; + } + + /** + * @return the query text + */ + public String getQuery() { + return query; + } + + /** + * @return the name of the query operation + */ + public String getOperationName() { + return operationName; + } + + /** + * @return the context object to pass to all data fetchers + */ + public Object getContext() { + return context; + } + + /** + * @return the root object to start the query execution on + */ + public Object getRoot() { + return root; + } + + /** + * @return a map of variables that can be referenced via $syntax in the query + */ + public Map getVariables() { + return variables; + } + + /** + * This helps you transform the current ExecutionInput object into another one by starting a builder with all + * the current values and allows you to transform it how you want. + * + * @param builderConsumer the consumer code that will be given a builder to transform + * + * @return a new ExecutionInput object based on calling build on that builder + */ + public ExecutionInput transform(Consumer builderConsumer) { + Builder builder = new Builder() + .query(this.query) + .operationName(this.operationName) + .context(this.context) + .root(this.root) + .variables(this.variables); + + builderConsumer.accept(builder); + + return builder.build(); + } + + + @Override + public String toString() { + return "ExecutionInput{" + + "query='" + query + '\'' + + ", operationName='" + operationName + '\'' + + ", context=" + context + + ", root=" + root + + ", variables=" + variables + + '}'; + } + + /** + * @return a new builder of ExecutionInput objects + */ + public static Builder newExecutionInput() { + return new Builder(); + } + + public static class Builder { + + private String query; + private String operationName; + private Object context = GraphQLContext.newContext(); + private Object root; + private Map variables = Collections.emptyMap(); + + public Builder query(String query) { + this.query = query; + return this; + } + + public Builder operationName(String operationName) { + this.operationName = operationName; + return this; + } + + /** + * By default you will get a {@link graphql.GraphQLContext} object but you can set your own. + * + * @param context the context object to use + * + * @return this builder + */ + public Builder context(Object context) { + this.context = context; + return this; + } + + public Builder context(GraphQLContext.Builder contextBuilder) { + this.context = contextBuilder.build(); + return this; + } + + public Builder context(UnaryOperator contextBuilderFunction) { + GraphQLContext.Builder builder = GraphQLContext.newContext(); + builder = contextBuilderFunction.apply(builder); + return context(builder.build()); + } + + public Builder root(Object root) { + this.root = root; + return this; + } + + public Builder variables(Map variables) { + this.variables = variables; + return this; + } + + public ExecutionInput build() { + return new ExecutionInput(this); + } + } +} diff --git a/src/main/java/graphql/nextgen/GraphQL.java b/src/main/java/graphql/nextgen/GraphQL.java new file mode 100644 index 0000000000..a9d9d6a62d --- /dev/null +++ b/src/main/java/graphql/nextgen/GraphQL.java @@ -0,0 +1,293 @@ +package graphql.nextgen; + +import graphql.ExecutionResult; +import graphql.ExecutionResultImpl; +import graphql.Internal; +import graphql.ParseResult; +import graphql.execution.AbortExecutionException; +import graphql.execution.ExecutionId; +import graphql.execution.ExecutionIdProvider; +import graphql.execution.instrumentation.InstrumentationContext; +import graphql.execution.instrumentation.InstrumentationState; +import graphql.execution.instrumentation.nextgen.Instrumentation; +import graphql.execution.instrumentation.nextgen.InstrumentationCreateStateParameters; +import graphql.execution.instrumentation.nextgen.InstrumentationExecutionParameters; +import graphql.execution.nextgen.DefaultExecutionStrategy; +import graphql.execution.nextgen.Execution; +import graphql.execution.nextgen.ExecutionStrategy; +import graphql.execution.preparsed.NoOpPreparsedDocumentProvider; +import graphql.execution.preparsed.PreparsedDocumentEntry; +import graphql.execution.preparsed.PreparsedDocumentProvider; +import graphql.language.Document; +import graphql.parser.Parser; +import graphql.schema.GraphQLSchema; +import graphql.validation.ValidationError; +import graphql.validation.Validator; +import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +import static graphql.Assert.assertNotNull; +import static graphql.InvalidSyntaxError.toInvalidSyntaxError; + +@Internal +public class GraphQL { + private static final Logger log = LoggerFactory.getLogger(graphql.GraphQL.class); + + private final GraphQLSchema graphQLSchema; + private final Class executionStrategy; + private final ExecutionIdProvider idProvider; + private final Instrumentation instrumentation; + private final PreparsedDocumentProvider preparsedDocumentProvider; + + public GraphQL(Builder builder) { + this.graphQLSchema = builder.graphQLSchema; + this.executionStrategy = builder.executionStrategy; + this.idProvider = builder.idProvider; + this.preparsedDocumentProvider = builder.preparsedDocumentProvider; + this.instrumentation = builder.instrumentation; + } + + /** + * Executes the graphql query using the provided input object builder + *

+ * This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} + * which is the result of executing the provided query. + * + * @param executionInputBuilder {@link ExecutionInput.Builder} + * + * @return a promise to an {@link ExecutionResult} which can include errors + */ + public CompletableFuture executeAsync(ExecutionInput.Builder executionInputBuilder) { + return executeAsync(executionInputBuilder.build()); + } + + /** + * Executes the graphql query using the provided input object builder + *

+ * This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} + * which is the result of executing the provided query. + *

+ * This allows a lambda style like : + *

+     * {@code
+     *    ExecutionResult result = graphql.execute(input -> input.query("{hello}").root(startingObj).context(contextObj));
+     * }
+     * 
+ * + * @param builderFunction a function that is given a {@link ExecutionInput.Builder} + * + * @return a promise to an {@link ExecutionResult} which can include errors + */ + public CompletableFuture executeAsync(UnaryOperator builderFunction) { + return executeAsync(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); + } + + /** + * Executes the graphql query using the provided input object + *

+ * This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} + * which is the result of executing the provided query. + * + * @param executionInput {@link ExecutionInput} + * + * @return a promise to an {@link ExecutionResult} which can include errors + */ + public CompletableFuture executeAsync(ExecutionInput executionInput) { + try { + log.debug("Executing request. operation name: '{}'. query: '{}'. variables '{}'", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); + + InstrumentationState instrumentationState = instrumentation.createState(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInput)); + + InstrumentationExecutionParameters inputInstrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState); + executionInput = instrumentation.instrumentExecutionInput(executionInput, inputInstrumentationParameters); + + InstrumentationExecutionParameters instrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState); + InstrumentationContext executionInstrumentation = instrumentation.beginExecution(instrumentationParameters); + + GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters); + + CompletableFuture executionResult = parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState); + // + // finish up instrumentation + executionResult = executionResult.whenComplete(executionInstrumentation::onCompleted); + // + // allow instrumentation to tweak the result + executionResult = executionResult.thenApply(result -> instrumentation.instrumentExecutionResult(result, instrumentationParameters)); + return executionResult; + } catch (AbortExecutionException abortException) { + return CompletableFuture.completedFuture(abortException.toExecutionResult()); + } + } + + private CompletableFuture parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { + AtomicReference executionInputRef = new AtomicReference<>(executionInput); + PreparsedDocumentEntry preparsedDoc = preparsedDocumentProvider.get(executionInput.getQuery(), + transformedQuery -> { + // if they change the original query in the pre-parser, then we want to see it downstream from then on + executionInputRef.set(executionInput.transform(bldr -> bldr.query(transformedQuery))); + return parseAndValidate(executionInputRef.get(), graphQLSchema, instrumentationState); + }); + if (preparsedDoc.hasErrors()) { + return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDoc.getErrors())); + } + + return execute(executionInputRef.get(), preparsedDoc.getDocument(), graphQLSchema, instrumentationState); + } + + private PreparsedDocumentEntry parseAndValidate(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { + log.debug("Parsing query: '{}'...", executionInput.getQuery()); + ParseResult parseResult = parse(executionInput, graphQLSchema, instrumentationState); + if (parseResult.isFailure()) { + log.warn("Query failed to parse : '{}'", executionInput.getQuery()); + return new PreparsedDocumentEntry(toInvalidSyntaxError(parseResult.getException())); + } else { + final Document document = parseResult.getDocument(); + + log.debug("Validating query: '{}'", executionInput.getQuery()); + final List errors = validate(executionInput, document, graphQLSchema, instrumentationState); + if (!errors.isEmpty()) { + log.warn("Query failed to validate : '{}'", executionInput.getQuery()); + return new PreparsedDocumentEntry(errors); + } + + return new PreparsedDocumentEntry(document); + } + } + + private ParseResult parse(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { + InstrumentationExecutionParameters parameters = new InstrumentationExecutionParameters(executionInput, graphQLSchema, instrumentationState); + InstrumentationContext parseInstrumentation = instrumentation.beginParse(parameters); + + Parser parser = new Parser(); + Document document; + try { + document = parser.parseDocument(executionInput.getQuery()); + document = instrumentation.instrumentDocument(document, parameters); + } catch (ParseCancellationException e) { + parseInstrumentation.onCompleted(null, e); + return ParseResult.ofError(e); + } + + parseInstrumentation.onCompleted(document, null); + return ParseResult.of(document); + } + + private List validate(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { + InstrumentationContext> validationCtx = instrumentation.beginValidation(new InstrumentationValidationParameters(executionInput, document, graphQLSchema, instrumentationState)); + + Validator validator = new Validator(); + List validationErrors = validator.validateDocument(graphQLSchema, document); + + validationCtx.onCompleted(validationErrors, null); + return validationErrors; + } + + private CompletableFuture execute(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { + String query = executionInput.getQuery(); + String operationName = executionInput.getOperationName(); + Object context = executionInput.getContext(); + + Execution execution = new Execution(); + ExecutionId executionId = idProvider.provide(query, operationName, context); + + log.debug("Executing '{}'. operation name: '{}'. query: '{}'. variables '{}'", executionId, executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); + CompletableFuture future = execution.execute(executionStrategy, document, graphQLSchema, executionId, executionInput); + future = future.whenComplete((result, throwable) -> { + if (throwable != null) { + log.error(String.format("Execution '%s' threw exception when executing : query : '%s'. variables '%s'", executionId, executionInput.getQuery(), executionInput.getVariables()), throwable); + } else { + int errorCount = result.getErrors().size(); + if (errorCount > 0) { + log.debug("Execution '{}' completed with '{}' errors", executionId, errorCount); + } else { + log.debug("Execution '{}' completed with zero errors", executionId); + } + } + }); + return future; + } + + /** + * Helps you build a GraphQL object ready to execute queries + * + * @param graphQLSchema the schema to use + * + * @return a builder of GraphQL objects + */ + public static Builder newGraphQL(GraphQLSchema graphQLSchema) { + return new Builder(graphQLSchema); + } + + /** + * This helps you transform the current GraphQL object into another one by starting a builder with all + * the current values and allows you to transform it how you want. + * + * @param builderConsumer the consumer code that will be given a builder to transform + * + * @return a new GraphQL object based on calling build on that builder + */ + public GraphQL transform(Consumer builderConsumer) { + Builder builder = new Builder(this); + builderConsumer.accept(builder); + return builder.build(); + } + + + public static class Builder { + private GraphQLSchema graphQLSchema; + private Class executionStrategy = DefaultExecutionStrategy.class; + private ExecutionIdProvider idProvider = ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER; + private Instrumentation instrumentation = new Instrumentation() { + }; + private PreparsedDocumentProvider preparsedDocumentProvider = NoOpPreparsedDocumentProvider.INSTANCE; + + + public Builder(GraphQLSchema graphQLSchema) { + this.graphQLSchema = graphQLSchema; + } + + public Builder(GraphQL graphQL) { + this.graphQLSchema = graphQL.graphQLSchema; + this.executionStrategy = graphQL.executionStrategy; + this.idProvider = graphQL.idProvider; + this.instrumentation = graphQL.instrumentation; + } + + public Builder schema(GraphQLSchema graphQLSchema) { + this.graphQLSchema = assertNotNull(graphQLSchema, "GraphQLSchema must be non null"); + return this; + } + + public Builder executionStrategy(Class executionStrategy) { + this.executionStrategy = assertNotNull(executionStrategy, "ExecutionStrategy must be non null"); + return this; + } + + public Builder instrumentation(Instrumentation instrumentation) { + this.instrumentation = assertNotNull(instrumentation, "Instrumentation must be non null"); + return this; + } + + public Builder preparsedDocumentProvider(PreparsedDocumentProvider preparsedDocumentProvider) { + this.preparsedDocumentProvider = assertNotNull(preparsedDocumentProvider, "PreparsedDocumentProvider must be non null"); + return this; + } + + public Builder executionIdProvider(ExecutionIdProvider executionIdProvider) { + this.idProvider = assertNotNull(executionIdProvider, "ExecutionIdProvider must be non null"); + return this; + } + + public GraphQL build() { + assertNotNull(graphQLSchema, "graphQLSchema must be non null"); + return new GraphQL(this); + } + } +} diff --git a/src/main/java/graphql/nextgen/package-info.java b/src/main/java/graphql/nextgen/package-info.java new file mode 100644 index 0000000000..8c994937fd --- /dev/null +++ b/src/main/java/graphql/nextgen/package-info.java @@ -0,0 +1,4 @@ +/** + * WARNING: All code in this package is a work in progress for a new execution engine. + */ +package graphql.nextgen; From da5da777e3a9d712c06cdb2cd2769da3b2d122bb Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Wed, 9 Jan 2019 17:53:28 +1100 Subject: [PATCH 2/6] Migrated the execution test of DEStrategy to use this new call in mechanism --- src/main/java/graphql/ParseResult.java | 19 +- .../instrumentation/Instrumentation.java | 6 +- .../SimpleInstrumentationContext.java | 11 + .../nextgen/Instrumentation.java | 24 +- .../graphql/execution/nextgen/Execution.java | 10 +- .../execution/nextgen/ExecutionHelper.java | 8 +- src/main/java/graphql/nextgen/GraphQL.java | 15 +- .../DefaultExecutionStrategyTest.groovy | 211 ++++++------------ 8 files changed, 128 insertions(+), 176 deletions(-) diff --git a/src/main/java/graphql/ParseResult.java b/src/main/java/graphql/ParseResult.java index 90f0f0ae48..fbaefec523 100644 --- a/src/main/java/graphql/ParseResult.java +++ b/src/main/java/graphql/ParseResult.java @@ -1,30 +1,37 @@ package graphql; +import graphql.execution.instrumentation.DocumentAndVariables; import graphql.language.Document; +import java.util.Map; + @Internal public class ParseResult { - private final Document document; + private final DocumentAndVariables documentAndVariables; private final Exception exception; - public ParseResult(Document document, Exception exception) { - this.document = document; + private ParseResult(DocumentAndVariables documentAndVariables, Exception exception) { + this.documentAndVariables = documentAndVariables; this.exception = exception; } public boolean isFailure() { - return document == null; + return documentAndVariables == null; } public Document getDocument() { - return document; + return documentAndVariables.getDocument(); + } + + public Map getVariables() { + return documentAndVariables.getVariables(); } public Exception getException() { return exception; } - public static ParseResult of(Document document) { + public static ParseResult of(DocumentAndVariables document) { return new ParseResult(document, null); } diff --git a/src/main/java/graphql/execution/instrumentation/Instrumentation.java b/src/main/java/graphql/execution/instrumentation/Instrumentation.java index f933a3edc0..b01d3d71cf 100644 --- a/src/main/java/graphql/execution/instrumentation/Instrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/Instrumentation.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import static graphql.execution.instrumentation.SimpleInstrumentationContext.noOp; + /** * Provides the capability to instrument the execution steps of a GraphQL query. * @@ -139,7 +141,7 @@ default InstrumentationState createState(InstrumentationCreateStateParameters pa * @return a non null {@link InstrumentationContext} object that will be called back when the step ends */ default InstrumentationContext beginFieldComplete(InstrumentationFieldCompleteParameters parameters) { - return new SimpleInstrumentationContext<>(); + return noOp(); } /** @@ -150,7 +152,7 @@ default InstrumentationContext beginFieldComplete(Instrumentati * @return a non null {@link InstrumentationContext} object that will be called back when the step ends */ default InstrumentationContext beginFieldListComplete(InstrumentationFieldCompleteParameters parameters) { - return new SimpleInstrumentationContext<>(); + return noOp(); } /** diff --git a/src/main/java/graphql/execution/instrumentation/SimpleInstrumentationContext.java b/src/main/java/graphql/execution/instrumentation/SimpleInstrumentationContext.java index a7d796f416..7ded4f674b 100644 --- a/src/main/java/graphql/execution/instrumentation/SimpleInstrumentationContext.java +++ b/src/main/java/graphql/execution/instrumentation/SimpleInstrumentationContext.java @@ -12,6 +12,17 @@ @PublicApi public class SimpleInstrumentationContext implements InstrumentationContext { + /** + * A context that does nothing + * + * @param the type needed + * + * @return a context that does nothing + */ + public static InstrumentationContext noOp() { + return new SimpleInstrumentationContext<>(); + } + private final BiConsumer codeToRunOnComplete; private final Consumer> codeToRunOnDispatch; diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java index a45d40d614..750448e25b 100644 --- a/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java @@ -2,12 +2,17 @@ import graphql.ExecutionResult; import graphql.Internal; +import graphql.execution.instrumentation.DocumentAndVariables; import graphql.execution.instrumentation.InstrumentationContext; import graphql.execution.instrumentation.InstrumentationState; -import graphql.execution.instrumentation.SimpleInstrumentationContext; import graphql.language.Document; import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; +import graphql.validation.ValidationError; + +import java.util.List; + +import static graphql.execution.instrumentation.SimpleInstrumentationContext.noOp; @Internal public interface Instrumentation { @@ -21,13 +26,12 @@ default ExecutionInput instrumentExecutionInput(ExecutionInput executionInput, I return executionInput; } - default GraphQLSchema instrumentSchema(GraphQLSchema graphQLSchema, InstrumentationExecutionParameters parameters) { - return graphQLSchema; + default DocumentAndVariables instrumentDocumentAndVariables(DocumentAndVariables documentAndVariables, InstrumentationExecutionParameters parameters) { + return documentAndVariables; } - default Document instrumentDocument(Document document, InstrumentationExecutionParameters parameters) { - // TODO - - return document; + default GraphQLSchema instrumentSchema(GraphQLSchema graphQLSchema, InstrumentationExecutionParameters parameters) { + return graphQLSchema; } default ExecutionResult instrumentExecutionResult(ExecutionResult result, InstrumentationExecutionParameters parameters) { @@ -35,10 +39,14 @@ default ExecutionResult instrumentExecutionResult(ExecutionResult result, Instru } default InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters) { - return new SimpleInstrumentationContext<>(); + return noOp(); } default InstrumentationContext beginParse(InstrumentationExecutionParameters parameters) { - return new SimpleInstrumentationContext<>(); + return noOp(); + } + + default InstrumentationContext> beginValidation(InstrumentationValidationParameters parameters) { + return noOp(); } } diff --git a/src/main/java/graphql/execution/nextgen/Execution.java b/src/main/java/graphql/execution/nextgen/Execution.java index bfe460fa77..041a1fcd24 100644 --- a/src/main/java/graphql/execution/nextgen/Execution.java +++ b/src/main/java/graphql/execution/nextgen/Execution.java @@ -1,14 +1,15 @@ package graphql.execution.nextgen; -import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.GraphQLError; import graphql.Internal; import graphql.execution.Async; import graphql.execution.ExecutionId; +import graphql.execution.instrumentation.InstrumentationState; import graphql.execution.nextgen.result.ResultNodesUtil; import graphql.language.Document; +import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; import java.util.concurrent.CompletableFuture; @@ -22,10 +23,11 @@ public CompletableFuture execute(Class execute(Class fragmentsByName = getOperationResult.fragmentsByName; @@ -52,6 +54,7 @@ public ExecutionData createExecutionData(Document document, ExecutionContext executionContext = newExecutionContextBuilder() .executionId(executionId) + .instrumentationState(instrumentationState) .graphQLSchema(graphQLSchema) .context(executionInput.getContext()) .root(executionInput.getRoot()) @@ -59,7 +62,6 @@ public ExecutionData createExecutionData(Document document, .variables(coercedVariables) .document(document) .operationDefinition(operationDefinition) - .dataLoaderRegistry(executionInput.getDataLoaderRegistry()) .build(); GraphQLObjectType operationRootType; diff --git a/src/main/java/graphql/nextgen/GraphQL.java b/src/main/java/graphql/nextgen/GraphQL.java index a9d9d6a62d..ea22dafe48 100644 --- a/src/main/java/graphql/nextgen/GraphQL.java +++ b/src/main/java/graphql/nextgen/GraphQL.java @@ -7,11 +7,13 @@ import graphql.execution.AbortExecutionException; import graphql.execution.ExecutionId; import graphql.execution.ExecutionIdProvider; +import graphql.execution.instrumentation.DocumentAndVariables; import graphql.execution.instrumentation.InstrumentationContext; import graphql.execution.instrumentation.InstrumentationState; import graphql.execution.instrumentation.nextgen.Instrumentation; import graphql.execution.instrumentation.nextgen.InstrumentationCreateStateParameters; import graphql.execution.instrumentation.nextgen.InstrumentationExecutionParameters; +import graphql.execution.instrumentation.nextgen.InstrumentationValidationParameters; import graphql.execution.nextgen.DefaultExecutionStrategy; import graphql.execution.nextgen.Execution; import graphql.execution.nextgen.ExecutionStrategy; @@ -35,7 +37,9 @@ import static graphql.Assert.assertNotNull; import static graphql.InvalidSyntaxError.toInvalidSyntaxError; +import static graphql.execution.instrumentation.DocumentAndVariables.newDocumentAndVariables; +@SuppressWarnings("Duplicates") @Internal public class GraphQL { private static final Logger log = LoggerFactory.getLogger(graphql.GraphQL.class); @@ -167,16 +171,19 @@ private ParseResult parse(ExecutionInput executionInput, GraphQLSchema graphQLSc Parser parser = new Parser(); Document document; + DocumentAndVariables documentAndVariables; try { document = parser.parseDocument(executionInput.getQuery()); - document = instrumentation.instrumentDocument(document, parameters); + documentAndVariables = newDocumentAndVariables() + .document(document).variables(executionInput.getVariables()).build(); + documentAndVariables = instrumentation.instrumentDocumentAndVariables(documentAndVariables, parameters); } catch (ParseCancellationException e) { parseInstrumentation.onCompleted(null, e); return ParseResult.ofError(e); } - parseInstrumentation.onCompleted(document, null); - return ParseResult.of(document); + parseInstrumentation.onCompleted(documentAndVariables.getDocument(), null); + return ParseResult.of(documentAndVariables); } private List validate(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { @@ -198,7 +205,7 @@ private CompletableFuture execute(ExecutionInput executionInput ExecutionId executionId = idProvider.provide(query, operationName, context); log.debug("Executing '{}'. operation name: '{}'. query: '{}'. variables '{}'", executionId, executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); - CompletableFuture future = execution.execute(executionStrategy, document, graphQLSchema, executionId, executionInput); + CompletableFuture future = execution.execute(executionStrategy, document, graphQLSchema, executionId, executionInput, instrumentationState); future = future.whenComplete((result, throwable) -> { if (throwable != null) { log.error(String.format("Execution '%s' threw exception when executing : query : '%s'. variables '%s'", executionId, executionInput.getQuery(), executionInput.getVariables()), throwable); diff --git a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy index 9f2c4d50b4..ad3d47bd7a 100644 --- a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy @@ -1,16 +1,16 @@ package graphql.execution.nextgen import graphql.ExceptionWhileDataFetching -import graphql.ExecutionInput -import graphql.TestUtil -import graphql.execution.ExecutionId +import graphql.nextgen.GraphQL import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment import spock.lang.Specification import java.util.concurrent.CompletableFuture +import static graphql.TestUtil.schema import static graphql.execution.DataFetcherResult.newResult +import static graphql.nextgen.ExecutionInput.newExecutionInput class DefaultExecutionStrategyTest extends Specification { @@ -20,7 +20,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: Foo } @@ -35,7 +35,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -43,17 +43,12 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - Execution execution = new Execution() when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] @@ -77,7 +72,7 @@ class DefaultExecutionStrategyTest extends Specification { Query: [foo: fooResolver], Foo : [id1: idResolver1, id2: idResolver2, id3: idResolver3] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: Foo } @@ -89,24 +84,21 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ { f1: foo { id1 } f2: foo { id2 } f3: foo { id3 } } - """) - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - Execution execution = new Execution(); + """ def cfId1 = new CompletableFuture() def cfId2 = new CompletableFuture() def cfId3 = new CompletableFuture() when: - execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + graphQL.executeAsync(newExecutionInput().query(query)) then: cfs.size() == 3 @@ -144,7 +136,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -159,7 +151,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -167,21 +159,14 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] - } def "test execution with null element "() { @@ -190,7 +175,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -205,7 +190,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -213,18 +198,11 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] @@ -237,7 +215,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -252,7 +230,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -260,18 +238,11 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] @@ -284,7 +255,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -299,7 +270,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -307,21 +278,15 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ def expectedFooData = [[id: "fooId1", bar: null], [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: expectedFooData] @@ -334,7 +299,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -349,7 +314,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -357,25 +322,17 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ def expectedFooData = [null, [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: expectedFooData] - } def "test execution with null element bubbling up to top "() { @@ -384,7 +341,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo!]! } @@ -399,7 +356,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -407,23 +364,14 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == null - } def "test list"() { @@ -431,7 +379,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -441,27 +389,18 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] - - } def "test list in lists "() { @@ -469,7 +408,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -482,29 +421,19 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { bar { id } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); - + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] - - } def "test simple batching with null value in list"() { @@ -512,7 +441,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -522,27 +451,18 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.getData() == [foo: fooData] - - } @@ -565,7 +485,7 @@ class DefaultExecutionStrategyTest extends Specification { } as DataFetcher ] ] - def schema = TestUtil.schema(''' + def schema = schema(''' type Query { foo: [Foo] } @@ -575,21 +495,17 @@ class DefaultExecutionStrategyTest extends Specification { ''', dataFetchers) - def document = TestUtil.parseQuery(''' + def query = ''' { foo { id } } - ''') - - ExecutionInput executionInput = ExecutionInput.newExecutionInput().build() - Execution execution = new Execution() + ''' when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.executeAsync(newExecutionInput().query(query)).get() then: result.errors.size() == 3 @@ -600,6 +516,5 @@ class DefaultExecutionStrategyTest extends Specification { def rte = new RuntimeException("Bang on " + env.getField().getName()) new ExceptionWhileDataFetching(env.executionStepInfo.getPath(), rte, env.getField().sourceLocation) } - } From bef881b366abfa8bb39c54eda899c72ee8222591 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Thu, 10 Jan 2019 15:06:39 +1100 Subject: [PATCH 3/6] Removed nextgen.ExecutionInput --- src/main/java/graphql/ExecutionInput.java | 11 + .../nextgen/Instrumentation.java | 2 +- .../InstrumentationCreateStateParameters.java | 2 +- .../InstrumentationExecutionParameters.java | 2 +- .../InstrumentationValidationParameters.java | 2 +- .../graphql/execution/nextgen/Execution.java | 2 +- .../execution/nextgen/ExecutionHelper.java | 2 +- .../java/graphql/nextgen/ExecutionInput.java | 162 --------------- src/main/java/graphql/nextgen/GraphQL.java | 1 + .../groovy/graphql/ExecutionInputTest.groovy | 7 + .../BatchedExecutionStrategyTest.groovy | 195 +++++------------- .../DefaultExecutionStrategyTest.groovy | 2 +- .../graphql/nextgen/GraphqlNextGenTest.groovy | 32 +++ 13 files changed, 113 insertions(+), 309 deletions(-) delete mode 100644 src/main/java/graphql/nextgen/ExecutionInput.java create mode 100644 src/test/groovy/graphql/nextgen/GraphqlNextGenTest.groovy diff --git a/src/main/java/graphql/ExecutionInput.java b/src/main/java/graphql/ExecutionInput.java index 6c945d9404..917d4a519a 100644 --- a/src/main/java/graphql/ExecutionInput.java +++ b/src/main/java/graphql/ExecutionInput.java @@ -120,6 +120,17 @@ public static Builder newExecutionInput() { return new Builder(); } + /** + * Creates a new builder of ExecutionInput objects with the given query + * + * @param query the query to execute + * + * @return a new builder of ExecutionInput objects + */ + public static Builder newExecutionInput(String query) { + return new Builder().query(query); + } + public static class Builder { private String query; diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java index 750448e25b..3e59616284 100644 --- a/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/nextgen/Instrumentation.java @@ -1,12 +1,12 @@ package graphql.execution.instrumentation.nextgen; +import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.Internal; import graphql.execution.instrumentation.DocumentAndVariables; import graphql.execution.instrumentation.InstrumentationContext; import graphql.execution.instrumentation.InstrumentationState; import graphql.language.Document; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; import graphql.validation.ValidationError; diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java index 3ff9eb79f3..ff4ad52cdc 100644 --- a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationCreateStateParameters.java @@ -1,7 +1,7 @@ package graphql.execution.instrumentation.nextgen; +import graphql.ExecutionInput; import graphql.Internal; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; /** diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java index 57ba6d4178..89c368c761 100644 --- a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationExecutionParameters.java @@ -1,8 +1,8 @@ package graphql.execution.instrumentation.nextgen; +import graphql.ExecutionInput; import graphql.Internal; import graphql.execution.instrumentation.InstrumentationState; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; import java.util.Collections; diff --git a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java index cf2bb9f737..ebaa26d3b2 100644 --- a/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java +++ b/src/main/java/graphql/execution/instrumentation/nextgen/InstrumentationValidationParameters.java @@ -1,9 +1,9 @@ package graphql.execution.instrumentation.nextgen; +import graphql.ExecutionInput; import graphql.Internal; import graphql.execution.instrumentation.InstrumentationState; import graphql.language.Document; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; /** diff --git a/src/main/java/graphql/execution/nextgen/Execution.java b/src/main/java/graphql/execution/nextgen/Execution.java index 041a1fcd24..e39a16a046 100644 --- a/src/main/java/graphql/execution/nextgen/Execution.java +++ b/src/main/java/graphql/execution/nextgen/Execution.java @@ -1,5 +1,6 @@ package graphql.execution.nextgen; +import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.GraphQLError; @@ -9,7 +10,6 @@ import graphql.execution.instrumentation.InstrumentationState; import graphql.execution.nextgen.result.ResultNodesUtil; import graphql.language.Document; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLSchema; import java.util.concurrent.CompletableFuture; diff --git a/src/main/java/graphql/execution/nextgen/ExecutionHelper.java b/src/main/java/graphql/execution/nextgen/ExecutionHelper.java index 58f807ead4..1b8ee579ef 100644 --- a/src/main/java/graphql/execution/nextgen/ExecutionHelper.java +++ b/src/main/java/graphql/execution/nextgen/ExecutionHelper.java @@ -1,5 +1,6 @@ package graphql.execution.nextgen; +import graphql.ExecutionInput; import graphql.Internal; import graphql.execution.ExecutionContext; import graphql.execution.ExecutionId; @@ -15,7 +16,6 @@ import graphql.language.NodeUtil; import graphql.language.OperationDefinition; import graphql.language.VariableDefinition; -import graphql.nextgen.ExecutionInput; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; diff --git a/src/main/java/graphql/nextgen/ExecutionInput.java b/src/main/java/graphql/nextgen/ExecutionInput.java deleted file mode 100644 index 85ec164c14..0000000000 --- a/src/main/java/graphql/nextgen/ExecutionInput.java +++ /dev/null @@ -1,162 +0,0 @@ -package graphql.nextgen; - -import graphql.GraphQLContext; -import graphql.Internal; - -import java.util.Collections; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.UnaryOperator; - -/** - * This represents the series of values that can be input on a graphql query execution - */ -@Internal -public class ExecutionInput { - private final String query; - private final String operationName; - private final Object context; - private final Object root; - private final Map variables; - - - @Internal - private ExecutionInput(Builder builder) { - this.query = builder.query; - this.operationName = builder.operationName; - this.context = builder.context; - this.root = builder.root; - this.variables = builder.variables; - } - - /** - * @return the query text - */ - public String getQuery() { - return query; - } - - /** - * @return the name of the query operation - */ - public String getOperationName() { - return operationName; - } - - /** - * @return the context object to pass to all data fetchers - */ - public Object getContext() { - return context; - } - - /** - * @return the root object to start the query execution on - */ - public Object getRoot() { - return root; - } - - /** - * @return a map of variables that can be referenced via $syntax in the query - */ - public Map getVariables() { - return variables; - } - - /** - * This helps you transform the current ExecutionInput object into another one by starting a builder with all - * the current values and allows you to transform it how you want. - * - * @param builderConsumer the consumer code that will be given a builder to transform - * - * @return a new ExecutionInput object based on calling build on that builder - */ - public ExecutionInput transform(Consumer builderConsumer) { - Builder builder = new Builder() - .query(this.query) - .operationName(this.operationName) - .context(this.context) - .root(this.root) - .variables(this.variables); - - builderConsumer.accept(builder); - - return builder.build(); - } - - - @Override - public String toString() { - return "ExecutionInput{" + - "query='" + query + '\'' + - ", operationName='" + operationName + '\'' + - ", context=" + context + - ", root=" + root + - ", variables=" + variables + - '}'; - } - - /** - * @return a new builder of ExecutionInput objects - */ - public static Builder newExecutionInput() { - return new Builder(); - } - - public static class Builder { - - private String query; - private String operationName; - private Object context = GraphQLContext.newContext(); - private Object root; - private Map variables = Collections.emptyMap(); - - public Builder query(String query) { - this.query = query; - return this; - } - - public Builder operationName(String operationName) { - this.operationName = operationName; - return this; - } - - /** - * By default you will get a {@link graphql.GraphQLContext} object but you can set your own. - * - * @param context the context object to use - * - * @return this builder - */ - public Builder context(Object context) { - this.context = context; - return this; - } - - public Builder context(GraphQLContext.Builder contextBuilder) { - this.context = contextBuilder.build(); - return this; - } - - public Builder context(UnaryOperator contextBuilderFunction) { - GraphQLContext.Builder builder = GraphQLContext.newContext(); - builder = contextBuilderFunction.apply(builder); - return context(builder.build()); - } - - public Builder root(Object root) { - this.root = root; - return this; - } - - public Builder variables(Map variables) { - this.variables = variables; - return this; - } - - public ExecutionInput build() { - return new ExecutionInput(this); - } - } -} diff --git a/src/main/java/graphql/nextgen/GraphQL.java b/src/main/java/graphql/nextgen/GraphQL.java index ea22dafe48..c7328eb5dd 100644 --- a/src/main/java/graphql/nextgen/GraphQL.java +++ b/src/main/java/graphql/nextgen/GraphQL.java @@ -1,5 +1,6 @@ package graphql.nextgen; +import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.Internal; diff --git a/src/test/groovy/graphql/ExecutionInputTest.groovy b/src/test/groovy/graphql/ExecutionInputTest.groovy index 31c7f38718..f9340cf140 100644 --- a/src/test/groovy/graphql/ExecutionInputTest.groovy +++ b/src/test/groovy/graphql/ExecutionInputTest.groovy @@ -62,4 +62,11 @@ class ExecutionInputTest extends Specification { executionInput.dataLoaderRegistry == registry executionInput.query == "new query" } + + def "defaults query into builder as expected"() { + when: + def executionInput = ExecutionInput.newExecutionInput("{ q }").build() + then: + executionInput.query == "{ q }" + } } diff --git a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy index 5fc59a806a..851e047177 100644 --- a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy @@ -1,20 +1,21 @@ package graphql.execution.nextgen -import graphql.ExecutionInput -import graphql.TestUtil -import graphql.execution.ExecutionId + +import graphql.nextgen.GraphQL import graphql.schema.DataFetcher import spock.lang.Specification -class BatchedExecutionStrategyTest extends Specification { +import static graphql.ExecutionInput.newExecutionInput +import static graphql.TestUtil.schema +class BatchedExecutionStrategyTest extends Specification { def "test simple execution"() { def fooData = [id: "fooId", bar: [id: "barId", name: "someBar"]] def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: Foo } @@ -29,7 +30,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -37,22 +38,15 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() + """ - Execution execution = new Execution() when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: fooData] - - } def "test execution with lists"() { @@ -61,7 +55,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -76,7 +70,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -84,22 +78,14 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: fooData] - } def "test execution with null element "() { @@ -108,7 +94,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -123,7 +109,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -131,17 +117,10 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -155,7 +134,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -170,7 +149,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -178,22 +157,13 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: fooData] - } def "test execution with null element in non null list"() { @@ -202,7 +172,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -217,7 +187,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -225,25 +195,16 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - + """ def expectedFooData = [[id: "fooId1", bar: null], [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: expectedFooData] - } def "test execution with null element bubbling up because of non null "() { @@ -252,7 +213,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -267,7 +228,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -275,25 +236,17 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) + """ def expectedFooData = [null, [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: expectedFooData] - } def "test execution with null element bubbling up to top "() { @@ -302,7 +255,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo!]! } @@ -317,7 +270,7 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -325,23 +278,13 @@ class BatchedExecutionStrategyTest extends Specification { name } }} - """) - - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution() - + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == null - } def "test list"() { @@ -349,7 +292,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -359,27 +302,18 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: fooData] - - } def "test list in lists "() { @@ -406,7 +340,7 @@ class BatchedExecutionStrategyTest extends Specification { Person: [cats: catsDataFetcher], Cat : [id: idDataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { friends: [Person] } @@ -419,24 +353,17 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {friends { cats { id } }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [friends: [[cats: [[id: "catId1"], [id: "catId2"]]], [cats: null], [cats: [[id: "catId3"], [id: "catId4"], [id: "catId5"]]]]] @@ -444,8 +371,6 @@ class BatchedExecutionStrategyTest extends Specification { idsCallCount == 1 catsBatchSize == 3 idsBatchSize == 5 - - } def "test simple batching with null value in list"() { @@ -453,7 +378,7 @@ class BatchedExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -463,28 +388,18 @@ class BatchedExecutionStrategyTest extends Specification { """, dataFetchers) - def document = graphql.TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = ExecutionInput.newExecutionInput() - .build() - - - Execution execution = new Execution(); + """ when: - def monoResult = execution.execute(BatchedExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def result = graphQL.executeAsync(newExecutionInput(query)).get() then: result.getData() == [foo: fooData] - - } - } diff --git a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy index ad3d47bd7a..7fe82b4563 100644 --- a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy @@ -8,9 +8,9 @@ import spock.lang.Specification import java.util.concurrent.CompletableFuture +import static graphql.ExecutionInput.newExecutionInput import static graphql.TestUtil.schema import static graphql.execution.DataFetcherResult.newResult -import static graphql.nextgen.ExecutionInput.newExecutionInput class DefaultExecutionStrategyTest extends Specification { diff --git a/src/test/groovy/graphql/nextgen/GraphqlNextGenTest.groovy b/src/test/groovy/graphql/nextgen/GraphqlNextGenTest.groovy new file mode 100644 index 0000000000..5b59e6ca9c --- /dev/null +++ b/src/test/groovy/graphql/nextgen/GraphqlNextGenTest.groovy @@ -0,0 +1,32 @@ +package graphql.nextgen + + +import graphql.schema.DataFetcher +import spock.lang.Specification + +import static graphql.ExecutionInput.newExecutionInput +import static graphql.TestUtil.schema + +class GraphqlNextGenTest extends Specification { + + def "simple query"() { + given: + def dataFetchers = [ + Query: [hello: { env -> "world" } as DataFetcher] + ] + + def schema = schema(''' + type Query { + hello : String! + } + ''', dataFetchers) + + def graphQL = GraphQL.newGraphQL(schema).build() + + when: + def result = graphQL.executeAsync(newExecutionInput('{ hello }')).get() + + then: + result.data == [hello: 'world'] + } +} From fb5f20fa07ec695885e865aa009e7db66b7dffc9 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Thu, 10 Jan 2019 15:58:18 +1100 Subject: [PATCH 4/6] Now uses an instatiation of the ES as input --- .../graphql/execution/nextgen/Execution.java | 4 +- src/main/java/graphql/nextgen/GraphQL.java | 6 +- .../BatchedExecutionStrategyTest.groovy | 24 +- .../DefaultExecutionStrategyTest.groovy | 520 ------------------ 4 files changed, 16 insertions(+), 538 deletions(-) delete mode 100644 src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy diff --git a/src/main/java/graphql/execution/nextgen/Execution.java b/src/main/java/graphql/execution/nextgen/Execution.java index e39a16a046..38a23a41ed 100644 --- a/src/main/java/graphql/execution/nextgen/Execution.java +++ b/src/main/java/graphql/execution/nextgen/Execution.java @@ -19,7 +19,7 @@ public class Execution { ExecutionHelper executionHelper = new ExecutionHelper(); - public CompletableFuture execute(Class executionStrategy, + public CompletableFuture execute(ExecutionStrategy executionStrategy, Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, @@ -37,8 +37,6 @@ public CompletableFuture execute(Class executionStrategy; + private final ExecutionStrategy executionStrategy; private final ExecutionIdProvider idProvider; private final Instrumentation instrumentation; private final PreparsedDocumentProvider preparsedDocumentProvider; @@ -250,7 +250,7 @@ public GraphQL transform(Consumer builderConsumer) { public static class Builder { private GraphQLSchema graphQLSchema; - private Class executionStrategy = DefaultExecutionStrategy.class; + private ExecutionStrategy executionStrategy = new DefaultExecutionStrategy(); private ExecutionIdProvider idProvider = ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER; private Instrumentation instrumentation = new Instrumentation() { }; @@ -273,7 +273,7 @@ public Builder schema(GraphQLSchema graphQLSchema) { return this; } - public Builder executionStrategy(Class executionStrategy) { + public Builder executionStrategy(ExecutionStrategy executionStrategy) { this.executionStrategy = assertNotNull(executionStrategy, "ExecutionStrategy must be non null"); return this; } diff --git a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy index 851e047177..1640b5c6de 100644 --- a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy @@ -42,7 +42,7 @@ class BatchedExecutionStrategyTest extends Specification { when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -81,7 +81,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -119,7 +119,7 @@ class BatchedExecutionStrategyTest extends Specification { }} """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() @@ -159,7 +159,7 @@ class BatchedExecutionStrategyTest extends Specification { }} """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -200,7 +200,7 @@ class BatchedExecutionStrategyTest extends Specification { [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -242,7 +242,7 @@ class BatchedExecutionStrategyTest extends Specification { [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -280,7 +280,7 @@ class BatchedExecutionStrategyTest extends Specification { }} """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -309,7 +309,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -317,9 +317,9 @@ class BatchedExecutionStrategyTest extends Specification { } def "test list in lists "() { - def catsBatchSize + def catsBatchSize = 0 def catsCallCount = 0 - def idsBatchSize + def idsBatchSize = 0 def idsCallCount = 0 def catsDataFetcher = { env -> @@ -362,7 +362,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: @@ -395,7 +395,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(BatchedExecutionStrategy).build() + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() def result = graphQL.executeAsync(newExecutionInput(query)).get() then: diff --git a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy deleted file mode 100644 index 7fe82b4563..0000000000 --- a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy +++ /dev/null @@ -1,520 +0,0 @@ -package graphql.execution.nextgen - -import graphql.ExceptionWhileDataFetching -import graphql.nextgen.GraphQL -import graphql.schema.DataFetcher -import graphql.schema.DataFetchingEnvironment -import spock.lang.Specification - -import java.util.concurrent.CompletableFuture - -import static graphql.ExecutionInput.newExecutionInput -import static graphql.TestUtil.schema -import static graphql.execution.DataFetcherResult.newResult - -class DefaultExecutionStrategyTest extends Specification { - - - def "test simple execution"() { - def fooData = [id: "fooId", bar: [id: "barId", name: "someBar"]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: Foo - } - type Foo { - id: ID - bar: Bar - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - - } - - def "fields are resolved in depth in parallel"() { - - List cfs = [] - - def fooResolver = { env -> - println env.getField() - def result = new CompletableFuture() - cfs << result - result - } as DataFetcher - def idResolver1 = Mock(DataFetcher) - def idResolver2 = Mock(DataFetcher) - def idResolver3 = Mock(DataFetcher) - def dataFetchers = [ - Query: [foo: fooResolver], - Foo : [id1: idResolver1, id2: idResolver2, id3: idResolver3] - ] - def schema = schema(""" - type Query { - foo: Foo - } - type Foo { - id1: ID - id2: ID - id3: ID - } - """, dataFetchers) - - - def query = """ - { - f1: foo { id1 } - f2: foo { id2 } - f3: foo { id3 } - } - """ - - def cfId1 = new CompletableFuture() - def cfId2 = new CompletableFuture() - def cfId3 = new CompletableFuture() - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - graphQL.executeAsync(newExecutionInput().query(query)) - - then: - cfs.size() == 3 - 0 * idResolver1.get(_) - 0 * idResolver1.get(_) - 0 * idResolver1.get(_) - - when: - cfs[1].complete(new Object()) - then: - 0 * idResolver1.get(_) - 1 * idResolver2.get(_) >> cfId2 - 0 * idResolver3.get(_) - - when: - cfs[2].complete(new Object()) - then: - 0 * idResolver1.get(_) - 0 * idResolver2.get(_) - 1 * idResolver3.get(_) >> cfId3 - - - when: - cfs[0].complete(new Object()) - then: - 1 * idResolver1.get(_) >> cfId1 - 0 * idResolver2.get(_) - 0 * idResolver3.get(_) - - } - - def "test execution with lists"() { - def fooData = [[id: "fooId1", bar: [[id: "barId1", name: "someBar1"], [id: "barId2", name: "someBar2"]]], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - bar: [Bar] - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - } - - def "test execution with null element "() { - def fooData = [[id: "fooId1", bar: null], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - bar: [Bar] - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - - } - - def "test execution with null element in list"() { - def fooData = [[id: "fooId1", bar: [[id: "barId1", name: "someBar1"], null]], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - bar: [Bar] - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - - } - - def "test execution with null element in non null list"() { - def fooData = [[id: "fooId1", bar: [[id: "barId1", name: "someBar1"], null]], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - bar: [Bar!] - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - def expectedFooData = [[id: "fooId1", bar: null], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - - - when: - def graphQL = GraphQL.newGraphQL(schema).executionStrategy(DefaultExecutionStrategy).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: expectedFooData] - - } - - def "test execution with null element bubbling up because of non null "() { - def fooData = [[id: "fooId1", bar: [[id: "barId1", name: "someBar1"], null]], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - bar: [Bar!]! - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - def expectedFooData = [null, - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: expectedFooData] - } - - def "test execution with null element bubbling up to top "() { - def fooData = [[id: "fooId1", bar: [[id: "barId1", name: "someBar1"], null]], - [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo!]! - } - type Foo { - id: ID - bar: [Bar!]! - } - type Bar { - id: ID - name: String - } - """, dataFetchers) - - - def query = """ - {foo { - id - bar { - id - name - } - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == null - } - - def "test list"() { - def fooData = [[id: "fooId1"], [id: "fooId2"], [id: "fooId3"]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - } - """, dataFetchers) - - - def query = """ - {foo { - id - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - } - - def "test list in lists "() { - def fooData = [[bar: [[id: "barId1"], [id: "barId2"]]], [bar: null], [bar: [[id: "barId3"], [id: "barId4"], [id: "barId5"]]]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - bar: [Bar] - } - type Bar { - id: ID - } - """, dataFetchers) - - - def query = """ - {foo { - bar { - id - } - }} - """ - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - } - - def "test simple batching with null value in list"() { - def fooData = [[id: "fooId1"], null, [id: "fooId3"]] - def dataFetchers = [ - Query: [foo: { env -> fooData } as DataFetcher] - ] - def schema = schema(""" - type Query { - foo: [Foo] - } - type Foo { - id: ID - } - """, dataFetchers) - - - def query = """ - {foo { - id - }} - """ - - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.getData() == [foo: fooData] - } - - - def "DataFetcherResult is respected with errors"() { - - def fooData = [[id: "fooId1"], null, [id: "fooId3"]] - def dataFetchers = [ - Query: [ - foo: { env -> - newResult().data(fooData) - .error(mkError(env)) - .build() - } as DataFetcher], - Foo : [ - id: { env -> - def id = env.source[env.getField().getName()] - newResult().data(id) - .error(mkError(env)) - .build() - } as DataFetcher - ] - ] - def schema = schema(''' - type Query { - foo: [Foo] - } - type Foo { - id: ID - } - ''', dataFetchers) - - - def query = ''' - { - foo { - id - } - } - ''' - - when: - def graphQL = GraphQL.newGraphQL(schema).build() - def result = graphQL.executeAsync(newExecutionInput().query(query)).get() - - then: - result.errors.size() == 3 - result.data == [foo: fooData] - } - - private ExceptionWhileDataFetching mkError(DataFetchingEnvironment env) { - def rte = new RuntimeException("Bang on " + env.getField().getName()) - new ExceptionWhileDataFetching(env.executionStepInfo.getPath(), rte, env.getField().sourceLocation) - } -} - From 9522e616f0e0561c0264a6994ab3284d9575bd16 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Fri, 11 Jan 2019 10:47:16 +1100 Subject: [PATCH 5/6] DefaultES test was deleted by accident - restored --- src/main/java/graphql/nextgen/GraphQL.java | 51 ++- .../BatchedExecutionStrategyTest.groovy | 29 +- .../DefaultExecutionStrategyTest.groovy | 292 ++++-------------- 3 files changed, 125 insertions(+), 247 deletions(-) diff --git a/src/main/java/graphql/nextgen/GraphQL.java b/src/main/java/graphql/nextgen/GraphQL.java index b6b14a7f8d..64f4e523d2 100644 --- a/src/main/java/graphql/nextgen/GraphQL.java +++ b/src/main/java/graphql/nextgen/GraphQL.java @@ -59,6 +59,55 @@ public GraphQL(Builder builder) { this.instrumentation = builder.instrumentation; } + /** + * Executes the graphql query using the provided input object builder + *

+ * This will return a completed {@link ExecutionResult} + * which is the result of executing the provided query. + * + * @param executionInputBuilder {@link ExecutionInput.Builder} + * + * @return an {@link ExecutionResult} which can include errors + */ + public ExecutionResult execute(ExecutionInput.Builder executionInputBuilder) { + return executeAsync(executionInputBuilder.build()).join(); + } + + /** + * Executes the graphql query using the provided input object builder + *

+ * This will return a completed {@link ExecutionResult} + * which is the result of executing the provided query. + *

+ * This allows a lambda style like : + *

+     * {@code
+     *    ExecutionResult result = graphql.execute(input -> input.query("{hello}").root(startingObj).context(contextObj));
+     * }
+     * 
+ * + * @param builderFunction a function that is given a {@link ExecutionInput.Builder} + * + * @return a promise to an {@link ExecutionResult} which can include errors + */ + public CompletableFuture execute(UnaryOperator builderFunction) { + return executeAsync(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); + } + + /** + * Executes the graphql query using the provided input object + *

+ * This will return a completed {@link ExecutionResult} + * which is the result of executing the provided query. + * + * @param executionInput {@link ExecutionInput} + * + * @return a promise to an {@link ExecutionResult} which can include errors + */ + public ExecutionResult execute(ExecutionInput executionInput) { + return executeAsync(executionInput).join(); + } + /** * Executes the graphql query using the provided input object builder *

@@ -82,7 +131,7 @@ public CompletableFuture executeAsync(ExecutionInput.Builder ex * This allows a lambda style like : *

      * {@code
-     *    ExecutionResult result = graphql.execute(input -> input.query("{hello}").root(startingObj).context(contextObj));
+     *    ExecutionResult result = graphql.executeAsync(input -> input.query("{hello}").root(startingObj).context(contextObj));
      * }
      * 
* diff --git a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy index 1640b5c6de..6ba86d013a 100644 --- a/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/BatchedExecutionStrategyTest.groovy @@ -43,8 +43,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] } @@ -82,8 +81,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] } @@ -120,8 +118,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] @@ -160,8 +157,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] } @@ -201,8 +197,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: expectedFooData] } @@ -243,8 +238,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: expectedFooData] } @@ -281,8 +275,7 @@ class BatchedExecutionStrategyTest extends Specification { """ when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == null } @@ -310,8 +303,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] } @@ -363,8 +355,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() - + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [friends: [[cats: [[id: "catId1"], [id: "catId2"]]], [cats: null], [cats: [[id: "catId3"], [id: "catId4"], [id: "catId5"]]]]] catsCallCount == 1 @@ -396,7 +387,7 @@ class BatchedExecutionStrategyTest extends Specification { when: def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new BatchedExecutionStrategy()).build() - def result = graphQL.executeAsync(newExecutionInput(query)).get() + def result = graphQL.execute(newExecutionInput(query)) then: result.getData() == [foo: fooData] diff --git a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy index f26b33a641..dea91a908b 100644 --- a/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy +++ b/src/test/groovy/graphql/execution/nextgen/DefaultExecutionStrategyTest.groovy @@ -1,18 +1,15 @@ package graphql.execution.nextgen -import graphql.ContextPassingDataFetcher import graphql.ExceptionWhileDataFetching -import graphql.ExecutionInput -import graphql.TestUtil -import graphql.execution.ExecutionId +import graphql.nextgen.GraphQL import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment -import graphql.schema.idl.RuntimeWiring import spock.lang.Specification import java.util.concurrent.CompletableFuture import static graphql.ExecutionInput.newExecutionInput +import static graphql.TestUtil.schema import static graphql.execution.DataFetcherResult.newResult class DefaultExecutionStrategyTest extends Specification { @@ -23,7 +20,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: Foo } @@ -38,7 +35,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -46,17 +43,12 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ - ExecutionInput executionInput = newExecutionInput() - .build() - - Execution execution = new Execution() when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] @@ -81,7 +73,7 @@ class DefaultExecutionStrategyTest extends Specification { Query: [foo: fooResolver], Foo : [id1: idResolver1, id2: idResolver2, id3: idResolver3] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: Foo } @@ -93,24 +85,24 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ { f1: foo { id1 } f2: foo { id2 } f3: foo { id3 } } - """) - ExecutionInput executionInput = newExecutionInput() - .build() - - Execution execution = new Execution() + """ def cfId1 = new CompletableFuture() def cfId2 = new CompletableFuture() def cfId3 = new CompletableFuture() when: - execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + // + // I think this is a dangerous test - It dispatches the query but never joins on the result + // so its expecting the DFs to be called by never resolved properly. ?? + graphQL.executeAsync(newExecutionInput().query(query)) then: cfs.size() == 3 @@ -148,7 +140,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -163,7 +155,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -171,21 +163,14 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] - } def "test execution with null element "() { @@ -194,7 +179,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -209,7 +194,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -217,18 +202,11 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] @@ -241,7 +219,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -256,7 +234,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -264,18 +242,11 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] @@ -288,7 +259,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -303,7 +274,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -311,21 +282,15 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ def expectedFooData = [[id: "fooId1", bar: null], [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).executionStrategy(new DefaultExecutionStrategy()).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: expectedFooData] @@ -338,7 +303,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -353,7 +318,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -361,25 +326,17 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) + """ def expectedFooData = [null, [id: "fooId2", bar: [[id: "barId3", name: "someBar3"], [id: "barId4", name: "someBar4"]]]] - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() - when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: expectedFooData] - } def "test execution with null element bubbling up to top "() { @@ -388,7 +345,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo!]! } @@ -403,7 +360,7 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id bar { @@ -411,23 +368,14 @@ class DefaultExecutionStrategyTest extends Specification { name } }} - """) - - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == null - } def "test list"() { @@ -435,7 +383,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -445,27 +393,18 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] - - } def "test list in lists "() { @@ -473,7 +412,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -486,29 +425,19 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { bar { id } }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() - + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] - - } def "test simple batching with null value in list"() { @@ -516,7 +445,7 @@ class DefaultExecutionStrategyTest extends Specification { def dataFetchers = [ Query: [foo: { env -> fooData } as DataFetcher] ] - def schema = TestUtil.schema(""" + def schema = schema(""" type Query { foo: [Foo] } @@ -526,107 +455,20 @@ class DefaultExecutionStrategyTest extends Specification { """, dataFetchers) - def document = TestUtil.parseQuery(""" + def query = """ {foo { id }} - """) - - ExecutionInput executionInput = newExecutionInput() - .build() - - - Execution execution = new Execution() + """ when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.getData() == [foo: fooData] } - def "data fetcher can return context down each level"() { - given: - - def spec = ''' - type Query { - first : Level1 - } - - type Level1 { - second : Level2 - } - - type Level2 { - third : Level3 - } - - type Level3 { - skip : Level4 - } - - type Level4 { - fourth : String - } - ''' - - - def runtimeWiring = RuntimeWiring.newRuntimeWiring() - .type("Query", - { type -> - type.dataFetcher("first", new ContextPassingDataFetcher()) - }) - .type("Level1", - { type -> - type.dataFetcher("second", new ContextPassingDataFetcher()) - }) - .type("Level2", - { type -> - type.dataFetcher("third", new ContextPassingDataFetcher()) - }) - .type("Level3", - { type -> - type.dataFetcher("skip", new ContextPassingDataFetcher(true)) - }) - .type("Level4", - { type -> - type.dataFetcher("fourth", new ContextPassingDataFetcher()) - }) - .build() - - def query = ''' - { - first { - second { - third { - skip { - fourth - } - } - } - } - } - ''' - - def schema = TestUtil.schema(spec, runtimeWiring) - def executionInput = newExecutionInput().query(query).root("").context(1).build() - def document = TestUtil.parseQuery(query) - - Execution execution = new Execution() - - when: - - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - - - then: - - result.errors.isEmpty() - result.data == [first: [second: [third: [skip: [fourth: "1,2,3,4,4,"]]]]] - } def "DataFetcherResult is respected with errors"() { @@ -647,7 +489,7 @@ class DefaultExecutionStrategyTest extends Specification { } as DataFetcher ] ] - def schema = TestUtil.schema(''' + def schema = schema(''' type Query { foo: [Foo] } @@ -657,21 +499,17 @@ class DefaultExecutionStrategyTest extends Specification { ''', dataFetchers) - def document = TestUtil.parseQuery(''' + def query = ''' { foo { id } } - ''') - - ExecutionInput executionInput = newExecutionInput().build() - Execution execution = new Execution() + ''' when: - def monoResult = execution.execute(DefaultExecutionStrategy, document, schema, ExecutionId.generate(), executionInput) - def result = monoResult.get() - + def graphQL = GraphQL.newGraphQL(schema).build() + def result = graphQL.execute(newExecutionInput().query(query)) then: result.errors.size() == 3 From 5c7573401a995e6faada712855c242dbc489c8af Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Tue, 22 Jan 2019 11:51:50 +1100 Subject: [PATCH 6/6] Make it work with the new syntax errors --- src/main/java/graphql/GraphQL.java | 1 + src/main/java/graphql/ParseResult.java | 17 +++++++++-------- src/main/java/graphql/nextgen/GraphQL.java | 7 +++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index db93fa059f..c3b84a6dd9 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -80,6 +80,7 @@ * * */ +@SuppressWarnings("Duplicates") @PublicApi public class GraphQL { diff --git a/src/main/java/graphql/ParseResult.java b/src/main/java/graphql/ParseResult.java index fbaefec523..eeec599a7c 100644 --- a/src/main/java/graphql/ParseResult.java +++ b/src/main/java/graphql/ParseResult.java @@ -2,40 +2,41 @@ import graphql.execution.instrumentation.DocumentAndVariables; import graphql.language.Document; +import graphql.parser.InvalidSyntaxException; import java.util.Map; @Internal public class ParseResult { private final DocumentAndVariables documentAndVariables; - private final Exception exception; + private final InvalidSyntaxException exception; - private ParseResult(DocumentAndVariables documentAndVariables, Exception exception) { + public ParseResult(DocumentAndVariables documentAndVariables, InvalidSyntaxException exception) { this.documentAndVariables = documentAndVariables; this.exception = exception; } - public boolean isFailure() { + public boolean isFailure() { return documentAndVariables == null; } - public Document getDocument() { + public Document getDocument() { return documentAndVariables.getDocument(); } - public Map getVariables() { + public Map getVariables() { return documentAndVariables.getVariables(); } - public Exception getException() { + public InvalidSyntaxException getException() { return exception; } - public static ParseResult of(DocumentAndVariables document) { + public static ParseResult of(DocumentAndVariables document) { return new ParseResult(document, null); } - public static ParseResult ofError(Exception e) { + public static ParseResult ofError(InvalidSyntaxException e) { return new ParseResult(null, e); } } diff --git a/src/main/java/graphql/nextgen/GraphQL.java b/src/main/java/graphql/nextgen/GraphQL.java index 64f4e523d2..b834d3f5af 100644 --- a/src/main/java/graphql/nextgen/GraphQL.java +++ b/src/main/java/graphql/nextgen/GraphQL.java @@ -22,11 +22,11 @@ import graphql.execution.preparsed.PreparsedDocumentEntry; import graphql.execution.preparsed.PreparsedDocumentProvider; import graphql.language.Document; +import graphql.parser.InvalidSyntaxException; import graphql.parser.Parser; import graphql.schema.GraphQLSchema; import graphql.validation.ValidationError; import graphql.validation.Validator; -import org.antlr.v4.runtime.misc.ParseCancellationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +37,6 @@ import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; -import static graphql.InvalidSyntaxError.toInvalidSyntaxError; import static graphql.execution.instrumentation.DocumentAndVariables.newDocumentAndVariables; @SuppressWarnings("Duplicates") @@ -200,7 +199,7 @@ private PreparsedDocumentEntry parseAndValidate(ExecutionInput executionInput, G ParseResult parseResult = parse(executionInput, graphQLSchema, instrumentationState); if (parseResult.isFailure()) { log.warn("Query failed to parse : '{}'", executionInput.getQuery()); - return new PreparsedDocumentEntry(toInvalidSyntaxError(parseResult.getException())); + return new PreparsedDocumentEntry(parseResult.getException().toInvalidSyntaxError()); } else { final Document document = parseResult.getDocument(); @@ -227,7 +226,7 @@ private ParseResult parse(ExecutionInput executionInput, GraphQLSchema graphQLSc documentAndVariables = newDocumentAndVariables() .document(document).variables(executionInput.getVariables()).build(); documentAndVariables = instrumentation.instrumentDocumentAndVariables(documentAndVariables, parameters); - } catch (ParseCancellationException e) { + } catch (InvalidSyntaxException e) { parseInstrumentation.onCompleted(null, e); return ParseResult.ofError(e); }