From d36106673799b0061f273da784d835e9dac5a48a Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Wed, 5 Jul 2017 13:18:48 +1000 Subject: [PATCH 1/3] This allows the standard pattern of allowing a builder to be passed to the `graphql.execute()` method --- src/main/java/graphql/GraphQL.java | 38 +++++++++++++++-- src/test/groovy/graphql/GraphQLTest.groovy | 48 ++++++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index d85e72475f..79eb0fc3ab 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; +import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; @@ -182,13 +183,12 @@ public GraphQL build() { } /** + * Executes the specified graphql query/mutation/subscription + * * @param query the query/mutation/subscription * * @return result including errors - * - * @deprecated Use {@link #execute(ExecutionInput)} */ - @Deprecated public ExecutionResult execute(String query) { ExecutionInput executionInput = ExecutionInput.newExecutionInput() .query(query) @@ -285,6 +285,38 @@ public ExecutionResult execute(String query, String operationName, Object contex } /** + * Executes the graphql query using the provided input object builder + * + * @param executionInputBuilder {@link ExecutionInput.Builder} + * + * @return result including errors + */ + public ExecutionResult execute(ExecutionInput.Builder executionInputBuilder) { + return execute(executionInputBuilder.build()); + } + + /** + * Executes the graphql query using calling the builder function and giving it a new builder. + *

+ * 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 result including errors + */ + public ExecutionResult execute(UnaryOperator builderFunction) { + return execute(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); + } + + /** + * Executes the graphql query using the provided input object + * * @param executionInput {@link ExecutionInput} * * @return result including errors diff --git a/src/test/groovy/graphql/GraphQLTest.groovy b/src/test/groovy/graphql/GraphQLTest.groovy index 551019bb98..2c5503fbfd 100644 --- a/src/test/groovy/graphql/GraphQLTest.groovy +++ b/src/test/groovy/graphql/GraphQLTest.groovy @@ -1,10 +1,20 @@ package graphql import graphql.language.SourceLocation -import graphql.schema.* +import graphql.schema.DataFetcher +import graphql.schema.DataFetchingEnvironment +import graphql.schema.GraphQLFieldDefinition +import graphql.schema.GraphQLList +import graphql.schema.GraphQLNonNull +import graphql.schema.GraphQLObjectType +import graphql.schema.GraphQLOutputType +import graphql.schema.GraphQLSchema +import graphql.schema.StaticDataFetcher import graphql.validation.ValidationErrorType import spock.lang.Specification +import java.util.function.UnaryOperator + import static graphql.Scalars.GraphQLInt import static graphql.Scalars.GraphQLString import static graphql.schema.GraphQLArgument.newArgument @@ -17,9 +27,7 @@ import static graphql.schema.GraphQLSchema.newSchema class GraphQLTest extends Specification { - - def "simple query"() { - given: + GraphQLSchema simpleSchema() { GraphQLFieldDefinition.Builder fieldDefinition = newFieldDefinition() .name("hello") .type(GraphQLString) @@ -30,6 +38,12 @@ class GraphQLTest extends Specification { .field(fieldDefinition) .build() ).build() + schema + } + + def "simple query"() { + given: + GraphQLSchema schema = simpleSchema() when: def result = GraphQL.newGraphQL(schema).build().execute('{ hello }').data @@ -422,4 +436,30 @@ class GraphQLTest extends Specification { !result.errors.isEmpty() result.errors[0].errorType == ErrorType.InvalidSyntax } + + def "execution input passing builder"() { + given: + GraphQLSchema schema = simpleSchema() + + when: + def builder = ExecutionInput.newExecutionInput().query('{ hello }') + def result = GraphQL.newGraphQL(schema).build().execute(builder).data + + then: + result == [hello: 'world'] + } + + def "execution input using builder function"() { + given: + GraphQLSchema schema = simpleSchema() + + when: + + def builderFunction = { it.query('{hello}')} as UnaryOperator + def result = GraphQL.newGraphQL(schema).build().execute(builderFunction).data + + then: + result == [hello: 'world'] + } + } From eee62957b2bbba3b41eacb5dba78c75ab5e759b2 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Wed, 5 Jul 2017 13:27:29 +1000 Subject: [PATCH 2/3] Fixed in async code call --- src/main/java/graphql/GraphQL.java | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index ffa26a0bd5..784907fa9b 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -301,6 +301,17 @@ public ExecutionResult execute(UnaryOperator builderFunc return execute(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); } + /** + * Executes the graphql query using the provided input object + * + * @param executionInput {@link ExecutionInput} + * @return result including errors + */ + public ExecutionResult execute(ExecutionInput executionInput) { + return executeAsync(executionInput).join(); + } + + /** * Executes the graphql query using the provided input object * @@ -308,7 +319,7 @@ public ExecutionResult execute(UnaryOperator builderFunc * which is the result of executing the provided query. * * @param executionInput {@link ExecutionInput} - * @return a promise to an execution result + * @return a promise to an {@link ExecutionResult} */ public CompletableFuture executeAsync(ExecutionInput executionInput) { log.debug("Executing request. operation name: {}. query: {}. variables {} ", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); @@ -319,13 +330,6 @@ public CompletableFuture executeAsync(ExecutionInput executionI return executionResult; } - /** - * @param executionInput {@link ExecutionInput} - * @return result including errors - */ - public ExecutionResult execute(ExecutionInput executionInput) { - return executeAsync(executionInput).join(); - } private CompletableFuture parseValidateAndExecute(ExecutionInput executionInput) { PreparsedDocumentEntry preparsedDoc = preparsedDocumentProvider.get(executionInput.getQuery(), query -> parseAndValidate(executionInput)); From faa4d95ad32d6a0c8c49c5aced03d760bb89df07 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Wed, 5 Jul 2017 17:28:25 +1000 Subject: [PATCH 3/3] Updated to have async versions of the builder methods removed some Idea warnings --- src/main/java/graphql/GraphQL.java | 69 +++++++++++++++++++--- src/test/groovy/graphql/GraphQLTest.groovy | 43 +++++++++++--- 2 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 784907fa9b..03f05a844f 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -51,6 +51,7 @@ public class GraphQL { * A GraphQL object ready to execute queries * * @param graphQLSchema the schema to use + * * @deprecated use the {@link #newGraphQL(GraphQLSchema)} builder instead. This will be removed in a future version. */ @Internal @@ -64,6 +65,7 @@ public GraphQL(GraphQLSchema graphQLSchema) { * * @param graphQLSchema the schema to use * @param queryStrategy the query execution strategy to use + * * @deprecated use the {@link #newGraphQL(GraphQLSchema)} builder instead. This will be removed in a future version. */ @Internal @@ -78,6 +80,7 @@ public GraphQL(GraphQLSchema graphQLSchema, ExecutionStrategy queryStrategy) { * @param graphQLSchema the schema to use * @param queryStrategy the query execution strategy to use * @param mutationStrategy the mutation execution strategy to use + * * @deprecated use the {@link #newGraphQL(GraphQLSchema)} builder instead. This will be removed in a future version. */ @Internal @@ -92,6 +95,7 @@ public GraphQL(GraphQLSchema graphQLSchema, ExecutionStrategy queryStrategy, Exe * @param queryStrategy the query execution strategy to use * @param mutationStrategy the mutation execution strategy to use * @param subscriptionStrategy the subscription execution strategy to use + * * @deprecated use the {@link #newGraphQL(GraphQLSchema)} builder instead. This will be removed in a future version. */ @Internal @@ -113,6 +117,7 @@ private GraphQL(GraphQLSchema graphQLSchema, ExecutionStrategy queryStrategy, Ex * 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) { @@ -182,7 +187,8 @@ public GraphQL build() { * Executes the specified graphql query/mutation/subscription * * @param query the query/mutation/subscription - * @return result including errors + * + * @return an {@link ExecutionResult} which can include errors */ public ExecutionResult execute(String query) { ExecutionInput executionInput = ExecutionInput.newExecutionInput() @@ -196,7 +202,9 @@ public ExecutionResult execute(String query) { * * @param query the query/mutation/subscription * @param context custom object provided to each {@link graphql.schema.DataFetcher} - * @return result including errors + * + * @return an {@link ExecutionResult} which can include errors + * * @deprecated Use {@link #execute(ExecutionInput)} */ @Deprecated @@ -215,7 +223,9 @@ public ExecutionResult execute(String query, Object context) { * @param query the query/mutation/subscription * @param operationName the name of the operation to execute * @param context custom object provided to each {@link graphql.schema.DataFetcher} - * @return result including errors + * + * @return an {@link ExecutionResult} which can include errors + * * @deprecated Use {@link #execute(ExecutionInput)} */ @Deprecated @@ -235,7 +245,9 @@ public ExecutionResult execute(String query, String operationName, Object contex * @param query the query/mutation/subscription * @param context custom object provided to each {@link graphql.schema.DataFetcher} * @param variables variable values uses as argument - * @return result including errors + * + * @return an {@link ExecutionResult} which can include errors + * * @deprecated Use {@link #execute(ExecutionInput)} */ @Deprecated @@ -256,7 +268,9 @@ public ExecutionResult execute(String query, Object context, Map * @param operationName name of the operation to execute * @param context custom object provided to each {@link graphql.schema.DataFetcher} * @param variables variable values uses as argument - * @return result including errors + * + * @return an {@link ExecutionResult} which can include errors + * * @deprecated Use {@link #execute(ExecutionInput)} */ @Deprecated @@ -276,7 +290,7 @@ public ExecutionResult execute(String query, String operationName, Object contex * * @param executionInputBuilder {@link ExecutionInput.Builder} * - * @return result including errors + * @return an {@link ExecutionResult} which can include errors */ public ExecutionResult execute(ExecutionInput.Builder executionInputBuilder) { return execute(executionInputBuilder.build()); @@ -295,7 +309,7 @@ public ExecutionResult execute(ExecutionInput.Builder executionInputBuilder) { * * @param builderFunction a function that is given a {@link ExecutionInput.Builder} * - * @return result including errors + * @return an {@link ExecutionResult} which can include errors */ public ExecutionResult execute(UnaryOperator builderFunction) { return execute(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); @@ -305,12 +319,48 @@ public ExecutionResult execute(UnaryOperator builderFunc * Executes the graphql query using the provided input object * * @param executionInput {@link ExecutionInput} - * @return result including errors + * + * @return 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 + * + * 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 @@ -319,7 +369,8 @@ public ExecutionResult execute(ExecutionInput executionInput) { * which is the result of executing the provided query. * * @param executionInput {@link ExecutionInput} - * @return a promise to an {@link ExecutionResult} + * + * @return a promise to an {@link ExecutionResult} which can include errors */ public CompletableFuture executeAsync(ExecutionInput executionInput) { log.debug("Executing request. operation name: {}. query: {}. variables {} ", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); diff --git a/src/test/groovy/graphql/GraphQLTest.groovy b/src/test/groovy/graphql/GraphQLTest.groovy index ef42c54545..06889f7c33 100644 --- a/src/test/groovy/graphql/GraphQLTest.groovy +++ b/src/test/groovy/graphql/GraphQLTest.groovy @@ -15,6 +15,8 @@ import spock.lang.Specification import java.util.function.UnaryOperator +import static graphql.ExecutionInput.Builder +import static graphql.ExecutionInput.newExecutionInput import static graphql.Scalars.GraphQLInt import static graphql.Scalars.GraphQLString import static graphql.schema.GraphQLArgument.newArgument @@ -205,7 +207,8 @@ class GraphQLTest extends Specification { def expected = [field2: 'value2'] when: - def result = GraphQL.newGraphQL(schema).build().execute(query, 'Query2', null, [:]) + def executionInput = newExecutionInput().query(query).operationName('Query2').context(null).variables([:]) + def result = GraphQL.newGraphQL(schema).build().execute(executionInput) then: result.data == expected @@ -252,7 +255,6 @@ class GraphQLTest extends Specification { } - def "query with int literal too large"() { given: GraphQLSchema schema = newSchema().query( @@ -275,6 +277,7 @@ class GraphQLTest extends Specification { result.errors[0].description.contains("has wrong type") } + @SuppressWarnings("GroovyAssignabilityCheck") def "query with missing argument results in arguments map with value null"() { given: def dataFetcher = Mock(DataFetcher) @@ -301,6 +304,7 @@ class GraphQLTest extends Specification { } } + @SuppressWarnings("GroovyAssignabilityCheck") def "query with missing key in an input object result in a empty map"() { given: def dataFetcher = Mock(DataFetcher) @@ -352,15 +356,15 @@ class GraphQLTest extends Specification { .field(newFieldDefinition() .name("query") .argument(newArgument().name("fooParam").type(enumType)) - .type(Scalars.GraphQLInt)) + .type(GraphQLInt)) .build() GraphQLSchema schema = newSchema() .query(queryType) .build() when: - final GraphQL graphQL = GraphQL.newGraphQL(schema).build(); - final ExecutionResult result = graphQL.execute("{query (fooParam: [Val1,Val2])}"); + final GraphQL graphQL = GraphQL.newGraphQL(schema).build() + final ExecutionResult result = graphQL.execute("{query (fooParam: [Val1,Val2])}") then: result.errors.size() == 1 result.errors[0].errorType == ErrorType.ValidationError @@ -373,7 +377,7 @@ class GraphQLTest extends Specification { GraphQLSchema schema = simpleSchema() when: - def builder = ExecutionInput.newExecutionInput().query('{ hello }') + def builder = newExecutionInput().query('{ hello }') def result = GraphQL.newGraphQL(schema).build().execute(builder).data then: @@ -386,11 +390,36 @@ class GraphQLTest extends Specification { when: - def builderFunction = { it.query('{hello}')} as UnaryOperator + def builderFunction = { it.query('{hello}') } as UnaryOperator def result = GraphQL.newGraphQL(schema).build().execute(builderFunction).data then: result == [hello: 'world'] } + def "execution input passing builder to async"() { + given: + GraphQLSchema schema = simpleSchema() + + when: + def builder = newExecutionInput().query('{ hello }') + def result = GraphQL.newGraphQL(schema).build().executeAsync(builder).join().data + + then: + result == [hello: 'world'] + } + + def "execution input using builder function to async"() { + given: + GraphQLSchema schema = simpleSchema() + + when: + + def builderFunction = { it.query('{hello}') } as UnaryOperator + def result = GraphQL.newGraphQL(schema).build().executeAsync(builderFunction).join().data + + then: + result == [hello: 'world'] + } + }