Skip to content

Commit ed18cb2

Browse files
committed
Add configuration to allow usage of normalized documents
1 parent 7c8b8aa commit ed18cb2

13 files changed

+322
-140
lines changed

src/main/java/graphql/ExperimentalApi.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,9 @@
2424
* The key that should be associated with a boolean value which indicates whether @defer and @stream behaviour is enabled for this execution.
2525
*/
2626
String ENABLE_INCREMENTAL_SUPPORT = "ENABLE_INCREMENTAL_SUPPORT";
27+
28+
/**
29+
* The key that should be associated with a boolean value which indicates whether normalized document behaviour is enabled for this execution.
30+
*/
31+
String ENABLE_NORMALIZED_DOCUMENT_SUPPORT = "ENABLE_NORMALIZED_DOCUMENT_SUPPORT";
2732
}

src/main/java/graphql/GraphQLUnusualConfiguration.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,13 @@ public IncrementalSupportConfig incrementalSupport() {
265265
return new IncrementalSupportConfig(this);
266266
}
267267

268+
/**
269+
* @return an element that allows you to control normalized document behavior
270+
*/
271+
public NormalizedDocumentSupportConfig normalizedDocumentSupport() {
272+
return new NormalizedDocumentSupportConfig(this);
273+
}
274+
268275
/**
269276
* @return an element that allows you to precisely control {@link org.dataloader.DataLoader} behavior
270277
* in graphql-java.
@@ -321,6 +328,28 @@ public GraphQLContextConfiguration then() {
321328
}
322329
}
323330

331+
public static class NormalizedDocumentSupportConfig extends BaseContextConfig {
332+
private NormalizedDocumentSupportConfig(GraphQLContextConfiguration contextConfig) {
333+
super(contextConfig);
334+
}
335+
336+
/**
337+
* @return true if normalized document behaviour is enabled for this execution.
338+
*/
339+
public boolean isNormalizedDocumentSupportEnabled() {
340+
return contextConfig.getBoolean(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT);
341+
}
342+
343+
/**
344+
* This controls whether normalized document behaviour is enabled for this execution.
345+
*/
346+
@ExperimentalApi
347+
public NormalizedDocumentSupportConfig enableNormalizedDocumentSupport(boolean enable) {
348+
contextConfig.put(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT, enable);
349+
return this;
350+
}
351+
}
352+
324353
public static class IncrementalSupportConfig extends BaseContextConfig {
325354
private IncrementalSupportConfig(GraphQLContextConfiguration contextConfig) {
326355
super(contextConfig);

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

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import graphql.language.Document;
1919
import graphql.language.FragmentDefinition;
2020
import graphql.language.OperationDefinition;
21-
import graphql.normalized.ExecutableNormalizedOperation;
2221
import graphql.normalized.ExecutableNormalizedOperationFactory;
22+
import graphql.normalized.GraphQlNormalizedOperation;
23+
import graphql.normalized.nf.NormalizedDocument;
24+
import graphql.normalized.nf.NormalizedDocumentFactory;
2325
import graphql.schema.GraphQLSchema;
2426
import graphql.util.FpKit;
2527
import graphql.util.LockKit;
@@ -66,7 +68,7 @@ public class ExecutionContext {
6668
private final ResponseMapFactory responseMapFactory;
6769

6870
private final ExecutionInput executionInput;
69-
private final Supplier<ExecutableNormalizedOperation> queryTree;
71+
private final Supplier<GraphQlNormalizedOperation> queryTree;
7072
private final boolean propagateErrorsOnNonNullContractFailure;
7173

7274
private final AtomicInteger isRunning = new AtomicInteger(0);
@@ -101,7 +103,7 @@ public class ExecutionContext {
101103
this.localContext = builder.localContext;
102104
this.executionInput = builder.executionInput;
103105
this.dataLoaderDispatcherStrategy = builder.dataLoaderDispatcherStrategy;
104-
this.queryTree = FpKit.interThreadMemoize(this::createExecutableNormalizedOperation);
106+
this.queryTree = FpKit.interThreadMemoize(this::createNormalizedOperation);
105107
this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
106108
this.engineRunningState = builder.engineRunningState;
107109
}
@@ -337,7 +339,7 @@ public ExecutionStrategy getStrategy(OperationDefinition.Operation operation) {
337339
}
338340
}
339341

340-
public Supplier<ExecutableNormalizedOperation> getNormalizedQueryTree() {
342+
public Supplier<GraphQlNormalizedOperation> getNormalizedQueryTree() {
341343
return queryTree;
342344
}
343345

@@ -369,22 +371,52 @@ public ResultNodesInfo getResultNodesInfo() {
369371
return resultNodesInfo;
370372
}
371373

372-
private ExecutableNormalizedOperation createExecutableNormalizedOperation() {
374+
private GraphQlNormalizedOperation createNormalizedOperation() {
373375
var parameters = new InstrumentationCreateNormalizedOperationParameters(executionInput, graphQLSchema);
374376
var instrument = instrumentation.beginCreateNormalizedOperation(parameters, instrumentationState);
375-
var result = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables);
377+
GraphQlNormalizedOperation result;
378+
if (hasNormalizedDocumentSupport()) {
379+
var normalizedDocument = NormalizedDocumentFactory.createNormalizedDocument(graphQLSchema, document);
380+
// Search the document for the operation that matches the operationDefinition name,
381+
// if no match then it could be anonymous query, then fallback to the first operation.
382+
var normalizedOperations = normalizedDocument.getNormalizedOperations();
383+
result = normalizedOperations.stream()
384+
.filter(this::isExecutingOperation)
385+
.findAny()
386+
.map(NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables::getNormalizedOperation)
387+
.orElseGet(normalizedDocument::getSingleNormalizedOperation);
388+
} else {
389+
result = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables);
390+
}
376391
if (instrument != null) {
377392
instrument.onCompleted(result, null);
378393
}
379394
return result;
380395
}
381396

397+
private boolean isExecutingOperation(NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables op) {
398+
var operation = op.getNormalizedOperation();
399+
var operationName = operation.getOperationName();
400+
var operationDefinitionName = operationDefinition.getName();
401+
if (operationName == null || operationDefinitionName == null) {
402+
return false;
403+
}
404+
405+
return operationName.equals(operationDefinitionName);
406+
}
407+
382408
@Internal
383409
public boolean hasIncrementalSupport() {
384410
GraphQLContext graphqlContext = getGraphQLContext();
385411
return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT);
386412
}
387413

414+
@Internal
415+
public boolean hasNormalizedDocumentSupport() {
416+
GraphQLContext graphqlContext = getGraphQLContext();
417+
return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT);
418+
}
419+
388420
@Internal
389421
public EngineRunningState getEngineRunningState() {
390422
return engineRunningState;

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import graphql.extensions.ExtensionsBuilder;
2828
import graphql.introspection.Introspection;
2929
import graphql.language.Field;
30-
import graphql.normalized.ExecutableNormalizedField;
31-
import graphql.normalized.ExecutableNormalizedOperation;
30+
import graphql.normalized.GraphQlNormalizedField;
31+
import graphql.normalized.GraphQlNormalizedOperation;
3232
import graphql.schema.CoercingSerializeException;
3333
import graphql.schema.DataFetcher;
3434
import graphql.schema.DataFetchingEnvironment;
@@ -420,7 +420,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
420420

421421
Supplier<Map<String, Object>> argumentValues = () -> executionStepInfo.get().getArguments();
422422

423-
Supplier<ExecutableNormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, executionStepInfo);
423+
Supplier<GraphQlNormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, executionStepInfo);
424424

425425
// DataFetchingFieldSelectionSet and QueryDirectives is a supplier of sorts - eg a lazy pattern
426426
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext.getGraphQLSchema(), fieldDef.getType(), normalizedFieldSupplier);
@@ -515,9 +515,9 @@ private Object invokeDataFetcher(ExecutionContext executionContext, ExecutionStr
515515
return fetchedValue;
516516
}
517517

518-
protected Supplier<ExecutableNormalizedField> getNormalizedField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Supplier<ExecutionStepInfo> executionStepInfo) {
519-
Supplier<ExecutableNormalizedOperation> normalizedQuery = executionContext.getNormalizedQueryTree();
520-
return () -> normalizedQuery.get().getNormalizedField(parameters.getField(), executionStepInfo.get().getObjectType(), executionStepInfo.get().getPath());
518+
protected Supplier<GraphQlNormalizedField> getNormalizedField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Supplier<ExecutionStepInfo> executionStepInfo) {
519+
Supplier<GraphQlNormalizedOperation> normalizedQuery = executionContext.getNormalizedQueryTree();
520+
return () -> normalizedQuery.get().getGraphQlNormalizedField(parameters.getField(), executionStepInfo.get().getObjectType(), executionStepInfo.get().getPath());
521521
}
522522

523523
protected FetchedValue unboxPossibleDataFetcherResult(ExecutionContext executionContext,

src/main/java/graphql/execution/ResolveType.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import graphql.Assert;
44
import graphql.Internal;
55
import graphql.TypeResolutionEnvironment;
6-
import graphql.normalized.ExecutableNormalizedField;
7-
import graphql.normalized.ExecutableNormalizedOperation;
6+
import graphql.normalized.GraphQlNormalizedField;
7+
import graphql.normalized.GraphQlNormalizedOperation;
88
import graphql.schema.DataFetchingFieldSelectionSet;
99
import graphql.schema.DataFetchingFieldSelectionSetImpl;
1010
import graphql.schema.GraphQLInterfaceType;
@@ -43,8 +43,8 @@ public GraphQLObjectType resolveType(ExecutionContext executionContext, MergedFi
4343
}
4444

4545
private DataFetchingFieldSelectionSet buildSelectionSet(ExecutionContext executionContext, MergedField field, GraphQLOutputType fieldType, ExecutionStepInfo executionStepInfo) {
46-
Supplier<ExecutableNormalizedOperation> normalizedQuery = executionContext.getNormalizedQueryTree();
47-
Supplier<ExecutableNormalizedField> normalizedFieldSupplier = () -> normalizedQuery.get().getNormalizedField(field, executionStepInfo.getObjectType(), executionStepInfo.getPath());
46+
Supplier<GraphQlNormalizedOperation> normalizedQuery = executionContext.getNormalizedQueryTree();
47+
Supplier<GraphQlNormalizedField> normalizedFieldSupplier = () -> normalizedQuery.get().getGraphQlNormalizedField(field, executionStepInfo.getObjectType(), executionStepInfo.getPath());
4848
return DataFetchingFieldSelectionSetImpl.newCollector(executionContext.getGraphQLSchema(), fieldType, normalizedFieldSupplier);
4949
}
5050

src/main/java/graphql/language/OperationDefinition.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import graphql.language.NodeUtil.DirectivesHolder;
99
import graphql.util.TraversalControl;
1010
import graphql.util.TraverserContext;
11+
import org.jspecify.annotations.Nullable;
1112

1213
import java.util.ArrayList;
1314
import java.util.LinkedHashMap;
@@ -93,7 +94,7 @@ public OperationDefinition withNewChildren(NodeChildrenContainer newChildren) {
9394
);
9495
}
9596

96-
public String getName() {
97+
public @Nullable String getName() {
9798
return name;
9899
}
99100

0 commit comments

Comments
 (0)