Skip to content

Commit d481d17

Browse files
committed
RFC: Return type overlap validation
This alters the "Overlapping Fields Can Be Merged" validation rule to better express return type validation issues. The existing rule has presented some false-positives in some schema where interfaces with co-variant types are commonly used. The "same return type" check doesn't remove any ambiguity. Instead what that "same return type" check is attempting to prevent is spreading two fragments (or inline fragments) which have fields with return types where ambiguity would be introduced in the response. In order to curb false-positives, we later changed this rule such that if two fields were known to never apply simultaneously, then we would skip the remainder of the rule. ``` { ... on Person { foo: fullName } ... on Pet { foo: petName } } ``` However this can introduce false-negatives! ``` { ... on Person { foo: birthday { bar: year } } ... on Business { foo: location { bar: street } } } ``` In the above example, `data.foo.bar` could be of type `Int` or type `String`, it's ambiguous! This differing return type breaks some client model implementations (Fragment models) in addition to just being confusing. For example this invalid query: ``` fragment A on Type { field: someIntField } fragment B on Type { ...A field: someStringField } ``` Might produce the following illegal Java artifacts: ``` interface A { int getField() } interface B implements A { string getField() } ```
1 parent de9074f commit d481d17

File tree

1 file changed

+42
-5
lines changed

1 file changed

+42
-5
lines changed

spec/Section 5 -- Validation.md

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,37 @@ FieldsInSetCanMerge(set) :
286286
* Let {fieldsForName} be the set of selections with a given response name in
287287
{set} including visiting fragments and inline fragments.
288288
* Given each pair of members {fieldA} and {fieldB} in {fieldsForName}:
289+
* {SameResponseShape(fieldA, fieldB)} must be true.
289290
* If the parent types of {fieldA} and {fieldB} are equal or if either is not
290291
an Object Type:
291292
* {fieldA} and {fieldB} must have identical field names.
292-
* {fieldA} and {fieldB} must have identical return type.
293293
* {fieldA} and {fieldB} must have identical sets of arguments.
294294
* Let {mergedSet} be the result of adding the selection set of {fieldA}
295295
and the selection set of {fieldB}.
296296
* {FieldsInSetCanMerge(mergedSet)} must be true.
297297

298+
SameResponseShape(fieldA, fieldB) :
299+
* Let {typeA} be the return type of {fieldA}.
300+
* Let {typeB} be the return type of {fieldB}.
301+
* If {typeA} or {typeB} is Non-Null.
302+
* {typeA} and {typeB} must both be Non-Null.
303+
* Let {typeA} be the nullable type of {typeA}
304+
* Let {typeB} be the nullable type of {typeB}
305+
* If {typeA} or {typeB} is List.
306+
* {typeA} and {typeB} must both be List.
307+
* Let {typeA} be the item type of {typeA}
308+
* Let {typeB} be the item type of {typeB}
309+
* Repeat from step 3.
310+
* If {typeA} or {typeB} is Scalar or Enum.
311+
* {typeA} and {typeB} must be the same type.
312+
* Assert: {typeA} and {typeB} are both composite types.
313+
* Let {mergedSet} be the result of adding the selection set of {fieldA} and
314+
the selection set of {fieldB}.
315+
* Let {fieldsForName} be the set of selections with a given response name in
316+
{mergedSet} including visiting fragments and inline fragments.
317+
* Given each pair of members {subfieldA} and {subfieldB} in {fieldsForName}:
318+
* {SameResponseShape(subfieldA, subfieldB)} must be true.
319+
298320
** Explanatory Text **
299321

300322
If multiple fields selections with the same response names are encountered
@@ -369,16 +391,16 @@ fragment differingArgs on Dog {
369391
}
370392
```
371393

372-
The following would not merge together, however both cannot be encountered
373-
against the same object:
394+
The following fields would not merge together, however both cannot be
395+
encountered against the same object, so they are safe:
374396

375397
```graphql
376398
fragment safeDifferingFields on Pet {
377399
... on Dog {
378-
name: nickname
400+
volume: barkVolume
379401
}
380402
... on Cat {
381-
name
403+
volume: meowVolume
382404
}
383405
}
384406

@@ -392,6 +414,21 @@ fragment safeDifferingArgs on Pet {
392414
}
393415
```
394416

417+
However, the field responses must be shapes which can be merged. For example,
418+
scalar values must not differ. In this example, `someValue` might be a `String`
419+
or an `Int`:
420+
421+
```!graphql
422+
fragment conflictingDifferingResponses on Pet {
423+
... on Dog {
424+
someValue: nickname
425+
}
426+
... on Cat {
427+
someValue: meowVolume
428+
}
429+
}
430+
```
431+
395432

396433
### Leaf Field Selections
397434

0 commit comments

Comments
 (0)