Skip to content

Commit afaa9aa

Browse files
bbakermanandimarek
andauthored
Support in DataFetchingFieldSelectionSet for Unions and Interfaces (#2079)
* make normalized field available to a DFE * fix tests * lookup ahead with normalized fields * fix after merge problems * use strong memoizer for the normalized query * Renamed memoizing functions * Work on better NormalisedField support * renamed the factory * Got most of the code now working and tested * PR tweaks * PR tweaks * PR tweaks based on review * PR tweaks based on review * PR tweaks based on review * PR tweaks based on review * removed failing test because its now done better in the other places * removed failing test because its now done better in the other places - this is another one that should die * PR feedback - added getImmediate fields Co-authored-by: Andreas Marek <andimarek@fastmail.fm>
1 parent cf89ea3 commit afaa9aa

23 files changed

+2755
-647
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import graphql.language.Document;
1111
import graphql.language.FragmentDefinition;
1212
import graphql.language.OperationDefinition;
13+
import graphql.normalized.NormalizedQueryTreeFactory;
14+
import graphql.normalized.NormalizedQueryTree;
1315
import graphql.schema.GraphQLSchema;
16+
import graphql.util.FpKit;
1417
import org.dataloader.DataLoaderRegistry;
1518

1619
import java.util.Collections;
@@ -21,6 +24,7 @@
2124
import java.util.Set;
2225
import java.util.ArrayList;
2326
import java.util.function.Consumer;
27+
import java.util.function.Supplier;
2428

2529
@SuppressWarnings("TypeParameterUnusedInFormals")
2630
@PublicApi
@@ -112,6 +116,7 @@ public Map<String, Object> getVariables() {
112116
public <T> T getContext() {
113117
return (T) context;
114118
}
119+
115120
@SuppressWarnings("unchecked")
116121
public <T> T getLocalContext() {
117122
return (T) localContext;
@@ -195,6 +200,10 @@ public ExecutionStrategy getSubscriptionStrategy() {
195200
return subscriptionStrategy;
196201
}
197202

203+
public Supplier<NormalizedQueryTree> getNormalizedQueryTree() {
204+
return FpKit.interThreadMemoize(() -> NormalizedQueryTreeFactory.createNormalizedQuery(graphQLSchema, operationDefinition, fragmentsByName, variables));
205+
}
206+
198207
/**
199208
* This helps you transform the current ExecutionContext object into another one by starting a builder with all
200209
* the current values and allows you to transform it how you want.

src/main/java/graphql/execution/ExecutionStepInfo.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class ExecutionStepInfo {
3636

3737
/**
3838
* If this StepInfo represent a field the type is equal to fieldDefinition.getType()
39-
*
39+
* <p>
4040
* if this StepInfo is a list element this type is the actual current list element. For example:
4141
* Query.pets: [[Pet]] with Pet either a Dog or Cat and the actual result is [[Dog1],[[Cat1]]
4242
* Then the type is (for a query "{pets{name}}"):
@@ -58,7 +58,7 @@ public class ExecutionStepInfo {
5858

5959
/**
6060
* field, fieldDefinition, fieldContainer and arguments differ per field StepInfo.
61-
*
61+
* <p>
6262
* But for list StepInfos these properties are the same as the field returning the list.
6363
*/
6464
private final MergedField field;
@@ -82,14 +82,23 @@ private ExecutionStepInfo(GraphQLOutputType type,
8282
this.fieldContainer = fieldsContainer;
8383
}
8484

85+
/**
86+
* @return the GraphQLObjectType defining the {@link #getFieldDefinition()}
87+
* @deprecated use {@link #getObjectType()} instead as it is named better
88+
* @see ExecutionStepInfo#getObjectType()
89+
*/
90+
public GraphQLObjectType getFieldContainer() {
91+
return fieldContainer;
92+
}
93+
8594
/**
8695
* The GraphQLObjectType where fieldDefinition is defined.
8796
* Note:
8897
* For the Introspection field __typename the returned object type doesn't actually contain the fieldDefinition.
8998
*
90-
* @return GraphQLObjectType defining {@link #getFieldDefinition()}
99+
* @return the GraphQLObjectType defining the {@link #getFieldDefinition()}
91100
*/
92-
public GraphQLObjectType getFieldContainer() {
101+
public GraphQLObjectType getObjectType() {
93102
return fieldContainer;
94103
}
95104

@@ -163,7 +172,6 @@ public Map<String, Object> getArguments() {
163172
*
164173
* @param name the name of the argument
165174
* @param <T> you decide what type it is
166-
*
167175
* @return the named argument or null if its not present
168176
*/
169177
@SuppressWarnings("unchecked")
@@ -192,7 +200,6 @@ public boolean hasParent() {
192200
* after type resolution has occurred
193201
*
194202
* @param newType the new type to be
195-
*
196203
* @return a new type info with the same
197204
*/
198205
public ExecutionStepInfo changeTypeWithPreservedNonNull(GraphQLOutputType newType) {

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import graphql.introspection.Introspection;
2020
import graphql.language.Argument;
2121
import graphql.language.Field;
22+
import graphql.normalized.NormalizedField;
23+
import graphql.normalized.NormalizedQueryTree;
2224
import graphql.schema.CoercingSerializeException;
2325
import graphql.schema.DataFetcher;
2426
import graphql.schema.DataFetchingEnvironment;
@@ -190,7 +192,7 @@ protected CompletableFuture<ExecutionResult> resolveField(ExecutionContext execu
190192
*/
191193
protected CompletableFuture<FieldValueInfo> resolveFieldWithInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
192194
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext, parameters, parameters.getField().getSingleField());
193-
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.memoize(() -> createExecutionStepInfo(executionContext, parameters, fieldDef, null));
195+
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.intraThreadMemoize(() -> createExecutionStepInfo(executionContext, parameters, fieldDef, null));
194196

195197
Instrumentation instrumentation = executionContext.getInstrumentation();
196198
InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginField(
@@ -235,15 +237,17 @@ protected CompletableFuture<FetchedValue> fetchField(ExecutionContext executionC
235237
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
236238
GraphQLOutputType fieldType = fieldDef.getType();
237239

238-
// DataFetchingFieldSelectionSet and QueryDirectives is a supplier of sorts - eg a lazy pattern
239-
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, parameters.getField());
240-
QueryDirectives queryDirectives = new QueryDirectivesImpl(field, executionContext.getGraphQLSchema(), executionContext.getVariables());
241-
242240
// if the DF (like PropertyDataFetcher) does not use the arguments of execution step info then dont build any
243-
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.memoize(
241+
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.intraThreadMemoize(
244242
() -> createExecutionStepInfo(executionContext, parameters, fieldDef, parentType));
245243
Supplier<Map<String, Object>> argumentValues = () -> executionStepInfo.get().getArguments();
246244

245+
Supplier<NormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, executionStepInfo);
246+
247+
// DataFetchingFieldSelectionSet and QueryDirectives is a supplier of sorts - eg a lazy pattern
248+
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(fieldType, normalizedFieldSupplier);
249+
QueryDirectives queryDirectives = new QueryDirectivesImpl(field, executionContext.getGraphQLSchema(), executionContext.getVariables());
250+
247251

248252
DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
249253
.source(parameters.getSource())
@@ -293,6 +297,11 @@ protected CompletableFuture<FetchedValue> fetchField(ExecutionContext executionC
293297
.thenApply(result -> unboxPossibleDataFetcherResult(executionContext, parameters, result));
294298
}
295299

300+
protected Supplier<NormalizedField> getNormalizedField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Supplier<ExecutionStepInfo> executionStepInfo) {
301+
Supplier<NormalizedQueryTree> normalizedQuery = executionContext.getNormalizedQueryTree();
302+
return () -> normalizedQuery.get().getNormalizedField(parameters.getField(), executionStepInfo.get().getObjectType(), executionStepInfo.get().getPath());
303+
}
304+
296305
protected FetchedValue unboxPossibleDataFetcherResult(ExecutionContext executionContext,
297306
ExecutionStrategyParameters parameters,
298307
Object result) {

src/main/java/graphql/execution/ResultPath.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,24 @@ public List<Object> toList() {
266266
return list;
267267
}
268268

269+
/**
270+
* @return this path as a list of result keys, without any indices
271+
*/
272+
public List<String> getKeysOnly() {
273+
if (parent == null) {
274+
return new LinkedList<>();
275+
}
276+
LinkedList<String> list = new LinkedList<>();
277+
ResultPath p = this;
278+
while (p.segment != null) {
279+
if (p.segment instanceof String) {
280+
list.addFirst((String) p.segment);
281+
}
282+
p = p.parent;
283+
}
284+
return list;
285+
}
286+
269287

270288
/**
271289
* @return the path as a string which represents the call hierarchy

src/main/java/graphql/execution/batched/BatchedExecutionStrategy.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
2525
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
2626
import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
27+
import graphql.normalized.NormalizedField;
2728
import graphql.schema.DataFetcher;
2829
import graphql.schema.DataFetchingEnvironment;
2930
import graphql.schema.DataFetchingFieldSelectionSet;
@@ -52,6 +53,7 @@
5253
import java.util.concurrent.CompletableFuture;
5354
import java.util.concurrent.CompletionException;
5455
import java.util.function.BiFunction;
56+
import java.util.function.Supplier;
5557
import java.util.stream.IntStream;
5658

5759
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
@@ -248,8 +250,10 @@ private CompletableFuture<FetchedValues> fetchData(ExecutionContext executionCon
248250

249251
QueryDirectivesImpl queryDirectives = new QueryDirectivesImpl(fields, executionContext.getGraphQLSchema(), executionContext.getVariables());
250252

253+
Supplier<NormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, parameters::getExecutionStepInfo);
254+
251255
GraphQLOutputType fieldType = fieldDef.getType();
252-
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, fields);
256+
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(fieldType, normalizedFieldSupplier);
253257

254258
DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
255259
.source(node.getSources())

src/main/java/graphql/execution/nextgen/ValueFetcher.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import graphql.execution.ValuesResolver;
1919
import graphql.execution.directives.QueryDirectivesImpl;
2020
import graphql.language.Field;
21+
import graphql.normalized.NormalizedField;
22+
import graphql.normalized.NormalizedQueryTree;
2123
import graphql.schema.DataFetcher;
2224
import graphql.schema.DataFetchingEnvironment;
2325
import graphql.schema.DataFetchingFieldSelectionSet;
@@ -117,12 +119,15 @@ public CompletableFuture<FetchedValue> fetchValue(ExecutionContext executionCont
117119
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
118120
GraphQLFieldsContainer parentType = getFieldsContainer(executionInfo);
119121

120-
Supplier<Map<String, Object>> argumentValues = FpKit.memoize(() -> valuesResolver.getArgumentValues(codeRegistry, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables()));
122+
Supplier<Map<String, Object>> argumentValues = FpKit.intraThreadMemoize(() -> valuesResolver.getArgumentValues(codeRegistry, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables()));
121123

122124
QueryDirectivesImpl queryDirectives = new QueryDirectivesImpl(sameFields, executionContext.getGraphQLSchema(), executionContext.getVariables());
123125

124126
GraphQLOutputType fieldType = fieldDef.getType();
125-
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, sameFields);
127+
128+
Supplier<NormalizedQueryTree> normalizedQuery = executionContext.getNormalizedQueryTree();
129+
Supplier<NormalizedField> normalisedField = () -> normalizedQuery.get().getNormalizedField(sameFields, executionInfo.getObjectType(), executionInfo.getPath());
130+
DataFetchingFieldSelectionSet selectionSet = DataFetchingFieldSelectionSetImpl.newCollector(fieldType, normalisedField);
126131

127132
DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
128133
.source(source)
@@ -133,7 +138,7 @@ public CompletableFuture<FetchedValue> fetchValue(ExecutionContext executionCont
133138
.fieldType(fieldType)
134139
.executionStepInfo(executionInfo)
135140
.parentType(parentType)
136-
.selectionSet(fieldCollector)
141+
.selectionSet(selectionSet)
137142
.queryDirectives(queryDirectives)
138143
.build();
139144

0 commit comments

Comments
 (0)