Skip to content

Commit 636f8d3

Browse files
authored
Added the ability to copy a GraphQL object more easily (graphql-java#722)
* Added the ability to copy a GraphQL object more easily * Used transform pattern
1 parent 99efdc9 commit 636f8d3

2 files changed

Lines changed: 105 additions & 10 deletions

File tree

src/main/java/graphql/GraphQL.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Map;
3030
import java.util.concurrent.CompletableFuture;
3131
import java.util.concurrent.CompletionException;
32+
import java.util.function.Consumer;
3233
import java.util.function.UnaryOperator;
3334

3435
import static graphql.Assert.assertNotNull;
@@ -135,6 +136,32 @@ public static Builder newGraphQL(GraphQLSchema graphQLSchema) {
135136
return new Builder(graphQLSchema);
136137
}
137138

139+
/**
140+
* This helps you transform the current GraphQL object into another one by starting a builder with all
141+
* the current values and allows you to transform it how you want.
142+
*
143+
* @param builderConsumer the consumer code that will be given a builder to changee
144+
*
145+
* @return a new GraphQL object based on calling build on that builder
146+
*/
147+
public GraphQL transform(Consumer<GraphQL.Builder> builderConsumer) {
148+
Builder builder = new Builder(this.graphQLSchema);
149+
builder
150+
.queryExecutionStrategy(nvl(this.queryStrategy, builder.queryExecutionStrategy))
151+
.mutationExecutionStrategy(nvl(this.mutationStrategy, builder.mutationExecutionStrategy))
152+
.subscriptionExecutionStrategy(nvl(this.subscriptionStrategy, builder.subscriptionExecutionStrategy))
153+
.executionIdProvider(nvl(this.idProvider, builder.idProvider))
154+
.instrumentation(nvl(this.instrumentation, builder.instrumentation))
155+
.preparsedDocumentProvider(nvl(this.preparsedDocumentProvider, builder.preparsedDocumentProvider));
156+
157+
builderConsumer.accept(builder);
158+
159+
return builder.build();
160+
}
161+
162+
private static <T> T nvl(T obj, T elseObj) {
163+
return obj == null ? elseObj : obj;
164+
}
138165

139166
@PublicApi
140167
public static class Builder {

src/test/groovy/graphql/GraphQLTest.groovy

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ package graphql
22

33
import graphql.analysis.MaxQueryComplexityInstrumentation
44
import graphql.analysis.MaxQueryDepthInstrumentation
5+
import graphql.execution.AsyncExecutionStrategy
6+
import graphql.execution.ExecutionContext
7+
import graphql.execution.ExecutionId
8+
import graphql.execution.ExecutionIdProvider
9+
import graphql.execution.ExecutionStrategyParameters
510
import graphql.execution.batched.BatchedExecutionStrategy
11+
import graphql.execution.instrumentation.Instrumentation
12+
import graphql.execution.instrumentation.NoOpInstrumentation
613
import graphql.language.SourceLocation
714
import graphql.schema.DataFetcher
815
import graphql.schema.DataFetchingEnvironment
@@ -610,7 +617,7 @@ class GraphQLTest extends Specification {
610617
.build()).build())
611618
.build()
612619
613-
MaxQueryDepthInstrumentation maximumQueryDepthInstrumentation = new MaxQueryDepthInstrumentation(3);
620+
MaxQueryDepthInstrumentation maximumQueryDepthInstrumentation = new MaxQueryDepthInstrumentation(3)
614621
615622
616623
def graphql = GraphQL.newGraphQL(schema).instrumentation(maximumQueryDepthInstrumentation).build()
@@ -654,7 +661,7 @@ class GraphQLTest extends Specification {
654661
.build()).build())
655662
.build()
656663
657-
MaxQueryComplexityInstrumentation maxQueryComplexityInstrumentation = new MaxQueryComplexityInstrumentation(3);
664+
MaxQueryComplexityInstrumentation maxQueryComplexityInstrumentation = new MaxQueryComplexityInstrumentation(3)
658665
659666
660667
def graphql = GraphQL.newGraphQL(schema).instrumentation(maxQueryComplexityInstrumentation).build()
@@ -711,7 +718,7 @@ class GraphQLTest extends Specification {
711718
712719
GraphQLSchema schema = newSchema()
713720
.query(query)
714-
.build();
721+
.build()
715722
716723
GraphQL graphQL = GraphQL.newGraphQL(schema)
717724
.instrumentation(instrumentation)
@@ -722,7 +729,7 @@ class GraphQLTest extends Specification {
722729
.build()
723730
724731
when:
725-
def result = graphQL.execute(executionInput);
732+
def result = graphQL.execute(executionInput)
726733
727734
then:
728735
result.errors.size() == 1
@@ -737,7 +744,7 @@ class GraphQLTest extends Specification {
737744
738745
def "batched execution with non batched DataFetcher returning CompletableFuture"() {
739746
given:
740-
GraphQLObjectType foo = GraphQLObjectType.newObject()
747+
GraphQLObjectType foo = newObject()
741748
.name("Foo")
742749
.withInterface(new GraphQLTypeReference("Node"))
743750
.field(
@@ -760,14 +767,14 @@ class GraphQLTest extends Specification {
760767
{
761768
env ->
762769
if (env.getObject() instanceof CompletableFuture) {
763-
throw new RuntimeException("This seems bad!");
770+
throw new RuntimeException("This seems bad!")
764771
}
765772
766773
return foo
767774
})
768775
.build()
769776
770-
GraphQLObjectType query = GraphQLObjectType.newObject()
777+
GraphQLObjectType query = newObject()
771778
.name("RootQuery")
772779
.field(
773780
{ field ->
@@ -786,16 +793,16 @@ class GraphQLTest extends Specification {
786793
} as UnaryOperator)
787794
.build()
788795
789-
GraphQLSchema schema = GraphQLSchema.newSchema()
796+
GraphQLSchema schema = newSchema()
790797
.query(query)
791798
.build()
792799
793800
GraphQL graphQL = GraphQL.newGraphQL(schema)
794801
.queryExecutionStrategy(new BatchedExecutionStrategy())
795802
.mutationExecutionStrategy(new BatchedExecutionStrategy())
796-
.build();
803+
.build()
797804
798-
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
805+
ExecutionInput executionInput = newExecutionInput()
799806
.query("{node {id}}")
800807
.build()
801808
when:
@@ -806,5 +813,66 @@ class GraphQLTest extends Specification {
806813
result.getData() == [node: [id: "abc"]]
807814
}
808815
816+
class CaptureStrategy extends AsyncExecutionStrategy {
817+
ExecutionId executionId = null
818+
Instrumentation instrumentation = null
819+
820+
@Override
821+
CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
822+
executionId = executionContext.executionId
823+
instrumentation = executionContext.instrumentation
824+
return super.execute(executionContext, parameters)
825+
}
826+
}
827+
828+
def "graphql copying works as expected"() {
829+
830+
def instrumentation = new NoOpInstrumentation()
831+
def hello = ExecutionId.from("hello")
832+
def executionIdProvider = new ExecutionIdProvider() {
833+
@Override
834+
ExecutionId provide(String q, String operationName, Object context) {
835+
return hello
836+
}
837+
}
838+
839+
def queryStrategy = new CaptureStrategy()
840+
GraphQL graphQL = GraphQL.newGraphQL(simpleSchema())
841+
.queryExecutionStrategy(queryStrategy)
842+
.instrumentation(instrumentation)
843+
.executionIdProvider(executionIdProvider)
844+
.build()
845+
846+
when:
847+
// now copy it as is
848+
def newGraphQL = graphQL.transform({ builder -> })
849+
def result = newGraphQL.execute('{ hello }').data
850+
851+
then:
852+
result == [hello: 'world']
853+
queryStrategy.executionId == hello
854+
queryStrategy.instrumentation == instrumentation
855+
856+
when:
857+
858+
// now make some changes
859+
def newInstrumentation = new NoOpInstrumentation()
860+
def goodbye = ExecutionId.from("goodbye")
861+
def newExecutionIdProvider = new ExecutionIdProvider() {
862+
@Override
863+
ExecutionId provide(String q, String operationName, Object context) {
864+
return goodbye
865+
}
866+
}
867+
868+
newGraphQL = graphQL.transform({ builder ->
869+
builder.executionIdProvider(newExecutionIdProvider).instrumentation(newInstrumentation)
870+
})
871+
result = newGraphQL.execute('{ hello }').data
809872
873+
then:
874+
result == [hello: 'world']
875+
queryStrategy.executionId == goodbye
876+
queryStrategy.instrumentation == newInstrumentation
877+
}
810878
}

0 commit comments

Comments
 (0)