Skip to content

Commit f9a34ef

Browse files
committed
Merge branch '1.2.x'
2 parents dd2a3d2 + aa1ee77 commit f9a34ef

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

spring-graphql-docs/modules/ROOT/pages/request-execution.adoc

+33
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,39 @@ https://github.com/graphql-java/graphql-java-extended-validation[Extended Valida
192192
library.
193193

194194

195+
[[execution.graphqlsource.execution-strategy]]
196+
=== `ExecutionStrategy`
197+
198+
An `ExecutionStrategy` in GraphQL Java drives the fetching of requested fields.
199+
To create an `ExecutionStrategy`, you need to provide a `DataFetcherExceptionHandler`.
200+
By default, Spring for GraphQL creates the exception handler to use as described in
201+
xref:request-execution.adoc#execution.exceptions[Exceptions] and sets it on the
202+
`GraphQL.Builder`. GraphQL Java then uses that to create `AsyncExecutionStrategy`
203+
instances with the configured exception handler.
204+
205+
If you need to create a custom `ExecutionStrategy`, you can detect
206+
``DataFetcherExceptionResolver``s and create an exception handler in the same way, and use
207+
it to create the custom `ExecutionStrategy`. For example, in a Spring Boot application:
208+
209+
[source,java,indent=0,subs="verbatim,quotes"]
210+
----
211+
@Bean
212+
GraphQlSourceBuilderCustomizer sourceBuilderCustomizer(
213+
ObjectProvider<DataFetcherExceptionResolver> resolvers) {
214+
215+
DataFetcherExceptionHandler exceptionHandler =
216+
DataFetcherExceptionResolver.createExceptionHandler(resolvers.stream().toList());
217+
218+
AsyncExecutionStrategy strategy = new CustomAsyncExecutionStrategy(exceptionHandler);
219+
220+
return sourceBuilder -> sourceBuilder.configureGraphQl(builder ->
221+
builder.queryExecutionStrategy(strategy).mutationExecutionStrategy(strategy));
222+
}
223+
----
224+
225+
226+
227+
195228
[[execution.graphqlsource.schema-transformation]]
196229
=== Schema Transformation
197230

spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolver.java

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,10 +26,12 @@
2626

2727
/**
2828
* Contract to resolve exceptions from {@link graphql.schema.DataFetcher}s.
29-
* Implementations are typically declared as beans in Spring configuration and
30-
* are invoked sequentially until one emits a List of {@link GraphQLError}s.
29+
* Resolves are typically declared as Spring beans and invoked in turn until one
30+
* resolves the exception by emitting a (possibly empty) {@code GraphQLError} list.
31+
* Use the static factory method {@link #createExceptionHandler} to create a
32+
* {@link DataFetcherExceptionHandler} from a list of resolvers.
3133
*
32-
* <p>Most resolver implementations can extend
34+
* <p>Resolver implementations can extend
3335
* {@link DataFetcherExceptionResolverAdapter} and override one of its
3436
* {@link DataFetcherExceptionResolverAdapter#resolveToSingleError resolveToSingleError} or
3537
* {@link DataFetcherExceptionResolverAdapter#resolveToMultipleErrors resolveToMultipleErrors}
@@ -85,13 +87,13 @@ protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironmen
8587
}
8688

8789
/**
88-
* Factory method to create a {@link DataFetcherExceptionResolver} from a
89-
* list of resolvers. Spring for GraphQL uses this method to set
90-
* {@link graphql.GraphQL.Builder#defaultDataFetcherExceptionHandler(DataFetcherExceptionHandler)}
91-
* from resolvers found in Spring configuration, and that default handler
92-
* is used in turn to create each {@code ExecutionStrategy}. Applications
93-
* may also find this factory method useful when creating a custom
94-
* {@code ExecutionStrategy}.
90+
* Factory method to create a {@link DataFetcherExceptionHandler} from a
91+
* list of {@link DataFetcherExceptionResolver}'s. This is used internally
92+
* in {@link AbstractGraphQlSourceBuilder} to set the exception handler on
93+
* {@link graphql.GraphQL.Builder}, which in turn is used to create
94+
* {@link graphql.execution.ExecutionStrategy}'s. Applications may also use
95+
* this method to create an exception handler when they to need to initialize
96+
* a custom {@code ExecutionStrategy}.
9597
* <p>Resolvers are invoked in turn until one resolves the exception by
9698
* emitting a (possibly empty) {@code GraphQLError} list. If the exception
9799
* remains unresolved, the handler creates a {@code GraphQLError} with

spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java

+24-17
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,19 @@
4040
* {@link DataFetcherExceptionHandler} that invokes {@link DataFetcherExceptionResolver}'s
4141
* in a sequence until one returns a list of {@link GraphQLError}'s.
4242
*
43+
* <p>Use {@link DataFetcherExceptionResolver#createExceptionHandler(List)} to
44+
* create an instance.
45+
*
4346
* @author Rossen Stoyanchev
4447
*/
4548
class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler {
4649

4750
private static final Log logger = LogFactory.getLog(ExceptionResolversExceptionHandler.class);
4851

52+
4953
private final List<DataFetcherExceptionResolver> resolvers;
5054

55+
5156
/**
5257
* Create an instance.
5358
* @param resolvers the resolvers to use
@@ -59,9 +64,11 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
5964

6065

6166
@Override
62-
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(DataFetcherExceptionHandlerParameters params) {
63-
Throwable exception = unwrapException(params);
64-
DataFetchingEnvironment env = params.getDataFetchingEnvironment();
67+
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(
68+
DataFetcherExceptionHandlerParameters handlerParameters) {
69+
70+
Throwable exception = unwrapException(handlerParameters);
71+
DataFetchingEnvironment env = handlerParameters.getDataFetchingEnvironment();
6572
ContextSnapshot snapshot = ContextSnapshotFactoryHelper.captureFrom(env.getGraphQlContext());
6673
try {
6774
return Flux.fromIterable(this.resolvers)
@@ -79,34 +86,34 @@ public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(Data
7986
}
8087
}
8188

82-
private DataFetcherExceptionHandlerResult handleResolverError(
83-
Throwable resolverException, Throwable originalException, DataFetchingEnvironment environment) {
84-
85-
if (logger.isWarnEnabled()) {
86-
logger.warn("Failure while resolving " + originalException.getMessage(), resolverException);
87-
}
88-
return createInternalError(originalException, environment);
89-
}
90-
9189
private Throwable unwrapException(DataFetcherExceptionHandlerParameters params) {
9290
Throwable ex = params.getException();
9391
return ((ex instanceof CompletionException) ? ex.getCause() : ex);
9492
}
9593

9694
private void logResolvedException(Throwable ex, DataFetcherExceptionHandlerResult result) {
9795
if (logger.isDebugEnabled()) {
98-
logger.debug("Resolved " + ex.getClass().getSimpleName() +
99-
" to GraphQL error(s): " + result.getErrors(), ex);
96+
String name = ex.getClass().getSimpleName();
97+
logger.debug("Resolved " + name + " to GraphQL error(s): " + result.getErrors(), ex);
98+
}
99+
}
100+
101+
private DataFetcherExceptionHandlerResult handleResolverError(
102+
Throwable resolverException, Throwable originalException, DataFetchingEnvironment env) {
103+
104+
if (logger.isWarnEnabled()) {
105+
logger.warn("Failure while resolving " + originalException.getMessage(), resolverException);
100106
}
107+
return createInternalError(originalException, env);
101108
}
102109

103-
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment environment) {
104-
ExecutionId executionId = environment.getExecutionId();
110+
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment env) {
111+
ExecutionId executionId = env.getExecutionId();
105112
if (logger.isErrorEnabled()) {
106113
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for executionId " + executionId, ex);
107114
}
108115
return DataFetcherExceptionHandlerResult
109-
.newResult(GraphqlErrorBuilder.newError(environment)
116+
.newResult(GraphqlErrorBuilder.newError(env)
110117
.errorType(ErrorType.INTERNAL_ERROR)
111118
.message(ErrorType.INTERNAL_ERROR + " for " + executionId)
112119
.build())

0 commit comments

Comments
 (0)