-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
AsyncExecutionStrategy.java
89 lines (75 loc) · 3.99 KB
/
AsyncExecutionStrategy.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package graphql.execution;
import graphql.ExecutionResult;
import graphql.PublicApi;
import graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
/**
* The standard graphql execution strategy that runs fields asynchronously non-blocking.
*/
@PublicApi
public class AsyncExecutionStrategy extends AbstractAsyncExecutionStrategy {
/**
* The standard graphql execution strategy that runs fields asynchronously
*/
public AsyncExecutionStrategy() {
super(new SimpleDataFetcherExceptionHandler());
}
/**
* Creates a execution strategy that uses the provided exception handler
*
* @param exceptionHandler the exception handler to use
*/
public AsyncExecutionStrategy(DataFetcherExceptionHandler exceptionHandler) {
super(exceptionHandler);
}
@Override
@SuppressWarnings("FutureReturnValueIgnored")
public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationExecutionStrategyParameters instrumentationParameters = new InstrumentationExecutionStrategyParameters(executionContext, parameters);
ExecutionStrategyInstrumentationContext executionStrategyCtx = instrumentation.beginExecutionStrategy(instrumentationParameters);
MergedSelectionSet fields = parameters.getFields();
Set<String> fieldNames = fields.keySet();
Async.CombinedBuilder<FieldValueInfo> futures = Async.ofExpectedSize(fields.size());
List<String> resolvedFields = new ArrayList<>(fieldNames.size());
for (String fieldName : fieldNames) {
MergedField currentField = fields.getSubField(fieldName);
ResultPath fieldPath = parameters.getPath().segment(mkNameForPath(currentField));
ExecutionStrategyParameters newParameters = parameters
.transform(builder -> builder.field(currentField).path(fieldPath).parent(parameters));
resolvedFields.add(fieldName);
CompletableFuture<FieldValueInfo> future = resolveFieldWithInfo(executionContext, newParameters);
futures.add(future);
}
CompletableFuture<ExecutionResult> overallResult = new CompletableFuture<>();
executionStrategyCtx.onDispatched(overallResult);
futures.await().whenComplete((completeValueInfos, throwable) -> {
BiConsumer<List<ExecutionResult>, Throwable> handleResultsConsumer = handleResults(executionContext, resolvedFields, overallResult);
if (throwable != null) {
handleResultsConsumer.accept(null, throwable.getCause());
return;
}
Async.CombinedBuilder<ExecutionResult> executionResultFutures = Async.ofExpectedSize(completeValueInfos.size());
for (FieldValueInfo completeValueInfo : completeValueInfos) {
executionResultFutures.add(completeValueInfo.getFieldValue());
}
executionStrategyCtx.onFieldValuesInfo(completeValueInfos);
executionResultFutures.await().whenComplete(handleResultsConsumer);
}).exceptionally((ex) -> {
// if there are any issues with combining/handling the field results,
// complete the future at all costs and bubble up any thrown exception so
// the execution does not hang.
executionStrategyCtx.onFieldValuesException();
overallResult.completeExceptionally(ex);
return null;
});
overallResult.whenComplete(executionStrategyCtx::onCompleted);
return overallResult;
}
}