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

Custom Error types #140

Closed
UkonnRa opened this issue Dec 27, 2019 · 6 comments
Closed

Custom Error types #140

UkonnRa opened this issue Dec 27, 2019 · 6 comments
Labels
enhancement New feature or request

Comments

@UkonnRa
Copy link

UkonnRa commented Dec 27, 2019

Now CalibanError is a sealed trait and all of the messages will be decoded as a string like:

  "errors": [
    "Execution error: {\"@type\":\"EmailAlreadyExisted\"}"
  ]

But as the GraphQL Spec says, the recommended error looks like:

{
  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ],
      "extensions": {
        "code": "CAN_NOT_FETCH_BY_ID",
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]
}

And my domain error trait looks like:

sealed trait DomainError {
  val code: String Refined CodeMatcher
  val message: String Refined NonEmpty
  val statusCode: Int Refined StatusCodeMatcher
}

No matter what, I hope that Caliban can handle the JSONObject type, rather than put everything into a single string.

@ghostdogpr
Copy link
Owner

ghostdogpr commented Dec 27, 2019

You can do mapError on your GraphQLInterpreter object to transform the CalibanError into your own type and unfold your domain errors.

However looking at the Circe encoder i think this will get transformed into a string anyway so I might have to make that more flexible. I’ll have a look when I get back.

@ghostdogpr ghostdogpr added enhancement New feature or request question labels Dec 27, 2019
@UkonnRa
Copy link
Author

UkonnRa commented Dec 27, 2019

Now I have at least two points where I have to transform my domain errors into CalibanError:

  1. When write custom ArgBuilders. Because:
  def flatMap[A](f: T => Either[ExecutionError, A]): ArgBuilder[A] = (input: InputValue) => self.build(input).flatMap(f)
  1. When creating RootResolver(queries, mutations), I have to ensure that all the errors in IO must be CalibanError, or the runtime exception will be thrown:
    "Execution error on field '<some-operator>': 
Effect failure with java.lang.ClassCastException: 
class com.xxx.DomainError$<DomainErrorType> 
cannot be cast to class
java.lang.Throwable 
(com.xxx.DomainError$<DomainErrorType> is in unnamed module of loader 'app'; java.lang.Throwable 
is in module java.base of loader 'bootstrap')"

So I have to convert my domain errors into JSON and put them into CalibanError, then mapError on GraphQLInterpreter, which cause meanless JSON serialization/deserialization.

So, we need to figure out how to handle this problem...

@ghostdogpr
Copy link
Owner

For 2. they don’t have to be CalibanError, they just have to be Throwable. You can simply make your domain errors extend Throwable and that should be okay.

@paulpdaniels
Copy link
Collaborator

I created a PR #145 which should correctly encode the message into an object instead of a string per the spec. More work will need to be done to provide the other fields. In particular the location and path field will require changes to the execution, validation and parsing stages, because we will need to

  1. Track the index location of the start of the selection field and then compute a relative line/column by re-tracking it through the original request (I think, based on the capabilities of fastparse at least).
  2. We will need to pass along a path when generating the steps so that we can pass the path to the execution handler when it is building an error.

The second point would actually have further use when creating middleware such as the apollo-tracing or apollo-caching spec both of which require paths in order to build the extension body.

The last requirement of having a custom extension body would be easier to service. We could add a def withExtension(fn: ResponseValue => ResponseValue): CalibanError method to the CalibanError then add a def extensions: ResponseValue to the error itself.

@ghostdogpr
Copy link
Owner

Since the rest of the work can be done separately, I created 3 issues #156 #157 #158 for supporting locations, path and extensions. Closing this one.

@monksy
Copy link

monksy commented Sep 22, 2021

Just an update to this: new custom error types are supported without being throwable via; #1059

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

No branches or pull requests

4 participants