Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphql.execution;

import graphql.Internal;
import graphql.execution.incremental.AlternativeCallContext;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;

Expand Down Expand Up @@ -59,4 +60,8 @@ default void fieldFetched(ExecutionContext executionContext,
default DataFetcher<?> modifyDataFetcher(DataFetcher<?> dataFetcher) {
return dataFetcher;
}

default void newSubscriptionExecution(FieldValueInfo fieldValueInfo, AlternativeCallContext alternativeCallContext) {

}
}
7 changes: 1 addition & 6 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.dataloader.FallbackDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
Expand Down Expand Up @@ -255,11 +254,7 @@ private DataLoaderDispatchStrategy createDataLoaderDispatchStrategy(ExecutionCon
if (executionContext.getDataLoaderRegistry() == EMPTY_DATALOADER_REGISTRY || doNotAutomaticallyDispatchDataLoader) {
return DataLoaderDispatchStrategy.NO_OP;
}
if (!executionContext.isSubscriptionOperation()) {
return new PerLevelDataLoaderDispatchStrategy(executionContext);
} else {
return new FallbackDataLoaderDispatchStrategy(executionContext);
}
return new PerLevelDataLoaderDispatchStrategy(executionContext);
}


Expand Down
34 changes: 17 additions & 17 deletions src/main/java/graphql/execution/ExecutionStrategyParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import graphql.Internal;
import graphql.PublicApi;
import graphql.execution.incremental.DeferredCallContext;
import graphql.execution.incremental.AlternativeCallContext;
import org.jspecify.annotations.Nullable;

import java.util.function.Consumer;
Expand All @@ -22,7 +22,7 @@ public class ExecutionStrategyParameters {
private final ResultPath path;
private final MergedField currentField;
private final ExecutionStrategyParameters parent;
private final DeferredCallContext deferredCallContext;
private final AlternativeCallContext alternativeCallContext;

private ExecutionStrategyParameters(ExecutionStepInfo executionStepInfo,
Object source,
Expand All @@ -32,7 +32,7 @@ private ExecutionStrategyParameters(ExecutionStepInfo executionStepInfo,
ResultPath path,
MergedField currentField,
ExecutionStrategyParameters parent,
DeferredCallContext deferredCallContext) {
AlternativeCallContext alternativeCallContext) {

this.executionStepInfo = assertNotNull(executionStepInfo, () -> "executionStepInfo is null");
this.localContext = localContext;
Expand All @@ -42,7 +42,7 @@ private ExecutionStrategyParameters(ExecutionStepInfo executionStepInfo,
this.path = path;
this.currentField = currentField;
this.parent = parent;
this.deferredCallContext = deferredCallContext;
this.alternativeCallContext = alternativeCallContext;
}

public ExecutionStepInfo getExecutionStepInfo() {
Expand Down Expand Up @@ -95,8 +95,8 @@ public ExecutionStrategyParameters getParent() {
*/
@Nullable
@Internal
public DeferredCallContext getDeferredCallContext() {
return deferredCallContext;
public AlternativeCallContext getDeferredCallContext() {
return alternativeCallContext;
}

/**
Expand All @@ -105,7 +105,7 @@ public DeferredCallContext getDeferredCallContext() {
* @return true if we're in the scope of a deferred call
*/
public boolean isInDeferredContext() {
return deferredCallContext != null;
return alternativeCallContext != null;
}

/**
Expand All @@ -128,7 +128,7 @@ ExecutionStrategyParameters transform(MergedField currentField,
path,
currentField,
parent,
deferredCallContext);
alternativeCallContext);
}

@Internal
Expand All @@ -143,7 +143,7 @@ ExecutionStrategyParameters transform(ExecutionStepInfo executionStepInfo,
path,
currentField,
parent,
deferredCallContext);
alternativeCallContext);
}

@Internal
Expand All @@ -159,7 +159,7 @@ ExecutionStrategyParameters transform(ExecutionStepInfo executionStepInfo,
path,
currentField,
parent,
deferredCallContext);
alternativeCallContext);
}

@Internal
Expand All @@ -174,7 +174,7 @@ ExecutionStrategyParameters transform(ExecutionStepInfo executionStepInfo,
path,
currentField,
parent,
deferredCallContext);
alternativeCallContext);
}

@Internal
Expand All @@ -189,7 +189,7 @@ ExecutionStrategyParameters transform(MergedField currentField,
path,
currentField,
parent,
deferredCallContext);
alternativeCallContext);
}

public ExecutionStrategyParameters transform(Consumer<Builder> builderConsumer) {
Expand Down Expand Up @@ -221,7 +221,7 @@ public static class Builder {
ResultPath path = ResultPath.rootPath();
MergedField currentField;
ExecutionStrategyParameters parent;
DeferredCallContext deferredCallContext;
AlternativeCallContext alternativeCallContext;

/**
* @see ExecutionStrategyParameters#newParameters()
Expand All @@ -239,7 +239,7 @@ private Builder(ExecutionStrategyParameters oldParameters) {
this.fields = oldParameters.fields;
this.nonNullableFieldValidator = oldParameters.nonNullableFieldValidator;
this.currentField = oldParameters.currentField;
this.deferredCallContext = oldParameters.deferredCallContext;
this.alternativeCallContext = oldParameters.alternativeCallContext;
this.path = oldParameters.path;
this.parent = oldParameters.parent;
}
Expand Down Expand Up @@ -289,13 +289,13 @@ public Builder parent(ExecutionStrategyParameters parent) {
return this;
}

public Builder deferredCallContext(DeferredCallContext deferredCallContext) {
this.deferredCallContext = deferredCallContext;
public Builder deferredCallContext(AlternativeCallContext alternativeCallContext) {
this.alternativeCallContext = alternativeCallContext;
return this;
}

public ExecutionStrategyParameters build() {
return new ExecutionStrategyParameters(executionStepInfo, source, localContext, fields, nonNullableFieldValidator, path, currentField, parent, deferredCallContext);
return new ExecutionStrategyParameters(executionStepInfo, source, localContext, fields, nonNullableFieldValidator, path, currentField, parent, alternativeCallContext);
}
}
}
32 changes: 24 additions & 8 deletions src/main/java/graphql/execution/SubscriptionExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import graphql.ExecutionResultImpl;
import graphql.GraphQLContext;
import graphql.PublicApi;
import graphql.execution.incremental.AlternativeCallContext;
import graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
Expand Down Expand Up @@ -106,7 +107,7 @@ private boolean keepOrdered(GraphQLContext graphQLContext) {
*/

private CompletableFuture<Publisher<Object>> createSourceEventStream(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
ExecutionStrategyParameters newParameters = firstFieldOfSubscriptionSelection(executionContext,parameters);
ExecutionStrategyParameters newParameters = firstFieldOfSubscriptionSelection(executionContext, parameters, false);

CompletableFuture<FetchedValue> fieldFetched = Async.toCompletableFuture(fetchField(executionContext, newParameters));
return fieldFetched.thenApply(fetchedValue -> {
Expand All @@ -133,25 +134,27 @@ private CompletableFuture<Publisher<Object>> createSourceEventStream(ExecutionCo
*/

private CompletableFuture<ExecutionResult> executeSubscriptionEvent(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Object eventPayload) {

Instrumentation instrumentation = executionContext.getInstrumentation();

ExecutionContext newExecutionContext = executionContext.transform(builder -> builder
.root(eventPayload)
.resetErrors()
);
ExecutionStrategyParameters newParameters = firstFieldOfSubscriptionSelection(newExecutionContext, parameters);
ExecutionStrategyParameters newParameters = firstFieldOfSubscriptionSelection(newExecutionContext, parameters, true);
ExecutionStepInfo subscribedFieldStepInfo = createSubscribedFieldStepInfo(executionContext, newParameters);

InstrumentationFieldParameters i13nFieldParameters = new InstrumentationFieldParameters(executionContext, () -> subscribedFieldStepInfo);
InstrumentationContext<ExecutionResult> subscribedFieldCtx = nonNullCtx(instrumentation.beginSubscribedFieldEvent(
i13nFieldParameters, executionContext.getInstrumentationState()
));

FetchedValue fetchedValue = unboxPossibleDataFetcherResult(newExecutionContext, parameters, eventPayload);
FetchedValue fetchedValue = unboxPossibleDataFetcherResult(newExecutionContext, newParameters, eventPayload);
FieldValueInfo fieldValueInfo = completeField(newExecutionContext, newParameters, fetchedValue);
executionContext.getDataLoaderDispatcherStrategy().newSubscriptionExecution(fieldValueInfo, newParameters.getDeferredCallContext());
CompletableFuture<ExecutionResult> overallResult = fieldValueInfo
.getFieldValueFuture()
.thenApply(val -> new ExecutionResultImpl(val, newExecutionContext.getErrors()))
.thenApply(val -> new ExecutionResultImpl(val, newParameters.getDeferredCallContext().getErrors()))
.thenApply(executionResult -> wrapWithRootFieldName(newParameters, executionResult));

// dispatch instrumentation so they can know about each subscription event
Expand Down Expand Up @@ -179,17 +182,30 @@ private String getRootFieldName(ExecutionStrategyParameters parameters) {
return rootField.getResultKey();
}

private ExecutionStrategyParameters firstFieldOfSubscriptionSelection(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
private ExecutionStrategyParameters firstFieldOfSubscriptionSelection(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
boolean newCallContext) {
MergedSelectionSet fields = parameters.getFields();
MergedField firstField = fields.getSubField(fields.getKeys().get(0));

ResultPath fieldPath = parameters.getPath().segment(mkNameForPath(firstField.getSingleField()));
NonNullableFieldValidator nonNullableFieldValidator = new NonNullableFieldValidator(executionContext);
return parameters.transform(builder -> builder
.field(firstField).path(fieldPath).nonNullFieldValidator(nonNullableFieldValidator));


return parameters.transform(builder -> {
builder
.field(firstField)
.path(fieldPath)
.nonNullFieldValidator(nonNullableFieldValidator);
if (newCallContext) {
builder.deferredCallContext(new AlternativeCallContext(1, 1));
}
});

}

private ExecutionStepInfo createSubscribedFieldStepInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
private ExecutionStepInfo createSubscribedFieldStepInfo(ExecutionContext
executionContext, ExecutionStrategyParameters parameters) {
Field field = parameters.getField().getSingleField();
GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType();
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,34 @@
import graphql.Internal;
import graphql.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Contains data relevant to the execution of a {@link DeferredFragmentCall}.
* Contains data relevant to the execution of a {@link DeferredFragmentCall} and Subscription events.
* <p>
* The responsibilities of this class are similar to {@link graphql.execution.ExecutionContext}, but restricted to the
* execution of a deferred call (instead of the whole GraphQL execution like {@link graphql.execution.ExecutionContext}).
* <p>
* Some behaviours, like error capturing, need to be scoped to a single {@link DeferredFragmentCall}, because each defer payload
* Some behaviours, like error capturing, need to be scoped to a single {@link DeferredFragmentCall} for deferred, because each defer payload
* contains its own distinct list of errors.
*/
@Internal
public class DeferredCallContext {
public class AlternativeCallContext {

private final int startLevel;
private final int fields;

private final List<GraphQLError> errors = new CopyOnWriteArrayList<>();
private final List<GraphQLError> errors = Collections.synchronizedList(new ArrayList<>());

public DeferredCallContext(int startLevel, int fields) {
public AlternativeCallContext(int startLevel, int fields) {
this.startLevel = startLevel;
this.fields = fields;
}

@VisibleForTesting
public DeferredCallContext() {
public AlternativeCallContext() {
this.startLevel = 0;
this.fields = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,26 @@ public Set<IncrementalCall<? extends IncrementalPayload>> createCalls() {

private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution) {
int level = parameters.getPath().getLevel() + 1;
DeferredCallContext deferredCallContext = new DeferredCallContext(level, deferredFields.size());
AlternativeCallContext alternativeCallContext = new AlternativeCallContext(level, deferredFields.size());

List<MergedField> mergedFields = deferredExecutionToFields.get(deferredExecution);

List<Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>>> calls = FpKit.arrayListSizedTo(mergedFields);
for (MergedField currentField : mergedFields) {
calls.add(this.createResultSupplier(currentField, deferredCallContext));
calls.add(this.createResultSupplier(currentField, alternativeCallContext));
}

return new DeferredFragmentCall(
deferredExecution.getLabel(),
this.parameters.getPath(),
calls,
deferredCallContext
alternativeCallContext
);
}

private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult>> createResultSupplier(
MergedField currentField,
DeferredCallContext deferredCallContext
AlternativeCallContext alternativeCallContext
) {
Map<String, MergedField> fields = new LinkedHashMap<>();
fields.put(currentField.getResultKey(), currentField);
Expand All @@ -145,7 +145,7 @@ private Supplier<CompletableFuture<DeferredFragmentCall.FieldWithExecutionResult
{
MergedSelectionSet mergedSelectionSet = MergedSelectionSet.newMergedSelectionSet().subFields(fields).build();
ResultPath path = parameters.getPath().segment(currentField.getResultKey());
builder.deferredCallContext(deferredCallContext)
builder.deferredCallContext(alternativeCallContext)
.field(currentField)
.fields(mergedSelectionSet)
.path(path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ public ResultPath getPath() {

private final ResultPath path;
private final List<Supplier<CompletableFuture<FieldWithExecutionResult>>> calls;
private final DeferredCallContext deferredCallContext;
private final AlternativeCallContext alternativeCallContext;

public DeferredFragmentCall(
String label,
ResultPath path,
List<Supplier<CompletableFuture<FieldWithExecutionResult>>> calls,
DeferredCallContext deferredCallContext
AlternativeCallContext alternativeCallContext
) {
this.label = label;
this.path = path;
this.calls = calls;
this.deferredCallContext = deferredCallContext;
this.alternativeCallContext = alternativeCallContext;
}

@Override
Expand Down Expand Up @@ -100,7 +100,7 @@ private DeferPayload handleNonNullableFieldError(DeferPayload result, Throwable
}

private DeferPayload transformToDeferredPayload(List<FieldWithExecutionResult> fieldWithExecutionResults) {
List<GraphQLError> errorsEncountered = deferredCallContext.getErrors();
List<GraphQLError> errorsEncountered = alternativeCallContext.getErrors();

Map<String, Object> dataMap = new HashMap<>();

Expand Down
Loading