Skip to content

Commit 27408f8

Browse files
committed
Prevent @Skip and @include on root subscription selection set
1 parent 6c81ed8 commit 27408f8

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

spec/Section 5 -- Validation.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,22 @@ query getName {
249249

250250
**Formal Specification**
251251

252-
* For each subscription operation definition {subscription} in the document
253-
* Let {subscriptionType} be the root Subscription type in {schema}.
254-
* Let {selectionSet} be the top level selection set on {subscription}.
255-
* Let {variableValues} be the empty set.
256-
* Let {groupedFieldSet} be the result of
257-
{CollectFields(subscriptionType, selectionSet, variableValues)}.
258-
* {groupedFieldSet} must have exactly one entry, which must not be an
259-
introspection field.
252+
* For each subscription operation definition {subscription} in the document:
253+
* Let {subscriptionType} be the root Subscription type in {schema}.
254+
* Let {selectionSet} be the top level selection set on {subscription}.
255+
* Let {groupedFieldSet} be the result of
256+
{CollectFields(subscriptionType, selectionSet, null)}.
257+
* {groupedFieldSet} must have exactly one entry, which must not be an
258+
introspection field.
260259

261260
**Explanatory Text**
262261

263262
Subscription operations must have exactly one root field.
264263

264+
To enable us to determine this without access to runtime variables, we must
265+
forbid the `@skip` and `@include` directives in the root selection set (by
266+
passing `null` as the `variableValues` argument to {CollectFields()}).
267+
265268
Valid examples:
266269

267270
```graphql example
@@ -312,6 +315,19 @@ fragment multipleSubscriptions on Subscription {
312315
}
313316
```
314317

318+
We do not allow the `@skip` and `@include` directives at the root of the
319+
subscription operation. The following example is also invalid:
320+
321+
```graphql counter-example
322+
subscription requiredRuntimeValidation($bool: Boolean!) {
323+
newMessage @include(if: $bool) {
324+
body
325+
sender
326+
}
327+
disallowedSecondRootField @skip(if: $bool)
328+
}
329+
```
330+
315331
The root field of a subscription operation must not be an introspection field.
316332
The following example is also invalid:
317333

spec/Section 6 -- Execution.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -479,17 +479,28 @@ The depth-first-search order of the field groups produced by {CollectFields()}
479479
is maintained through execution, ensuring that fields appear in the executed
480480
response in a stable and predictable order.
481481

482+
When {CollectFields()} is used during validation (see for example the
483+
[single root field](#sec-Single-root-field) subscription operation validation
484+
rule), the runtime value for {variableValues} will not be available - in this
485+
case we set {variableValues} to {null} and forbid the use of the `@skip` and
486+
`@include` directives. During execution, {variableValues} will always be
487+
non-null.
488+
482489
CollectFields(objectType, selectionSet, variableValues, visitedFragments):
483490

484491
* If {visitedFragments} is not provided, initialize it to the empty set.
485492
* Initialize {groupedFields} to an empty ordered map of lists.
486493
* For each {selection} in {selectionSet}:
487-
* If {selection} provides the directive `@skip`, let {skipDirective} be that directive.
488-
* If {skipDirective}'s {if} argument is {true} or is a variable in {variableValues} with the value {true}, continue with the next
489-
{selection} in {selectionSet}.
490-
* If {selection} provides the directive `@include`, let {includeDirective} be that directive.
491-
* If {includeDirective}'s {if} argument is not {true} and is not a variable in {variableValues} with the value {true}, continue with the next
492-
{selection} in {selectionSet}.
494+
* If {variableValues} is {null}:
495+
* {selection} must not provide the `@skip` directive.
496+
* {selection} must not provide the `@include` directive.
497+
* Otherwise:
498+
* If {selection} provides the directive `@skip`, let {skipDirective} be that directive.
499+
* If {skipDirective}'s {if} argument is {true} or is a variable in {variableValues} with the value {true}, continue with the next
500+
{selection} in {selectionSet}.
501+
* If {selection} provides the directive `@include`, let {includeDirective} be that directive.
502+
* If {includeDirective}'s {if} argument is not {true} and is not a variable in {variableValues} with the value {true}, continue with the next
503+
{selection} in {selectionSet}.
493504
* If {selection} is a {Field}:
494505
* Let {responseKey} be the response key of {selection} (the alias if defined, otherwise the field name).
495506
* Let {groupForResponseKey} be the list in {groupedFields} for

0 commit comments

Comments
 (0)