-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix overlapping fields validator edge case #1497
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
base: next
Are you sure you want to change the base?
Conversation
…l-js into overlapping-fragment-issue
@@ -112,6 +112,8 @@ type ConflictReasonMessage = string | Array<ConflictReason>; | |||
type NodeAndDef = [GraphQLCompositeType, FieldNode, ?GraphQLField<*, *>]; | |||
// Map of array of those. | |||
type NodeAndDefCollection = ObjMap<Array<NodeAndDef>>; | |||
// Tuble defining a fragment spread (name, parentType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tuple?
…l-js into overlapping-fragment-issue
Not totally done yet: sub selections also need to check for fragment spreads inside different typed inline fragments. Still WIP |
} | ||
|
||
fragment X on Pet { | ||
name(surname: true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example currently fails on the Facebook GraphQL server (which does not depend on graphql-js for validation).
I would need to think about this example and whether it can be valid. I'm not sure whether it's a bug in both graphql-js
and the Facebook server, or if it's intended behavior.
This is a very interesting problem! There is a difficult tradeoff between complexity and correctness here. It is actually really difficult to decide whether two fields are "mutually exclusive" in the general case. The current implementation (which is also codified in the spec: http://facebook.github.io/graphql/June2018/#sec-Field-Selection-Merging) handles a few simple, well defined cases, but intentionally punts on anything more complicated. Your proposed implementation handles a few more cases, but still, as far as I can tell, doesn't completely solve the general case. Here are some examples to demonstrate: Example 1 (from the spec). These fields are correctly recognized as mutually exclusive by both implementations.
Example 2 (from your test case). These fields are correctly recognized by your implementation but not the current one.
Example 3 (the fun one). I don't think even your implementation would currently be able to tell that these fields are mutually exclusive (but please correct me if I'm wrong -- I don't have a graphql-js checkout ready so I didn't actually check).
You can probably imagine all kinds of more complicated combinations of nested fragments. So I think we have a few options here:
|
Thanks to both of you for looking into it and the context behind this validator. Let me take a look at some of these examples and come back with more info. The reason I'm here is because this case is currently handled in graphql-ruby, but the other unhandled cases and performance concerns are important to me too, so good point! 👍 |
Here are some notes in case you're considering a more general solution: The current (intentional) limitations are codified by this line in the algorithm description from the spec:
This assumes that each field has a single "parent type", which is assumed to be the type of its immediate parent fragment/field (so in Solving the general case requires changing this so that we either:
Each of these can have complicated interactions with the memoization used in the code (which is critical for keeping the time complexity of GraphQL validation linear in the size of the query). Currently the memoization relies on the fact that each field AST node has a single unchanging parent type, which would no longer be true because now what we consider "parent type" for the same field changes throughout the validation. |
I wrote about static analysis of queries (https://www.graphql.de/blog/static-query-analysis/) which allows for the detection of these kind of advances type problems. It is implemented here: https://github.com/andimarek/graphql-analyzer. If this kind of analysis should happen as part of the validation is another question and comes with tradeoff mentioned by @jjergus. |
Test shows a case where the validator should pass, but currently fails.
This is because we flatten inline fragments, and fragment names at the the selection level, but lose the context that they are mutually exclusive because of the inline fragment condition. Trying to come up with a fix at the moment.
The fix is to carry the current parent type for fragment spreads, and use this to compute the
isMutuallyExclusive
condition instead of defaulting to false.The main changes is that we now need to carry more than the
fragmentNames
, sofragmentNames
becomesfragmentSpreads
, a tuple of(FragmentName, ParentType)
.