diff --git a/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandler.kt b/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandler.kt index fea1431ec..308ac0821 100644 --- a/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandler.kt +++ b/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandler.kt @@ -24,6 +24,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.util.ClassUtils import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionException /** * Default DataFetcherExceptionHandler used by the framework, can be replaced with a custom implementation. @@ -41,7 +42,7 @@ class DefaultDataFetcherExceptionHandler : DataFetcherExceptionHandler { } private fun doHandleException(handlerParameters: DataFetcherExceptionHandlerParameters): DataFetcherExceptionHandlerResult { - val exception = handlerParameters.exception + val exception = unwrapCompletionException(handlerParameters.exception) logger.error( "Exception while executing data fetcher for ${handlerParameters.path}: ${exception.message}", exception @@ -62,6 +63,10 @@ class DefaultDataFetcherExceptionHandler : DataFetcherExceptionHandler { .build() } + private fun unwrapCompletionException(e: Throwable): Throwable { + return if (e is CompletionException && e.cause != null) e.cause!! else e + } + companion object { private val logger: Logger = LoggerFactory.getLogger(DefaultDataFetcherExceptionHandler::class.java) diff --git a/graphql-dgs/src/test/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandlerTest.kt b/graphql-dgs/src/test/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandlerTest.kt index 970b55f9b..52a4fb67d 100644 --- a/graphql-dgs/src/test/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandlerTest.kt +++ b/graphql-dgs/src/test/kotlin/com/netflix/graphql/dgs/exceptions/DefaultDataFetcherExceptionHandlerTest.kt @@ -26,6 +26,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.security.access.AccessDeniedException +import java.util.concurrent.CompletionException internal class DefaultDataFetcherExceptionHandlerTest { @@ -126,4 +127,22 @@ internal class DefaultDataFetcherExceptionHandlerTest { // We return null here because we don't want graphql-java to write classification field assertThat(result.errors[0].errorType).isNull() } + + @Test + fun `CompletionException returns wrapped error code`() { + val completionException = CompletionException( + "com.netflix.graphql.dgs.exceptions.DgsEntityNotFoundException: Requested entity not found", + DgsEntityNotFoundException() + ) + every { dataFetcherExceptionHandlerParameters.exception }.returns(completionException) + + val result = DefaultDataFetcherExceptionHandler().handleException(dataFetcherExceptionHandlerParameters).get() + assertThat(result.errors.size).isEqualTo(1) + + val extensions = result.errors[0].extensions + assertThat(extensions["errorType"]).isEqualTo("NOT_FOUND") + + // We return null here because we don't want graphql-java to write classification field + assertThat(result.errors[0].errorType).isNull() + } }