Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/graphql/ExperimentalApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@
* The key that should be associated with a boolean value which indicates whether @defer and @stream behaviour is enabled for this execution.
*/
String ENABLE_INCREMENTAL_SUPPORT = "ENABLE_INCREMENTAL_SUPPORT";

/**
* The key that should be associated with a boolean value which indicates whether normalized document behaviour is enabled for this execution.
*/
String ENABLE_NORMALIZED_DOCUMENT_SUPPORT = "ENABLE_NORMALIZED_DOCUMENT_SUPPORT";
}
29 changes: 29 additions & 0 deletions src/main/java/graphql/GraphQLUnusualConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ public IncrementalSupportConfig incrementalSupport() {
return new IncrementalSupportConfig(this);
}

/**
* @return an element that allows you to control normalized document behavior
*/
public NormalizedDocumentSupportConfig normalizedDocumentSupport() {
return new NormalizedDocumentSupportConfig(this);
}

/**
* @return an element that allows you to precisely control {@link org.dataloader.DataLoader} behavior
* in graphql-java.
Expand Down Expand Up @@ -321,6 +328,28 @@ public GraphQLContextConfiguration then() {
}
}

public static class NormalizedDocumentSupportConfig extends BaseContextConfig {
private NormalizedDocumentSupportConfig(GraphQLContextConfiguration contextConfig) {
super(contextConfig);
}

/**
* @return true if normalized document behaviour is enabled for this execution.
*/
public boolean isNormalizedDocumentSupportEnabled() {
return contextConfig.getBoolean(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT);
}

/**
* This controls whether normalized document behaviour is enabled for this execution.
*/
@ExperimentalApi
public NormalizedDocumentSupportConfig enableNormalizedDocumentSupport(boolean enable) {
contextConfig.put(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT, enable);
return this;
}
}

public static class IncrementalSupportConfig extends BaseContextConfig {
private IncrementalSupportConfig(GraphQLContextConfiguration contextConfig) {
super(contextConfig);
Expand Down
58 changes: 55 additions & 3 deletions src/main/java/graphql/execution/ExecutionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
import graphql.language.OperationDefinition;
import graphql.normalized.ExecutableNormalizedOperation;
import graphql.normalized.ExecutableNormalizedOperationFactory;
import graphql.normalized.GraphQlNormalizedOperation;
import graphql.normalized.nf.NormalizedDocument;
import graphql.normalized.nf.NormalizedDocumentFactory;
import graphql.normalized.nf.NormalizedOperation;
import graphql.schema.GraphQLSchema;
import graphql.util.FpKit;
import graphql.util.LockKit;
Expand Down Expand Up @@ -65,7 +69,7 @@ public class ExecutionContext {
private final ResponseMapFactory responseMapFactory;

private final ExecutionInput executionInput;
private final Supplier<ExecutableNormalizedOperation> queryTree;
private final Supplier<GraphQlNormalizedOperation> queryTree;
private final boolean propagateErrorsOnNonNullContractFailure;

private final AtomicInteger isRunning = new AtomicInteger(0);
Expand Down Expand Up @@ -100,7 +104,7 @@ public class ExecutionContext {
this.localContext = builder.localContext;
this.executionInput = builder.executionInput;
this.dataLoaderDispatcherStrategy = builder.dataLoaderDispatcherStrategy;
this.queryTree = FpKit.interThreadMemoize(() -> ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables));
this.queryTree = FpKit.interThreadMemoize(this::createGraphQLNormalizedOperation);
this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
this.engineRunningState = builder.engineRunningState;
}
Expand Down Expand Up @@ -336,7 +340,7 @@ public ExecutionStrategy getStrategy(OperationDefinition.Operation operation) {
}
}

public Supplier<ExecutableNormalizedOperation> getNormalizedQueryTree() {
public Supplier<GraphQlNormalizedOperation> getNormalizedQueryTree() {
return queryTree;
}

Expand Down Expand Up @@ -368,12 +372,60 @@ public ResultNodesInfo getResultNodesInfo() {
return resultNodesInfo;
}

private GraphQlNormalizedOperation createGraphQLNormalizedOperation() {
// Check for experimental support for normalized documents
if (hasNormalizedDocumentSupport()) {
return createNormalizedOperation();
}

return createExecutableNormalizedOperation();
}

@ExperimentalApi
private NormalizedOperation createNormalizedOperation() {
var normalizedDocument = NormalizedDocumentFactory.createNormalizedDocument(graphQLSchema, document);

// Search the document for the operation that matches the operationDefinition name,
// if no match then it could be anonymous query, then fallback to the first operation.
var normalizedOperations = normalizedDocument.getNormalizedOperations();
var normalizedOperation = normalizedOperations.stream()
.filter(this::isExecutingOperation)
.findAny()
.map(NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables::getNormalizedOperation)
.orElseGet(normalizedDocument::getSingleNormalizedOperation);

return normalizedOperation;
}

private ExecutableNormalizedOperation createExecutableNormalizedOperation() {
var executableNormalizedOperation = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables);

return executableNormalizedOperation;
}

private boolean isExecutingOperation(NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables op) {
var operation = op.getNormalizedOperation();
var operationName = operation.getOperationName();
var operationDefinitionName = operationDefinition.getName();
if (operationName == null || operationDefinitionName == null) {
return false;
}

return operationName.equals(operationDefinitionName);
}

@Internal
public boolean hasIncrementalSupport() {
GraphQLContext graphqlContext = getGraphQLContext();
return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT);
}

@Internal
private boolean hasNormalizedDocumentSupport() {
GraphQLContext graphqlContext = getGraphQLContext();
return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_NORMALIZED_DOCUMENT_SUPPORT);
}

@Internal
public EngineRunningState getEngineRunningState() {
return engineRunningState;
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import graphql.extensions.ExtensionsBuilder;
import graphql.introspection.Introspection;
import graphql.language.Field;
import graphql.normalized.ExecutableNormalizedField;
import graphql.normalized.ExecutableNormalizedOperation;
import graphql.normalized.GraphQlNormalizedField;
import graphql.normalized.GraphQlNormalizedOperation;
import graphql.schema.CoercingSerializeException;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
Expand Down Expand Up @@ -420,7 +420,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec

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

Supplier<ExecutableNormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, executionStepInfo);
Supplier<GraphQlNormalizedField> normalizedFieldSupplier = getNormalizedField(executionContext, parameters, executionStepInfo);

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

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

protected FetchedValue unboxPossibleDataFetcherResult(ExecutionContext executionContext,
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/graphql/execution/ResolveType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import graphql.Assert;
import graphql.Internal;
import graphql.TypeResolutionEnvironment;
import graphql.normalized.ExecutableNormalizedField;
import graphql.normalized.ExecutableNormalizedOperation;
import graphql.normalized.GraphQlNormalizedField;
import graphql.normalized.GraphQlNormalizedOperation;
import graphql.schema.DataFetchingFieldSelectionSet;
import graphql.schema.DataFetchingFieldSelectionSetImpl;
import graphql.schema.GraphQLInterfaceType;
Expand Down Expand Up @@ -43,8 +43,8 @@ public GraphQLObjectType resolveType(ExecutionContext executionContext, MergedFi
}

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

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/graphql/language/OperationDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import graphql.language.NodeUtil.DirectivesHolder;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import org.jspecify.annotations.Nullable;

import java.util.ArrayList;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -93,7 +94,7 @@ public OperationDefinition withNewChildren(NodeChildrenContainer newChildren) {
);
}

public String getName() {
public @Nullable String getName() {
return name;
}

Expand Down
Loading