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

Add @nullOnError #3772

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

Conversation

martinbonnin
Copy link
Contributor

See #3756

import graphql.GraphQLContext;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.*;
Copy link
Member

Choose a reason for hiding this comment

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

we avoid * imports

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted! Any objection to checking in .idea/codeStyles to avoid IntelliJ adding those automatically?

Copy link
Member

Choose a reason for hiding this comment

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

That's a great idea, please do check in a code style file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done here

@bbakerman
Copy link
Member

The graphql-java team @andimarek and @dondonz discussed this today.

We decided that we would accept this feature with the idea that is very much opt in.

So rather than use src/main/java/graphql/schema/idl/SchemaGenerator.java and its Options to "auto put" the directive in place we would require that people copy the directive into their schema themselves. if some one was programmatically creating a schema say then they would have to add Directives.ErrorHandlingDirective themselves into the schema say.

This is in line with how the experimental @defer is also required to be added to the SDL in text form or programmatically added to the schema say

Also we should adopt the same "opt in" mechanism as defer uses eg graphql.ExperimentalApi#ENABLE_INCREMENTAL_SUPPORT

We should create another String constant like this and if its present in the graphql context, then this code is enabled

eg something like this

var b = Optional.ofNullable(executionContext.getGraphQLContext())
                        .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_ERROR_PROPAGATION))
                        .orElse(false)

We have to think of a name of this "feature" and I dont think "error handling" is a great name because its so general versus this very specific "dont bubble non null errors up" form of error handling.

So this also calls into question the directive name of "errorHandling" - again its a general term but in practice this is actually a very specific thing (non null exception propogration or not )

@martinbonnin
Copy link
Contributor Author

@bbakerman makes a lot of sense, this is very similar to @defer 👍 .

I pushed some changes here. Let me know what you think.

Re: naming, it's hard 😅 . I went with ENABLE_CUSTOM_ERROR_HANDLING but no strong opinion there. If anyone has an idea, please feel free to share.

@bbakerman
Copy link
Member

Thanks for the update and removing the SchemaGenerator code.

I think the final part here is the naming.

Right now its

enum OnError {
              ALLOW_NULL
              PROPAGATE
            }
directive @errorHandling(onError: OnError) on QUERY | MUTATION | SUBSCRIPTION

My problem with the name is errorHandling and onError is very generic - eg it sounds a little bit more like perhaps "all the error handling" on this operation. Whereas in truth its a very specific bit of error handling - it prevents the propagation of exceptions when a non null type field returns null.

I think we should name this directive more specifically about this "non null type field returning null" behavior.

Naming is hard - lets go shopping!!

Here are my suggestions (with a matching directive argument and enum type)

  • onNonNullFieldReturningNull
  • onNonNullFieldContractFailure
  • onFieldContractFailure
  • onContractFailure

The last one is my favourite name because we could extend it for further contract failures in the future (but probably not)

enum NonNullFieldIsNull {
              ALLOW_NULL
              PROPAGATE
            }

directive @onContractFailure(nonNullFieldIsNull: NonNullFieldIsNull) on QUERY | MUTATION | SUBSCRIPTION

Thoughts?

@andimarek / @dondonz @martinbonnin

@martinbonnin
Copy link
Contributor Author

martinbonnin commented Dec 15, 2024

Thanks for the feedback!

Re: naming, my initial reaction is that this directive is indeed a very generic thing. It's about how to represent field errors for the whole operation and is not restricted to non-null positions. For an example, we could have this:

enum OnError {
  ALLOW_NULL // coerce to null
  PROPAGATE // set nearest nullable position to null
  THROW // stop processing the operation at the first field error (and save CPU cycles if the client does not handle partial data)
}

It's not only about "non-null returning null". It's also about "non-null raising an error" and possibly "any field raises an error".

I could suggest maybe something like:

directive @onFieldError(action: ErrorAction) on ...

enum ErrorAction {
  ALLOW_NULL
  PROPAGATE
  THROW
}

Not much different but at least it focuses on field errors.

PS: I just realized it's not "field errors" actually but more something like "position errors" because you could have an error in a list without setting the field to null but the whole spec uses "field error" for "position error" so I'd stick to "field error for consistency.

@martinbonnin
Copy link
Contributor Author

martinbonnin commented Dec 16, 2024

For some more background there, the initial discussions mentioned a single directive @noBubblesPlz (half-joking name :) but at least everyone got that).

This single directive had the advantage of simplicity. The possibility to have the server abort an operation completely when an error is encountered and therefore have a 3rd THROW case is what motivated using an enum.

For the sake of this experimental feature, we can maybe limit the scope and keep things simple with a single @doNotPropagateErrors directive?

query GetFoo @doNotPropagateErrors {
  foo
}

@dondonz
Copy link
Member

dondonz commented Dec 17, 2024

Naming is truly the hardest thing 😄

Only speaking for myself here, I would like to see a specific name.

For example for other GraphQL built-in directives we're more specific with directive names:

  • It's @defer and @stream rather than @incrementalDelivery with an enum to toggle
  • It's @skip and @include rather than @conditionalField with an enum to toggle

As someone who didn't already know about the specification PR, at first glance I was a little confused by @errorHandling as the name. Being a general term I wasn't sure about the intended behaviour straight away, and I was confused because there already is a default for error handling in the spec. Having a specific name makes it clearer the behaviour will be different to the default.

I see on the spec PR, the tentative name of the directive is @nullOnError, I think that's better because it's more specific and it's short. Other options on that PR description are also specific and could work: @disableErrorPropagation, @disableNullPropagation. There may be a set of directives as there are options for different behaviours

I'm mindful you've probably thought long and hard about the name already, I don't want to gatecrash this naming discussion! This is only my personal view. You're the spec champions and you are in a better spot to make a decision.

@martinbonnin
Copy link
Contributor Author

martinbonnin commented Dec 17, 2024

@dondonz makes a lot of sense.

I'm down with @nullOnError 👍 The simple solution is very often a good solution.

I'm mindful you've probably thought long and hard about the name already

TBH, I've gone through so many naming cycles that I'm not sure of anything anymore 😄 Your perspective is very refreshing. This does not accomodate the future evolution of adding a 3rd enum to save CPU cycles and stop processing at the first error but WAPGNI (We Aren't Probably Gonna Need It!)

I'll make the changes a bit later today. Done in 2c5cac1

@martinbonnin martinbonnin changed the title Add @errorHandling Add @nullOnError Dec 17, 2024
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