graphql.execution.AsyncExecutionStrategy Maven / Gradle / Ivy
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;
import static graphql.collect.ImmutableKit.map;
/**
* 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 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 fieldNames = fields.keySet();
List> futures = new ArrayList<>(fieldNames.size());
List 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 future = resolveFieldWithInfo(executionContext, newParameters);
futures.add(future);
}
CompletableFuture overallResult = new CompletableFuture<>();
executionStrategyCtx.onDispatched(overallResult);
Async.each(futures).whenComplete((completeValueInfos, throwable) -> {
BiConsumer, Throwable> handleResultsConsumer = handleResults(executionContext, resolvedFields, overallResult);
if (throwable != null) {
handleResultsConsumer.accept(null, throwable.getCause());
return;
}
List> executionResultFuture = map(completeValueInfos, FieldValueInfo::getFieldValue);
executionStrategyCtx.onFieldValuesInfo(completeValueInfos);
Async.each(executionResultFuture).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;
}
}