Skip to content

Commit 3cee7aa

Browse files
authored
Subscriptions: Final edits (#392)
* Subscriptions: Final edits This does a last pass over the subscriptions specification to clean up some ambiguities and crisp up some language. Two important things to take note of: 1. The `CreateSourceEventStream` algo was underspecified and did not articulate how fields should be collected from the selection set. This makes it clear that `CollectFields` is used, just like any other selection set. It also did not describe behavior when executing an invalid query, this adds that a query-level error should be thrown. 2. The Subscription Operation validation is similarly underspecified, and does not describe what should happen if a fragment spread is found as the first selection in a selection set. This clarifies that it is *fields* that exactly one is expected, rather than selections. * Add note about GetOperation()
1 parent e57b1cd commit 3cee7aa

File tree

3 files changed

+45
-17
lines changed

3 files changed

+45
-17
lines changed

spec/Section 3 -- Type System.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,11 +1019,11 @@ must *not* be queried if either the `@skip` condition is true *or* the
10191019
A GraphQL schema includes types, indicating where query, mutation, and
10201020
subscription operations start. This provides the initial entry points into the
10211021
type system. The query type must always be provided, and is an Object
1022-
base type. The mutation type is optional; if it is null, that means
1022+
base type. The mutation type is optional; if it is not provided, that means
10231023
the system does not support mutations. If it is provided, it must
10241024
be an object base type. Similarly, the subscription type is optional; if it is
1025-
null, the system does not support subscriptions. If it is provided, it must be
1026-
an object base type.
1025+
not provided, the system does not support subscriptions. If it is provided, it
1026+
must be an object base type.
10271027

10281028
The fields on the query type indicate what fields are available at
10291029
the top level of a GraphQL query. For example, a basic GraphQL query

spec/Section 5 -- Validation.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,12 @@ query getName {
205205
**Formal Specification**
206206

207207
* For each subscription operation definition {subscription} in the document
208-
* Let {rootFields} be the top level selection set on {subscription}.
209-
* {rootFields} must be a set of one.
208+
* Let {subscriptionType} be the root Subscription type in {schema}.
209+
* Let {selectionSet} be the top level selection set on {subscription}.
210+
* Let {variableValues} be the empty set.
211+
* Let {groupedFieldSet} be the result of
212+
{CollectFields(subscriptionType, selectionSet, variableValues)}.
213+
* {groupedFieldSet} must have exactly one entry.
210214

211215
**Explanatory Text**
212216

@@ -224,14 +228,14 @@ subscription sub {
224228
```
225229

226230
```graphql example
227-
fragment newMessageFields on Message {
228-
body
229-
sender
231+
subscription sub {
232+
...newMessageFields
230233
}
231234

232-
subscription sub {
235+
fragment newMessageFields on Subscription {
233236
newMessage {
234-
... newMessageFields
237+
body
238+
sender
235239
}
236240
}
237241
```
@@ -248,6 +252,20 @@ subscription sub {
248252
}
249253
```
250254

255+
```graphql counter-example
256+
subscription sub {
257+
...multipleSubscriptions
258+
}
259+
260+
fragment multipleSubscriptions on Subscription {
261+
newMessage {
262+
body
263+
sender
264+
}
265+
disallowedSecondRootField
266+
}
267+
```
268+
251269
Introspection fields are counted. The following example is also invalid:
252270

253271
```graphql counter-example
@@ -260,6 +278,11 @@ subscription sub {
260278
}
261279
```
262280

281+
Note: While each subscription must have exactly one root field, a document may
282+
contain any number of operations, each of which may contain different root
283+
fields. When executed, a document containing multiple subscription operations
284+
must provide the operation name as described in {GetOperation()}.
285+
263286
## Fields
264287

265288
### Field Selections on Objects, Interfaces, and Unions Types

spec/Section 6 -- Execution.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Note: This algorithm is very similar to {CoerceArgumentValues()}.
104104

105105
## Executing Operations
106106

107-
The type system, as described in the Type System section of the spec, must
107+
The type system, as described in the "Type System" section of the spec, must
108108
provide a query root object type. If mutations or subscriptions are supported,
109109
it must also provide a mutation or subscription root object type, respectively.
110110

@@ -212,7 +212,7 @@ must receive no more events from that event stream.
212212

213213
**Supporting Subscriptions at Scale**
214214

215-
Supporting subscriptions is a significant change for any GraphQL server. Query
215+
Supporting subscriptions is a significant change for any GraphQL service. Query
216216
and mutation operations are stateless, allowing scaling via cloning of GraphQL
217217
server instances. Subscriptions, by contrast, are stateful and require
218218
maintaining the GraphQL document, variables, and other context over the lifetime
@@ -233,10 +233,15 @@ CreateSourceEventStream(subscription, schema, variableValues, initialValue):
233233

234234
* Let {subscriptionType} be the root Subscription type in {schema}.
235235
* Assert: {subscriptionType} is an Object type.
236-
* Let {selectionSet} be the top level Selection Set in {subscription}.
237-
* Let {rootField} be the first top level field in {selectionSet}.
238-
* Let {argumentValues} be the result of {CoerceArgumentValues(subscriptionType, rootField, variableValues)}.
239-
* Let {fieldStream} be the result of running {ResolveFieldEventStream(subscriptionType, initialValue, rootField, argumentValues)}.
236+
* Let {groupedFieldSet} be the result of
237+
{CollectFields(subscriptionType, selectionSet, variableValues)}.
238+
* If {groupedFieldSet} does not have exactly one entry, throw a query error.
239+
* Let {fields} be the value of the first entry in {groupedFieldSet}.
240+
* Let {fieldName} be the name of the first entry in {fields}.
241+
Note: This value is unaffected if an alias is used.
242+
* Let {field} be the first entry in {fields}.
243+
* Let {argumentValues} be the result of {CoerceArgumentValues(subscriptionType, field, variableValues)}
244+
* Let {fieldStream} be the result of running {ResolveFieldEventStream(subscriptionType, initialValue, fieldName, argumentValues)}.
240245
* Return {fieldStream}.
241246

242247
ResolveFieldEventStream(subscriptionType, rootValue, fieldName, argumentValues):
@@ -284,7 +289,7 @@ payloads for a subscription. This may in turn also cancel the Source Stream.
284289
This is also a good opportunity to clean up any other resources used by
285290
the subscription.
286291

287-
Unsubscribe(responseStream)
292+
Unsubscribe(responseStream):
288293

289294
* Cancel {responseStream}
290295

0 commit comments

Comments
 (0)