Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Excellent catch! I think we can and should fast track this. Once you have the GraphQL.js implementation in place either add it to the next GraphQL WG agenda or let me know if you’d rather I represent it on your behalf. |
benjie
left a comment
There was a problem hiding this comment.
Let's change the named type to an assertion for clarity
spec/Section 3 -- Type System.md
Outdated
| - Let {namedFieldType} be the underlying named type of {fieldType}. | ||
| - If {namedFieldType} is not an Input Object type: | ||
| - Return {true}. | ||
| - If {namedFieldType} is not a _OneOf Input Object_: | ||
| - Return {true}. | ||
| - If {OneOfInputObjectCanBeProvidedAFiniteValue(namedFieldType, nextVisited)}: | ||
| - Return {true}. |
There was a problem hiding this comment.
| - Let {namedFieldType} be the underlying named type of {fieldType}. | |
| - If {namedFieldType} is not an Input Object type: | |
| - Return {true}. | |
| - If {namedFieldType} is not a _OneOf Input Object_: | |
| - Return {true}. | |
| - If {OneOfInputObjectCanBeProvidedAFiniteValue(namedFieldType, nextVisited)}: | |
| - Return {true}. | |
| - Assert: {fieldType} is a named type. | |
| - If {fieldType} is not an Input Object type: | |
| - Return {true}. | |
| - If {fieldType} is not a _OneOf Input Object_ type: | |
| - Return {true}. | |
| - If {OneOfInputObjectCanBeProvidedAFiniteValue(fieldType, nextVisited)}: | |
| - Return {true}. |
viaduct.arbitrary.graphql is a suite of generators used to property test Viaduct and other internal airbnb systems. The generators in this package use a model for GraphQL values, which can be applied to GraphQL objects that require them. Examples of this include the default values for a graphql argument definition, the arguments/variables used in a GraphQL document, or the result of executing a selection set. This value model has evolved over time -- the initial `RawValue` family of types has been gradually replaced with an `IR.Value` model. Currently, both models exist in the code base and are used for slightly different jobs. This PR completely removes the `RawValue` model and updates all systems to use `IR.Value`. Along the way it also updates most components to operate on a ViaductSchema rather than graphql-java's GraphQLSchema. **Better Edgecase Coverage** Before this change, the schema generator would assign default values and applied directives as it generated each type. This created complexity in other parts of the system (eg value generators had to know how to not generate a value for a type that hadn't been defined yet), and made it impossible for the system to generate some kinds of edge cases. This PR changes this behavior to insert default values and applied directives as additional passes, after all types have been generated. This simplifies value generation and allows for more kinds of edge cases to be generated. This ability to generate more edge cases has surfaced some novel issues, such as this potential spec issue with OneOf types: graphql/graphql-spec#1211 Github-Change-Id: 998226 GitOrigin-RevId: 6ae10823b358faf51f883d593d33d6767933ee8b
reframe the issue as as general class of input object habitation, not just a special edge case around pure OneOf trees. This allows handling the case of mixed OneOf/non-OneOf types, like: ```graphql input A @OneOf { b: B } input B { a: A! } ``` To support this, I've also rewritten the previously-proposed `OneOfInputObjectCanBeProvidedAFiniteValue` function as `InputObjectCanBeProvidedAFiniteValue`, which handles both pure inputs, pure OneOfs, and mixed input/OneOfs validation.
|
@benjie after looking at this more, I realized that my previous proposal only dealt with closed OneOf trees, but that one can also define uninhabited types by mixing OneOf and non-OneOfs. For example, this cycle is considered valid by both the current spec and my original proposal: input A @oneOf { b:B }
input B { a:A! }I've updated this PR to fold the overly-specific OneOf rules I'd originally written into the Cyclic References section, capturing the general shape of the problem for both flavors of input objects. This is implemented in graphql-js PR graphql/graphql-js#4564 I was going to plan on being at the graphql-wg call on March 5th, though with the slightly wider scope of this PR I'd be fine to catch a different meeting. |
benjie
left a comment
There was a problem hiding this comment.
Great work! For Non-Null type we normally say "Let {innerType} be the inner type of {...}" or "Let {nullableType} be the unwrapped nullable type of", but these are just editorial nits really - the structure looks correct to me.
| - Let {innerType} be the type wrapped by {fieldType}. | ||
| - Return {FieldTypeCanBeProvidedAFiniteValue(innerType, visited)}. | ||
| - Assert: {fieldType} is a named type. | ||
| - If {fieldType} is not an Input Object type: |
There was a problem hiding this comment.
Not sure if we need this added for clarity... To make it clear that OneOf Input Objects are included?
| - If {fieldType} is not an Input Object type: | |
| - If {fieldType} is not an Input Object type (including variants): |
There was a problem hiding this comment.
I think this has the potential to add more confusion than it clarifies. "Input Object type", without an 'all variants' qualifier, is used consistently to describe OneOf and non-OneOf types. Adding "(including variants)" only here might make readers wonder whether the unqualified uses elsewhere are intentionally exclusive.
That said, you know the spec's editorial voice better than I do, so happy to add it if you think it helps.
There was a problem hiding this comment.
Nah, I also don’t think it should be there, but I think it might warrant discussion.
spec/Section 3 -- Type System.md
Outdated
|
|
||
| Because _OneOf Input Objects_ require exactly one field to be set and non-null, | ||
| nullable fields do not allow a finite value to be provided as they do in other | ||
| Input Objects. This example is invalid because providing a value for `First` |
There was a problem hiding this comment.
I understand the issue and the counter-example below, but am having trouble parsing/understanding the meaning of this sentence "nullable fields do not allow a finite value to be provided as they do in other Input Objects". Maybe it could be rephrased or maybe it's just me :)
There was a problem hiding this comment.
That's fair -- I was trying to describe that while a nullable field would normally provide an escape from infinite recursion, the field-value-may-not-be-null requirement of a OneOf prevents using this escape.
spec/Section 3 -- Type System.md
Outdated
| Because _OneOf Input Objects_ require exactly one field to be set and non-null, | ||
| nullable fields do not allow a finite value to be provided as they do in other | ||
| Input Objects. This example is invalid because providing a value for `First` | ||
| requires a non-null `Second`, and constructing a `Second` requires a non-null | ||
| `First`: |
There was a problem hiding this comment.
Agree with @BoD; perhaps:
| Because _OneOf Input Objects_ require exactly one field to be set and non-null, | |
| nullable fields do not allow a finite value to be provided as they do in other | |
| Input Objects. This example is invalid because providing a value for `First` | |
| requires a non-null `Second`, and constructing a `Second` requires a non-null | |
| `First`: | |
| _OneOf Input Object_ require exactly one field be provided, and that field | |
| cannot be `null`. This example is invalid because providing a value for `First` | |
| requires a non-null `Second`, and constructing a `Second` requires a non-null | |
| `First`: |
There was a problem hiding this comment.
Thanks @benjie !
In applying this change, I re-read the nearby Circular References section and saw some opportunities to simplify:
- Counter Example No 84 uses a different order of
value/selffields from other examples - The
valuefield in these examples isn't discussed in the prose and isn't material to this section.
My inclination is to simplify these examples by removing the value fields, which I think would reduce some noise in these examples. Would you support including this change in this PR?
As a counter-argument, the value fields make these examples more realistic, rather than degenerate examples of recursion. Keeping them implies that the example I added around OneOf should probably also have them.
There was a problem hiding this comment.
I have no preference either way on the end result (that's more a question for @leebyron) but if you want to rework the existing examples please do so via a separate PR not this one.
There was a problem hiding this comment.
Scratch that I do have a preference: leave the existing circular references with the value since it shows its nullability doesn't matter to the problem (there's no branching). Leave your current examples with only one field, because they demonstrate the problem clearly without confusing the reader with combinatorics (because there is branching depending on which field you choose).
Changing the order of value/self in example 84 to be consistent would be a fine separate editorial PR if you were so inclined.
There was a problem hiding this comment.
Great! I spun this out into another PR at #1214
OneOf types can form graphs for which finite values cannot be created.
The simplest example of this is a self-recursing OneOf type:
While this is the simplest form, OneOfs can also be combined with non-OneOf input object types to create other uninhabited types:
These kinds of types are considered valid on two fronts today:
While this follows the letter of the Circular References part of the spec, it violates the spirit of the spec in that there is no way to create a finite, legal value for these types. I propose that these topologies be declared invalid.
I took a stab at reworking the Circular References section to consider OneOfs and added a more formal specification framed around finite values.