@@ -258,16 +258,74 @@ query getName {
258258- Let {subscriptionType} be the root Subscription type in {schema}.
259259- For each subscription operation definition {subscription} in the document:
260260 - Let {selectionSet} be the top level selection set on {subscription}.
261- - Let {variableValues} be the empty set.
262- - Let {groupedFieldSet} be the result of {CollectFields(subscriptionType,
263- selectionSet, variableValues)}.
261+ - Let {groupedFieldSet} be the result of
262+ {CollectSubscriptionFields(subscriptionType, selectionSet)}.
264263 - {groupedFieldSet} must have exactly one entry, which must not be an
265264 introspection field.
266265
266+ CollectSubscriptionFields(objectType, selectionSet, visitedFragments):
267+
268+ - If {visitedFragments} is not provided, initialize it to the empty set.
269+ - Initialize {groupedFields} to an empty ordered map of lists.
270+ - For each {selection} in {selectionSet}:
271+ - {selection} must not provide the ` @skip ` directive.
272+ - {selection} must not provide the ` @include ` directive.
273+ - If {selection} is a {Field}:
274+ - Let {responseKey} be the response key of {selection} (the alias if
275+ defined, otherwise the field name).
276+ - Let {groupForResponseKey} be the list in {groupedFields} for
277+ {responseKey}; if no such list exists, create it as an empty list.
278+ - Append {selection} to the {groupForResponseKey}.
279+ - If {selection} is a {FragmentSpread}:
280+ - Let {fragmentSpreadName} be the name of {selection}.
281+ - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
282+ {selection} in {selectionSet}.
283+ - Add {fragmentSpreadName} to {visitedFragments}.
284+ - Let {fragment} be the Fragment in the current Document whose name is
285+ {fragmentSpreadName}.
286+ - If no such {fragment} exists, continue with the next {selection} in
287+ {selectionSet}.
288+ - Let {fragmentType} be the type condition on {fragment}.
289+ - If {DoesFragmentTypeApply(objectType, fragmentType)} is {false}, continue
290+ with the next {selection} in {selectionSet}.
291+ - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
292+ - Let {fragmentGroupedFieldSet} be the result of calling
293+ {CollectSubscriptionFields(objectType, fragmentSelectionSet,
294+ visitedFragments)}.
295+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
296+ - Let {responseKey} be the response key shared by all fields in
297+ {fragmentGroup}.
298+ - Let {groupForResponseKey} be the list in {groupedFields} for
299+ {responseKey}; if no such list exists, create it as an empty list.
300+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
301+ - If {selection} is an {InlineFragment}:
302+ - Let {fragmentType} be the type condition on {selection}.
303+ - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
304+ fragmentType)} is {false}, continue with the next {selection} in
305+ {selectionSet}.
306+ - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
307+ - Let {fragmentGroupedFieldSet} be the result of calling
308+ {CollectSubscriptionFields(objectType, fragmentSelectionSet,
309+ visitedFragments)}.
310+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
311+ - Let {responseKey} be the response key shared by all fields in
312+ {fragmentGroup}.
313+ - Let {groupForResponseKey} be the list in {groupedFields} for
314+ {responseKey}; if no such list exists, create it as an empty list.
315+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
316+ - Return {groupedFields}.
317+
318+ Note: This algorithm is very similar to {CollectFields()}, it differs in that it
319+ does not have access to runtime variables and thus the ` @skip ` and ` @include `
320+ directives cannot be used.
321+
267322** Explanatory Text**
268323
269324Subscription operations must have exactly one root field.
270325
326+ To enable us to determine this without access to runtime variables, we must
327+ forbid the ` @skip ` and ` @include ` directives in the root selection set.
328+
271329Valid examples:
272330
273331``` graphql example
@@ -318,6 +376,19 @@ fragment multipleSubscriptions on Subscription {
318376}
319377```
320378
379+ We do not allow the ` @skip ` and ` @include ` directives at the root of the
380+ subscription operation. The following example is also invalid:
381+
382+ ``` graphql counter-example
383+ subscription requiredRuntimeValidation ($bool : Boolean ! ) {
384+ newMessage @include (if : $bool ) {
385+ body
386+ sender
387+ }
388+ disallowedSecondRootField @skip (if : $bool )
389+ }
390+ ```
391+
321392The root field of a subscription operation must not be an introspection field.
322393The following example is also invalid:
323394
0 commit comments