Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/graphql error handling #80

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

robertionut95b
Copy link

@robertionut95b robertionut95b commented Nov 2, 2023

Added GraphQL error handling support. Currently integrated with spring-graphql and working towards supporting more 3rd party implementations of GraphQL in Spring Framework/Boot.

Supports minimum version 1.2.2 of spring-graphql.

Features I was not able to implement or not yet supported:

  • Global response customization
  • Support for multiple 3rd party implementations of GraphQL in Spring framework:
  • Map location and path properties in the error responses: can't yet find a generic enough way which would work for all implementations above

Added GraphQL error handling support. Currently integrated with spring-graphql and working towards supporting more 3rd party implementations of GraphQL in Spring Framework/Boot.
import io.github.wimdeblauwe.errorhandlingspringbootstarter.mapper.HttpStatusMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

public abstract class AbstractErrorHandlingConfiguration {
@Bean
@ConditionalOnMissingBean
public LoggingService loggingService(ErrorHandlingProperties properties) {
return new LoggingService(properties);
public LoggingService loggingService(ErrorHandlingProperties properties,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather not have any graphql related classes in this class. It would be better to have a dedicated subclass.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Do you want this Configuration class to be a separate type of ServletErrorHandlingConfiguration specific to GraphQL configuration? Would there be a requirement to mark it as optional? Such way in properties with @ConditionalOnProperty(value = "error.handling.graphql.enabled", matchIfMissing = false)?

@@ -27,6 +32,20 @@ public void logException(ApiErrorResponse errorResponse, Throwable exception) {
}
}

public void logException(GraphQLError graphQLError, Throwable exception) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer a separate GraphQlLoggingService class. Common code could be moved into an abstract super class, or to helper components.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can extract the common logic into an AbstractLoggingService class. Then, each of the concrete implementations for REST and GraphQL can declare it's custom logic for logException method depending on the context. Is this what you had in mind?

import org.springframework.http.HttpStatusCode;

public abstract class AbstractGraphqlExceptionHandler implements GraphqlExceptionHandler {
protected final HttpStatusMapper httpStatusMapper;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess these can be private since there are the getter methods anyway?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can mark them as private, yes. I also mostly used the specialized "get...(Throwable exception)" methods.

import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class GqlConstraintViolationExceptionHandler extends AbstractGraphqlExceptionHandler {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of duplication with ConstraintViolationApiExceptionHandler. We should find a way to avoid that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes here it is a fact, indeed. How would you propose to tackle this? We have multiple options:

  1. Static Utils class to handle ConstraintViolationException -> ApiErrorResponse
  2. Bean helper class that translates ConstraintViolationException -> ApiErrorResponse (this increases extensibility)
  3. Specialized method (+ it's dependencies) in the super class to handle this specific behavior

@@ -0,0 +1,51 @@
package io.github.wimdeblauwe.errorhandlingspringbootstarter.handler;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can put all GraphQL related handlers in a graphql sub-package?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you want the Gql handlers to be under the root package or under root/handler sub-package?

@wimdeblauwe
Copy link
Owner

Thank you for this PR. Nice that you have so many tests added! In general, I'd like the graphql parts to be a little more separate, so using different packages. Maybe you can add a proposal as a comment here first before you start moving things around?

@ooraini
Copy link
Contributor

ooraini commented Nov 2, 2023

Hi, I have used both the error-handling-starter and recently started using GraphQl, so I have a two comments.

  • GqlErrorType looks awfully similar to org.springframework.graphql.execution.ErrorType Maybe use that instead
  • Not sure why GraphQlErrorMapper is needed since you will always return a 200 response

@robertionut95b
Copy link
Author

Hi, I have used both the error-handling-starter and recently started using GraphQl, so I have a two comments.

  • GqlErrorType looks awfully similar to org.springframework.graphql.execution.ErrorType Maybe use that instead
  • Not sure why GraphQlErrorMapper is needed since you will always return a 200 response

Hello @ooraini,

Those are some good points, but my arguments on using them:

  • We can later extend the GqlErrorType to more custom values for specific use-cases, a simple off the top of my head case with incorrect queries which natively resolves to ValidationError. If the API's will allow us to furtherly customize the responses then we use more specific values. We could use BAD_REQUEST as well, but I feel like this is more oriented to invalid request responses in terms of parameters or request body.
  • GraphQlErrorMapper is used to aid into creating a ErrorClassification which is later injected as value in the GraphqlError. For cases where we have let's say a Validation error, we can also map this value as BAD_REQUEST, similarly to how HttpStatus behaves. I also use it in the opposite way, to translate from ErrorClassification to HttpStatus in order to ensure the existing logging functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants