Document how to configure custom ExecutionStrategy
Closes gh-832
This commit is contained in:
@@ -196,6 +196,39 @@ https://github.com/graphql-java/graphql-java-extended-validation[Extended Valida
|
||||
library.
|
||||
|
||||
|
||||
[[execution.graphqlsource.execution-strategy]]
|
||||
=== `ExecutionStrategy`
|
||||
|
||||
An `ExecutionStrategy` in GraphQL Java drives the fetching of requested fields.
|
||||
To create an `ExecutionStrategy`, you need to provide a `DataFetcherExceptionHandler`.
|
||||
By default, Spring for GraphQL creates the exception handler to use as described in
|
||||
xref:request-execution.adoc#execution.exceptions[Exceptions] and sets it on the
|
||||
`GraphQL.Builder`. GraphQL Java then uses that to create `AsyncExecutionStrategy`
|
||||
instances with the configured exception handler.
|
||||
|
||||
If you need to create a custom `ExecutionStrategy`, you can detect
|
||||
``DataFetcherExceptionResolver``s and create an exception handler in the same way, and use
|
||||
it to create the custom `ExecutionStrategy`. For example, in a Spring Boot application:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Bean
|
||||
GraphQlSourceBuilderCustomizer sourceBuilderCustomizer(
|
||||
ObjectProvider<DataFetcherExceptionResolver> resolvers) {
|
||||
|
||||
DataFetcherExceptionHandler exceptionHandler =
|
||||
DataFetcherExceptionResolver.createExceptionHandler(resolvers.stream().toList());
|
||||
|
||||
AsyncExecutionStrategy strategy = new CustomAsyncExecutionStrategy(exceptionHandler);
|
||||
|
||||
return sourceBuilder -> sourceBuilder.configureGraphQl(builder ->
|
||||
builder.queryExecutionStrategy(strategy).mutationExecutionStrategy(strategy));
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
[[execution.graphqlsource.schema-transformation]]
|
||||
=== Schema Transformation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,10 +26,12 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Contract to resolve exceptions from {@link graphql.schema.DataFetcher}s.
|
||||
* Implementations are typically declared as beans in Spring configuration and
|
||||
* are invoked sequentially until one emits a List of {@link GraphQLError}s.
|
||||
* Resolves are typically declared as Spring beans and invoked in turn until one
|
||||
* resolves the exception by emitting a (possibly empty) {@code GraphQLError} list.
|
||||
* Use the static factory method {@link #createExceptionHandler} to create a
|
||||
* {@link DataFetcherExceptionHandler} from a list of resolvers.
|
||||
*
|
||||
* <p>Most resolver implementations can extend
|
||||
* <p>Resolver implementations can extend
|
||||
* {@link DataFetcherExceptionResolverAdapter} and override one of its
|
||||
* {@link DataFetcherExceptionResolverAdapter#resolveToSingleError resolveToSingleError} or
|
||||
* {@link DataFetcherExceptionResolverAdapter#resolveToMultipleErrors resolveToMultipleErrors}
|
||||
@@ -85,13 +87,13 @@ public interface DataFetcherExceptionResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link DataFetcherExceptionResolver} from a
|
||||
* list of resolvers. Spring for GraphQL uses this method to set
|
||||
* {@link graphql.GraphQL.Builder#defaultDataFetcherExceptionHandler(DataFetcherExceptionHandler)}
|
||||
* from resolvers found in Spring configuration, and that default handler
|
||||
* is used in turn to create each {@code ExecutionStrategy}. Applications
|
||||
* may also find this factory method useful when creating a custom
|
||||
* {@code ExecutionStrategy}.
|
||||
* Factory method to create a {@link DataFetcherExceptionHandler} from a
|
||||
* list of {@link DataFetcherExceptionResolver}'s. This is used internally
|
||||
* in {@link AbstractGraphQlSourceBuilder} to set the exception handler on
|
||||
* {@link graphql.GraphQL.Builder}, which in turn is used to create
|
||||
* {@link graphql.execution.ExecutionStrategy}'s. Applications may also use
|
||||
* this method to create an exception handler when they to need to initialize
|
||||
* a custom {@code ExecutionStrategy}.
|
||||
* <p>Resolvers are invoked in turn until one resolves the exception by
|
||||
* emitting a (possibly empty) {@code GraphQLError} list. If the exception
|
||||
* remains unresolved, the handler creates a {@code GraphQLError} with
|
||||
|
||||
@@ -40,14 +40,19 @@ import org.springframework.util.Assert;
|
||||
* {@link DataFetcherExceptionHandler} that invokes {@link DataFetcherExceptionResolver}'s
|
||||
* in a sequence until one returns a list of {@link GraphQLError}'s.
|
||||
*
|
||||
* <p>Use {@link DataFetcherExceptionResolver#createExceptionHandler(List)} to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ExceptionResolversExceptionHandler.class);
|
||||
|
||||
|
||||
private final List<DataFetcherExceptionResolver> resolvers;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
* @param resolvers the resolvers to use
|
||||
@@ -60,9 +65,11 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(DataFetcherExceptionHandlerParameters params) {
|
||||
Throwable exception = unwrapException(params);
|
||||
DataFetchingEnvironment env = params.getDataFetchingEnvironment();
|
||||
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(
|
||||
DataFetcherExceptionHandlerParameters handlerParameters) {
|
||||
|
||||
Throwable exception = unwrapException(handlerParameters);
|
||||
DataFetchingEnvironment env = handlerParameters.getDataFetchingEnvironment();
|
||||
ContextSnapshot snapshot = ContextSnapshot.captureFrom(env.getGraphQlContext());
|
||||
try {
|
||||
return Flux.fromIterable(this.resolvers)
|
||||
@@ -80,15 +87,6 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
|
||||
}
|
||||
}
|
||||
|
||||
private DataFetcherExceptionHandlerResult handleResolverError(
|
||||
Throwable resolverException, Throwable originalException, DataFetchingEnvironment environment) {
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failure while resolving " + originalException.getMessage(), resolverException);
|
||||
}
|
||||
return createInternalError(originalException, environment);
|
||||
}
|
||||
|
||||
private Throwable unwrapException(DataFetcherExceptionHandlerParameters params) {
|
||||
Throwable ex = params.getException();
|
||||
return ((ex instanceof CompletionException) ? ex.getCause() : ex);
|
||||
@@ -96,18 +94,27 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
|
||||
|
||||
private void logResolvedException(Throwable ex, DataFetcherExceptionHandlerResult result) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolved " + ex.getClass().getSimpleName() +
|
||||
" to GraphQL error(s): " + result.getErrors(), ex);
|
||||
String name = ex.getClass().getSimpleName();
|
||||
logger.debug("Resolved " + name + " to GraphQL error(s): " + result.getErrors(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment environment) {
|
||||
ExecutionId executionId = environment.getExecutionId();
|
||||
private DataFetcherExceptionHandlerResult handleResolverError(
|
||||
Throwable resolverException, Throwable originalException, DataFetchingEnvironment env) {
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failure while resolving " + originalException.getMessage(), resolverException);
|
||||
}
|
||||
return createInternalError(originalException, env);
|
||||
}
|
||||
|
||||
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment env) {
|
||||
ExecutionId executionId = env.getExecutionId();
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for executionId " + executionId, ex);
|
||||
}
|
||||
return DataFetcherExceptionHandlerResult
|
||||
.newResult(GraphqlErrorBuilder.newError(environment)
|
||||
.newResult(GraphqlErrorBuilder.newError(env)
|
||||
.errorType(ErrorType.INTERNAL_ERROR)
|
||||
.message(ErrorType.INTERNAL_ERROR + " for " + executionId)
|
||||
.build())
|
||||
|
||||
Reference in New Issue
Block a user