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";
}
39 changes: 32 additions & 7 deletions src/main/java/graphql/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import graphql.execution.ExecutionId;
import graphql.execution.ExecutionIdProvider;
import graphql.execution.ExecutionStrategy;
import graphql.execution.ResponseMapFactory;
import graphql.execution.SimpleDataFetcherExceptionHandler;
import graphql.execution.SubscriptionExecutionStrategy;
import graphql.execution.ValueUnboxer;
Expand All @@ -25,6 +24,8 @@
import graphql.execution.preparsed.PreparsedDocumentEntry;
import graphql.execution.preparsed.PreparsedDocumentProvider;
import graphql.language.Document;
import graphql.normalized.nf.provider.NoOpNormalizedDocumentProvider;
import graphql.normalized.nf.provider.NormalizedDocumentProvider;
import graphql.schema.GraphQLSchema;
import graphql.validation.ValidationError;

Expand Down Expand Up @@ -158,6 +159,7 @@ public static GraphQLUnusualConfiguration.GraphQLContextConfiguration unusualCon
private final ExecutionIdProvider idProvider;
private final Instrumentation instrumentation;
private final PreparsedDocumentProvider preparsedDocumentProvider;
private final NormalizedDocumentProvider normalizedDocumentProvider;
private final ValueUnboxer valueUnboxer;
private final boolean doNotAutomaticallyDispatchDataLoader;

Expand All @@ -170,6 +172,7 @@ private GraphQL(Builder builder) {
this.idProvider = assertNotNull(builder.idProvider, () -> "idProvider must be non null");
this.instrumentation = assertNotNull(builder.instrumentation, () -> "instrumentation must not be null");
this.preparsedDocumentProvider = assertNotNull(builder.preparsedDocumentProvider, () -> "preparsedDocumentProvider must be non null");
this.normalizedDocumentProvider = assertNotNull(builder.normalizedDocumentProvider, () -> "normalizedDocumentProvider must be non null");
this.valueUnboxer = assertNotNull(builder.valueUnboxer, () -> "valueUnboxer must not be null");
this.doNotAutomaticallyDispatchDataLoader = builder.doNotAutomaticallyDispatchDataLoader;
}
Expand Down Expand Up @@ -227,6 +230,13 @@ public PreparsedDocumentProvider getPreparsedDocumentProvider() {
return preparsedDocumentProvider;
}

/**
* @return the NormalizedDocumentProvider for this {@link GraphQL} instance
*/
public NormalizedDocumentProvider getNormalizedDocumentProvider() {
return normalizedDocumentProvider;
}

/**
* @return the ValueUnboxer for this {@link GraphQL} instance
*/
Expand Down Expand Up @@ -261,7 +271,8 @@ public GraphQL transform(Consumer<GraphQL.Builder> builderConsumer) {
.subscriptionExecutionStrategy(this.subscriptionStrategy)
.executionIdProvider(Optional.ofNullable(this.idProvider).orElse(builder.idProvider))
.instrumentation(Optional.ofNullable(this.instrumentation).orElse(builder.instrumentation))
.preparsedDocumentProvider(Optional.ofNullable(this.preparsedDocumentProvider).orElse(builder.preparsedDocumentProvider));
.preparsedDocumentProvider(Optional.ofNullable(this.preparsedDocumentProvider).orElse(builder.preparsedDocumentProvider))
.normalizedDocumentProvider(Optional.ofNullable(this.normalizedDocumentProvider).orElse(builder.normalizedDocumentProvider));

builderConsumer.accept(builder);

Expand All @@ -278,6 +289,7 @@ public static class Builder {
private ExecutionIdProvider idProvider = DEFAULT_EXECUTION_ID_PROVIDER;
private Instrumentation instrumentation = null; // deliberate default here
private PreparsedDocumentProvider preparsedDocumentProvider = NoOpPreparsedDocumentProvider.INSTANCE;
private NormalizedDocumentProvider normalizedDocumentProvider = NoOpNormalizedDocumentProvider.INSTANCE;
private boolean doNotAutomaticallyDispatchDataLoader = false;
private ValueUnboxer valueUnboxer = ValueUnboxer.DEFAULT;

Expand Down Expand Up @@ -328,6 +340,18 @@ public Builder preparsedDocumentProvider(PreparsedDocumentProvider preparsedDocu
return this;
}

/**
* This allows you to set a {@link NormalizedDocumentProvider} that will be used to provide normalized documents
*
* @param normalizedDocumentProvider the provider of normalized documents
*
* @return this builder
*/
public Builder normalizedDocumentProvider(NormalizedDocumentProvider normalizedDocumentProvider) {
this.normalizedDocumentProvider = normalizedDocumentProvider;
return this;
}

public Builder executionIdProvider(ExecutionIdProvider executionIdProvider) {
this.idProvider = assertNotNull(executionIdProvider, () -> "ExecutionIdProvider must be non null");
return this;
Expand Down Expand Up @@ -497,7 +521,7 @@ public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionI

GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters, instrumentationState);

CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState, engineRunningState);
CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(instrumentedExecutionInput, graphQLSchema, instrumentationState, engineRunningState, normalizedDocumentProvider);
//
// finish up instrumentation
executionResult = executionResult.whenComplete(completeInstrumentationCtxCF(executionInstrumentation));
Expand Down Expand Up @@ -529,7 +553,7 @@ private ExecutionInput ensureInputHasId(ExecutionInput executionInput) {
}


private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, EngineRunningState engineRunningState) {
private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState, EngineRunningState engineRunningState, NormalizedDocumentProvider normalizedDocumentProvider) {
AtomicReference<ExecutionInput> executionInputRef = new AtomicReference<>(executionInput);
Function<ExecutionInput, PreparsedDocumentEntry> computeFunction = transformedInput -> {
// if they change the original query in the pre-parser, then we want to see it downstream from then on
Expand All @@ -542,7 +566,7 @@ private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInpu
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors()));
}
try {
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState, engineRunningState);
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState, engineRunningState, normalizedDocumentProvider);
} catch (AbortExecutionException e) {
return CompletableFuture.completedFuture(e.toExecutionResult());
}
Expand Down Expand Up @@ -606,10 +630,11 @@ private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput
Document document,
GraphQLSchema graphQLSchema,
InstrumentationState instrumentationState,
EngineRunningState engineRunningState
EngineRunningState engineRunningState,
NormalizedDocumentProvider normalizedDocumentProvider
) {

Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader);
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader, normalizedDocumentProvider);
ExecutionId executionId = executionInput.getExecutionId();

return execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState, engineRunningState);
Expand Down
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
8 changes: 6 additions & 2 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import graphql.language.NodeUtil;
import graphql.language.OperationDefinition;
import graphql.language.VariableDefinition;
import graphql.normalized.nf.provider.NormalizedDocumentProvider;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.impl.SchemaUtil;
Expand Down Expand Up @@ -55,20 +56,22 @@ public class Execution {
private final Instrumentation instrumentation;
private final ValueUnboxer valueUnboxer;
private final boolean doNotAutomaticallyDispatchDataLoader;

private final NormalizedDocumentProvider normalizedDocumentProvider;

public Execution(ExecutionStrategy queryStrategy,
ExecutionStrategy mutationStrategy,
ExecutionStrategy subscriptionStrategy,
Instrumentation instrumentation,
ValueUnboxer valueUnboxer,
boolean doNotAutomaticallyDispatchDataLoader) {
boolean doNotAutomaticallyDispatchDataLoader,
NormalizedDocumentProvider normalizedDocumentProvider) {
this.queryStrategy = queryStrategy != null ? queryStrategy : new AsyncExecutionStrategy();
this.mutationStrategy = mutationStrategy != null ? mutationStrategy : new AsyncSerialExecutionStrategy();
this.subscriptionStrategy = subscriptionStrategy != null ? subscriptionStrategy : new AsyncExecutionStrategy();
this.instrumentation = instrumentation;
this.valueUnboxer = valueUnboxer;
this.doNotAutomaticallyDispatchDataLoader = doNotAutomaticallyDispatchDataLoader;
this.normalizedDocumentProvider = normalizedDocumentProvider;
}

public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, ExecutionInput executionInput, InstrumentationState instrumentationState, EngineRunningState engineRunningState) {
Expand Down Expand Up @@ -118,6 +121,7 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
.locale(executionInput.getLocale())
.valueUnboxer(valueUnboxer)
.responseMapFactory(responseMapFactory)
.normalizedDocumentProvider(normalizedDocumentProvider)
.executionInput(executionInput)
.propagapropagateErrorsOnNonNullContractFailureeErrors(propagateErrorsOnNonNullContractFailure)
.engineRunningState(engineRunningState)
Expand Down
75 changes: 72 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,11 @@
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.normalized.nf.provider.NormalizedDocumentProvider;
import graphql.schema.GraphQLSchema;
import graphql.util.FpKit;
import graphql.util.LockKit;
Expand All @@ -30,9 +35,11 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

@SuppressWarnings("TypeParameterUnusedInFormals")
Expand Down Expand Up @@ -63,9 +70,10 @@ public class ExecutionContext {
private final IncrementalCallState incrementalCallState = new IncrementalCallState();
private final ValueUnboxer valueUnboxer;
private final ResponseMapFactory responseMapFactory;
private final NormalizedDocumentProvider normalizedDocumentProvider;

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 @@ -96,11 +104,12 @@ public class ExecutionContext {
this.locale = builder.locale;
this.valueUnboxer = builder.valueUnboxer;
this.responseMapFactory = builder.responseMapFactory;
this.normalizedDocumentProvider = builder.normalizedDocumentProvider;
this.errors.set(builder.errors);
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().join());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nit: there's a space after this.

this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
this.engineRunningState = builder.engineRunningState;
}
Expand Down Expand Up @@ -303,6 +312,11 @@ public ResponseMapFactory getResponseMapFactory() {
return responseMapFactory;
}

@Internal
public NormalizedDocumentProvider getNormalizedDocumentProvider() {
return normalizedDocumentProvider;
}

/**
* @return the total list of errors for this execution context
*/
Expand Down Expand Up @@ -336,7 +350,7 @@ public ExecutionStrategy getStrategy(OperationDefinition.Operation operation) {
}
}

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

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

private NormalizedDocument createNormalizedDocument() {
return NormalizedDocumentFactory.createNormalizedDocument(graphQLSchema, document);
}

private CompletableFuture<GraphQlNormalizedOperation> createGraphQLNormalizedOperation() {
// Check for experimental support for normalized documents
if (hasNormalizedDocumentSupport()) {
return createNormalizedOperation()
.thenApply(Function.identity()); // Cast to interface.
}

return CompletableFuture.completedFuture(createExecutableNormalizedOperation());
}

@ExperimentalApi
private CompletableFuture<NormalizedOperation> createNormalizedOperation() {
return normalizedDocumentProvider.getNormalizedDocument(executionInput, this::createNormalizedDocument).thenApply(normalizedDocumentEntry -> {
var normalizedDocument = normalizedDocumentEntry.getDocument();

// 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
Loading
Loading