javaspring-bootgraphqljavax.validation

How to implement exception handler for javax.validation.ConstraintViolationException in Spring boot GraphQL?


How to implement exception handler for javax.validation.ConstraintViolationException in Spring boot GraphQL?

I am getting following response

{
"errors": [
    {
        "message": "INTERNAL_ERROR for a7027473-59c3-b9ad-1917-873f354a352d",
        "locations": [
            {
                "line": 2,
                "column": 5
            }
        ],
        "path": [
            "createAuction"
        ],
        "extensions": {
            "classification": "INTERNAL_ERROR"
        }
    }
],
"data": {
    "createAuction": null
}

}

and the following exception in the log

javax.validation.ConstraintViolationException: createAuction.auctionInput.startTime: Auction start date time must be greater than the current date time
    at org.springframework.graphql.data.method.annotation.support.HandlerMethodInputValidator.validate(HandlerMethodInputValidator.java:80) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.validateAndInvoke(DataFetcherHandlerMethod.java:189) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:122) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:514) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:74) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.execution.ReactorContextManager.invokeCallable(ReactorContextManager.java:104) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:73) ~[spring-graphql-1.0.1.jar:1.0.1]
    at org.springframework.boot.actuate.metrics.graphql.GraphQlMetricsInstrumentation.lambda$instrumentDataFetcher$1(GraphQlMetricsInstrumentation.java:98) ~[spring-boot-actuator-2.7.2.jar:2.7.2]
    at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:87) ~[graphql-java-18.2.jar:na]
    at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:279) ~[graphql-java-18.2.jar:na]
    at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:210) ~[graphql-java-18.2.jar:na]
    at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:182) ~[graphql-java-18.2.jar:na]
    at graphql.execution.AsyncSerialExecutionStrategy.lambda$execute$1(AsyncSerialExecutionStrategy.java:43) ~[graphql-java-18.2.jar:na]
    at graphql.execution.Async.eachSequentiallyImpl(Async.java:80) ~[graphql-java-18.2.jar:na]
    at graphql.execution.Async.eachSequentially(Async.java:69) ~[graphql-java-18.2.jar:na]
    at graphql.execution.AsyncSerialExecutionStrategy.execute(AsyncSerialExecutionStrategy.java:38) ~[graphql-java-18.2.jar:na]
    at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-18.2.jar:na]
    at graphql.execution.Execution.execute(Execution.java:105) ~[graphql-java-18.2.jar:na]
    at graphql.GraphQL.execute(GraphQL.java:641) ~[graphql-java-18.2.jar:na]

Solution

  • Need to extend DataFetcherExceptionResolverAdapter and this class must be extended by your controller.

    public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter {
    
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
        Throwable t = NestedExceptionUtils.getMostSpecificCause(ex);
        logger.debug("Custom Exception {}", t.getLocalizedMessage());
        // Spring & custom validation error 
        if (ex instanceof ConstraintViolationException constraintViolationException) {
            ValidationErrorResponse error = new ValidationErrorResponse();
            for (ConstraintViolation<?> violation : constraintViolationException.getConstraintViolations()) {
                error.getViolations().add(new Violation(violation.getPropertyPath().toString(), violation.getMessage()));
            }
            return GraphqlErrorBuilder.newError(env)
                    .errorType(ErrorType.ValidationError).message(error.getMessage()).build();
        } else {
                return GraphqlErrorBuilder.newError(env)
                        .errorType(AuctionErrorType.OperationNotSupported).message(illegalStateException.getLocalizedMessage()).build();
            }
        }
        
        return null;
    }
    }