Skip to content

Commit 304fb45

Browse files
committed
Refine handling of unresolved exceptions
Closes gh-352
1 parent e3cf655 commit 304fb45

File tree

3 files changed

+21
-14
lines changed

3 files changed

+21
-14
lines changed

spring-graphql-docs/src/docs/asciidoc/index.adoc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -514,30 +514,38 @@ added by <<server-interception, WebGraphQlInterceptor>> components.
514514
[[execution-exceptions]]
515515
=== Exception Resolution
516516

517-
GraphQL Java applications can register a `DataFetcherExceptionHandler` to decide how to
517+
A GraphQL Java application can register a `DataFetcherExceptionHandler` to decide how to
518518
represent exceptions from the data layer in the "errors" section of the GraphQL response.
519519

520520
Spring for GraphQL has a built-in `DataFetcherExceptionHandler` that is configured for use
521-
by the <<execution-graphqlsource>> builder. It enables applications to register one or
522-
more Spring `DataFetcherExceptionResolver` components that are invoked sequentially
523-
until one resolves the `Exception` to a list of `graphql.GraphQLError` objects.
521+
by the default <<execution-graphqlsource>> builder. It allows applications to register
522+
one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially
523+
until one resolves the `Exception` to a (possibly empty) list of `graphql.GraphQLError`
524+
objects.
524525

525526
`DataFetcherExceptionResolver` is an asynchronous contract. For most implementations, it
526527
would be sufficient to extend `DataFetcherExceptionResolverAdapter` and override
527528
one of its `resolveToSingleError` or `resolveToMultipleErrors` methods that
528529
resolve exceptions synchronously.
529530

530-
A `GraphQLError` can be assigned an `graphql.ErrorClassification`. Spring for GraphQL
531-
defines an `ErrorType` enum with common, error classification categories:
531+
A `GraphQLError` can be assigned to a category via `graphql.ErrorClassification`.
532+
In Spring GraphQL, you can also assign via `ErrorType` which has the following common
533+
classifications that applications can use to categorize errors:
532534

533535
- `BAD_REQUEST`
534536
- `UNAUTHORIZED`
535537
- `FORBIDDEN`
536538
- `NOT_FOUND`
537539
- `INTERNAL_ERROR`
538540

539-
Applications can use this to classify errors. If an error remains unresolved, by
540-
default it is marked as `INTERNAL_ERROR`.
541+
If an exception remains unresolved, by default it is categorized as an `INTERNAL_ERROR`
542+
with a generic message that includes the category name and the `executionId` from
543+
`DataFetchingEnvironment`. The message is intentionally opaque to avoid leaking
544+
implementation details. Applications can use a `DataFetcherExceptionResolver` to customize
545+
error details.
546+
547+
Unresolved exception are logged at ERROR level along with the `executionId` to correlate
548+
to the error sent to the client. Resolved exceptions are logged at DEBUG level.
541549

542550

543551

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import reactor.util.context.ContextView;
3636

3737
import org.springframework.util.Assert;
38-
import org.springframework.util.StringUtils;
3938

4039
/**
4140
* {@link DataFetcherExceptionHandler} that invokes {@link DataFetcherExceptionResolver}'s
@@ -111,14 +110,14 @@ private void logResolvedException(Throwable ex, DataFetcherExceptionHandlerResul
111110
}
112111

113112
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment environment) {
113+
ExecutionId executionId = environment.getExecutionId();
114114
if (logger.isErrorEnabled()) {
115-
ExecutionId id = environment.getExecutionId();
116-
logger.error("Unresolved " + ex.getClass().getSimpleName() + ", executionId= " + id, ex);
115+
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for executionId " + executionId, ex);
117116
}
118117
return DataFetcherExceptionHandlerResult
119118
.newResult(GraphqlErrorBuilder.newError(environment)
120119
.errorType(ErrorType.INTERNAL_ERROR)
121-
.message((StringUtils.hasText(ex.getMessage()) ? ex.getMessage() : ex.getClass().getSimpleName()))
120+
.message(ErrorType.INTERNAL_ERROR + " for " + executionId)
122121
.build())
123122
.build();
124123
}

spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import reactor.util.context.Context;
2828
import reactor.util.context.ContextView;
2929

30-
import org.springframework.graphql.ResponseHelper;
3130
import org.springframework.graphql.GraphQlSetup;
31+
import org.springframework.graphql.ResponseHelper;
3232
import org.springframework.graphql.TestThreadLocalAccessor;
3333

3434
import static org.assertj.core.api.Assertions.assertThat;
@@ -126,7 +126,7 @@ void unresolvedException() throws Exception {
126126

127127
ResponseHelper response = ResponseHelper.forResult(result);
128128
assertThat(response.errorCount()).isEqualTo(1);
129-
assertThat(response.error(0).message()).isEqualTo("Invalid greeting");
129+
assertThat(response.error(0).message()).startsWith("INTERNAL_ERROR for ");
130130
assertThat(response.error(0).errorType()).isEqualTo("INTERNAL_ERROR");
131131

132132
String greeting = response.rawValue("greeting");

0 commit comments

Comments
 (0)