Skip to content

Commit 427f493

Browse files
authored
Issue #1768 ExecutionInput: allow to set value a top level (#1779)
* ExecutionInput: allow to set value a top level * ExecutionInput: fix tests
1 parent bdc958e commit 427f493

File tree

6 files changed

+121
-6
lines changed

6 files changed

+121
-6
lines changed

src/main/java/graphql/ExecutionInput.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class ExecutionInput {
2121
private final String query;
2222
private final String operationName;
2323
private final Object context;
24+
private final Object localContext;
2425
private final Object root;
2526
private final Map<String, Object> variables;
2627
private final DataLoaderRegistry dataLoaderRegistry;
@@ -30,7 +31,7 @@ public class ExecutionInput {
3031

3132

3233
@Internal
33-
private ExecutionInput(String query, String operationName, Object context, Object root, Map<String, Object> variables, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl, ExecutionId executionId, Locale locale) {
34+
private ExecutionInput(String query, String operationName, Object context, Object root, Map<String, Object> variables, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl, ExecutionId executionId, Locale locale, Object localContext) {
3435
this.query = assertNotNull(query, "query can't be null");
3536
this.operationName = operationName;
3637
this.context = context;
@@ -40,6 +41,7 @@ private ExecutionInput(String query, String operationName, Object context, Objec
4041
this.cacheControl = cacheControl;
4142
this.executionId = executionId;
4243
this.locale = locale;
44+
this.localContext = localContext;
4345
}
4446

4547
/**
@@ -62,6 +64,12 @@ public String getOperationName() {
6264
public Object getContext() {
6365
return context;
6466
}
67+
/**
68+
* @return the local context object to pass to all top level (i.e. query, mutation, subscription) data fetchers
69+
*/
70+
public Object getLocalContext() {
71+
return localContext;
72+
}
6573

6674
/**
6775
* @return the root object to start the query execution on
@@ -119,6 +127,7 @@ public ExecutionInput transform(Consumer<Builder> builderConsumer) {
119127
.query(this.query)
120128
.operationName(this.operationName)
121129
.context(this.context)
130+
.localContext(this.localContext)
122131
.root(this.root)
123132
.dataLoaderRegistry(this.dataLoaderRegistry)
124133
.cacheControl(this.cacheControl)
@@ -167,6 +176,7 @@ public static class Builder {
167176
private String query;
168177
private String operationName;
169178
private Object context = GraphQLContext.newContext().build();
179+
private Object localContext;
170180
private Object root;
171181
private Map<String, Object> variables = Collections.emptyMap();
172182
//
@@ -212,6 +222,15 @@ public Builder locale(Locale locale) {
212222
return this;
213223
}
214224

225+
/**
226+
* Sets initial localContext in root data fetchers
227+
* @return this builder
228+
*/
229+
public Builder localContext(Object localContext) {
230+
this.localContext = localContext;
231+
return this;
232+
}
233+
215234
/**
216235
* By default you will get a {@link GraphQLContext} object but you can set your own.
217236
*
@@ -263,7 +282,7 @@ public Builder cacheControl(CacheControl cacheControl) {
263282
}
264283

265284
public ExecutionInput build() {
266-
return new ExecutionInput(query, operationName, context, root, variables, dataLoaderRegistry, cacheControl, executionId, locale);
285+
return new ExecutionInput(query, operationName, context, root, variables, dataLoaderRegistry, cacheControl, executionId, locale, localContext);
267286
}
268287
}
269288
}

src/main/java/graphql/execution/Execution.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
8787
.mutationStrategy(mutationStrategy)
8888
.subscriptionStrategy(subscriptionStrategy)
8989
.context(executionInput.getContext())
90+
.localContext(executionInput.getLocalContext())
9091
.root(executionInput.getRoot())
9192
.fragmentsByName(fragmentsByName)
9293
.variables(coercedVariables)
@@ -145,7 +146,7 @@ private CompletableFuture<ExecutionResult> executeOperation(ExecutionContext exe
145146
ExecutionStrategyParameters parameters = newParameters()
146147
.executionStepInfo(executionStepInfo)
147148
.source(root)
148-
.localContext(null) // this is important to default as this
149+
.localContext(executionContext.getLocalContext())
149150
.fields(fields)
150151
.nonNullFieldValidator(nonNullableFieldValidator)
151152
.path(path)

src/main/java/graphql/execution/ExecutionContext.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ExecutionContext {
3838
private final Map<String, Object> variables;
3939
private final Object root;
4040
private final Object context;
41+
private final Object localContext;
4142
private final Instrumentation instrumentation;
4243
private final List<GraphQLError> errors = new CopyOnWriteArrayList<>();
4344
private final Set<ExecutionPath> errorPaths = new HashSet<>();
@@ -47,7 +48,7 @@ public class ExecutionContext {
4748
private final DeferSupport deferSupport = new DeferSupport();
4849
private final ValueUnboxer valueUnboxer;
4950

50-
ExecutionContext(Instrumentation instrumentation, ExecutionId executionId, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, ExecutionStrategy queryStrategy, ExecutionStrategy mutationStrategy, ExecutionStrategy subscriptionStrategy, Map<String, FragmentDefinition> fragmentsByName, Document document, OperationDefinition operationDefinition, Map<String, Object> variables, Object context, Object root, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl, Locale locale, List<GraphQLError> startingErrors, ValueUnboxer valueUnboxer) {
51+
ExecutionContext(Instrumentation instrumentation, ExecutionId executionId, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, ExecutionStrategy queryStrategy, ExecutionStrategy mutationStrategy, ExecutionStrategy subscriptionStrategy, Map<String, FragmentDefinition> fragmentsByName, Document document, OperationDefinition operationDefinition, Map<String, Object> variables, Object context, Object root, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl, Locale locale, List<GraphQLError> startingErrors, ValueUnboxer valueUnboxer, Object localContext) {
5152
this.graphQLSchema = graphQLSchema;
5253
this.executionId = executionId;
5354
this.instrumentationState = instrumentationState;
@@ -66,6 +67,7 @@ public class ExecutionContext {
6667
this.locale = locale;
6768
this.valueUnboxer = valueUnboxer;
6869
this.errors.addAll(startingErrors);
70+
this.localContext = localContext;
6971
}
7072

7173

@@ -105,6 +107,10 @@ public Map<String, Object> getVariables() {
105107
public <T> T getContext() {
106108
return (T) context;
107109
}
110+
@SuppressWarnings("unchecked")
111+
public <T> T getLocalContext() {
112+
return (T) localContext;
113+
}
108114

109115
@SuppressWarnings("unchecked")
110116
public <T> T getRoot() {

src/main/java/graphql/execution/ExecutionContextBuilder.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class ExecutionContextBuilder {
4141
private Locale locale;
4242
private List<GraphQLError> errors = new ArrayList<>();
4343
private ValueUnboxer valueUnboxer;
44+
private Object localContext;
4445

4546
/**
4647
* @return a new builder of {@link graphql.execution.ExecutionContext}s
@@ -74,6 +75,7 @@ public ExecutionContextBuilder() {
7475
mutationStrategy = other.getMutationStrategy();
7576
subscriptionStrategy = other.getSubscriptionStrategy();
7677
context = other.getContext();
78+
localContext = other.getLocalContext();
7779
root = other.getRoot();
7880
document = other.getDocument();
7981
operationDefinition = other.getOperationDefinition();
@@ -127,6 +129,11 @@ public ExecutionContextBuilder context(Object context) {
127129
return this;
128130
}
129131

132+
public ExecutionContextBuilder localContext(Object localContext) {
133+
this.localContext = localContext;
134+
return this;
135+
}
136+
130137
public ExecutionContextBuilder root(Object root) {
131138
this.root = root;
132139
return this;
@@ -194,7 +201,8 @@ public ExecutionContext build() {
194201
cacheControl,
195202
locale,
196203
errors,
197-
valueUnboxer);
204+
valueUnboxer,
205+
localContext);
198206
}
199207

200208
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package graphql
2+
3+
import graphql.schema.DataFetcher
4+
import graphql.schema.GraphQLObjectType
5+
import graphql.schema.GraphQLSchema
6+
import graphql.schema.GraphQLSchemaElement
7+
import graphql.schema.GraphQLTypeVisitorStub
8+
import graphql.schema.SchemaTransformer
9+
import graphql.schema.idl.NaturalEnumValuesProvider
10+
import graphql.util.TraversalControl
11+
import graphql.util.TraverserContext
12+
import graphql.util.TreeTransformerUtil
13+
import spock.lang.Specification
14+
15+
import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring
16+
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring
17+
18+
class Issue1768 extends Specification {
19+
20+
static enum ThreadSort {
21+
NEWEST_FIRST,
22+
OLDEST_FIRST,
23+
MOST_COMMENTS_FIRST
24+
}
25+
26+
def "#1768 check if the old behavior is not broken" () {
27+
def spec = '''
28+
type Query {
29+
dummy: String
30+
}
31+
'''
32+
GraphQL graphql = TestUtil.graphQL(spec, [Query: [dummy: (DataFetcher<String>) { null }]]).build()
33+
34+
when:
35+
ExecutionResult result = graphql.execute {
36+
it.query(" { dummy } ")
37+
}
38+
39+
then:
40+
result.data.dummy == null
41+
42+
}
43+
def "#1768 test if local context is set for top level"() {
44+
def spec = '''
45+
type Query {
46+
dummy: String
47+
}
48+
'''
49+
GraphQL graphql = TestUtil.graphQL(spec, [Query: [dummy: (DataFetcher<String>) { (String) it.localContext }]]).build()
50+
51+
when:
52+
ExecutionResult result = graphql.execute {
53+
it.localContext("test").query(" { dummy } ")
54+
}
55+
56+
then:
57+
result.data.dummy == "test"
58+
}
59+
def "#1768 test if the top local context gets transferred to the next level"() {
60+
def spec = '''
61+
type Query {
62+
dummy: DummyType
63+
}
64+
type DummyType {
65+
dummy: String
66+
}
67+
'''
68+
GraphQL graphql = TestUtil.graphQL(spec, [
69+
Query: [dummy: (DataFetcher<Map>) { [ : ]}],
70+
DummyType: [dummy: (DataFetcher<String>) { (String) it.localContext }]]).build()
71+
72+
when:
73+
ExecutionResult result = graphql.execute {
74+
it.localContext("test").query(" { dummy { dummy }} ")
75+
}
76+
77+
then:
78+
result.data.dummy.dummy == "test"
79+
}
80+
81+
}

src/test/groovy/graphql/execution/ExecutionStrategyTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ExecutionStrategyTest extends Specification {
6464
executionStrategy, executionStrategy, executionStrategy,
6565
[:], null, null,
6666
variables, "context", "root", new DataLoaderRegistry(),
67-
null, Locale.getDefault(), Collections.emptyList(), ValueUnboxer.DEFAULT)
67+
null, Locale.getDefault(), Collections.emptyList(), ValueUnboxer.DEFAULT, null)
6868
}
6969

7070
@SuppressWarnings("GroovyAssignabilityCheck")

0 commit comments

Comments
 (0)