Skip to content

Commit 2a62aa2

Browse files
committed
Add defer/stream validation rules
1 parent 0c815d9 commit 2a62aa2

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

spec/Section 5 -- Validation.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ FieldsInSetCanMerge(set):
422422
{set} including visiting fragments and inline fragments.
423423
- Given each pair of members {fieldA} and {fieldB} in {fieldsForName}:
424424
- {SameResponseShape(fieldA, fieldB)} must be true.
425+
- {SameStreamDirective(fieldA, fieldB)} must be true.
425426
- If the parent types of {fieldA} and {fieldB} are equal or if either is not
426427
an Object Type:
427428
- {fieldA} and {fieldB} must have identical field names.
@@ -456,6 +457,16 @@ SameResponseShape(fieldA, fieldB):
456457
- If {SameResponseShape(subfieldA, subfieldB)} is {false}, return {false}.
457458
- Return {true}.
458459

460+
SameStreamDirective(fieldA, fieldB):
461+
462+
- If neither {fieldA} nor {fieldB} has a directive named `stream`.
463+
- Return {true}.
464+
- If both {fieldA} and {fieldB} have a directive named `stream`.
465+
- Let {streamA} be the directive named `stream` on {fieldA}.
466+
- Let {streamB} be the directive named `stream` on {fieldB}.
467+
- If {streamA} and {streamB} have identical sets of arguments, return {true}.
468+
- Return {false}.
469+
459470
Note: In prior versions of the spec the term "composite" was used to signal a
460471
type that is either an Object, Interface or Union type.
461472

@@ -1521,6 +1532,174 @@ query ($foo: Boolean = true, $bar: Boolean = false) {
15211532
}
15221533
```
15231534

1535+
### Defer And Stream Directives Are Used On Valid Root Field
1536+
1537+
** Formal Specification **
1538+
1539+
- For every {directive} in a document.
1540+
- Let {directiveName} be the name of {directive}.
1541+
- Let {mutationType} be the root Mutation type in {schema}.
1542+
- Let {subscriptionType} be the root Subscription type in {schema}.
1543+
- If {directiveName} is "defer" or "stream":
1544+
- The parent type of {directive} must not be {mutationType} or
1545+
{subscriptionType}.
1546+
1547+
**Explanatory Text**
1548+
1549+
The defer and stream directives are not allowed to be used on root fields of the
1550+
mutation or subscription type.
1551+
1552+
For example, the following document will not pass validation because `@defer`
1553+
has been used on a root mutation field:
1554+
1555+
```raw graphql counter-example
1556+
mutation {
1557+
... @defer {
1558+
mutationField
1559+
}
1560+
}
1561+
```
1562+
1563+
### Defer And Stream Directives Are Used On Valid Operations
1564+
1565+
** Formal Specification **
1566+
1567+
- Let {subscriptionFragments} be the empty set.
1568+
- For each {operation} in a document:
1569+
- If {operation} is a subscription operation:
1570+
- Let {fragments} be every fragment referenced by that {operation}
1571+
transitively.
1572+
- For each {fragment} in {fragments}:
1573+
- Let {fragmentName} be the name of {fragment}.
1574+
- Add {fragmentName} to {subscriptionFragments}.
1575+
- For every {directive} in a document:
1576+
- If {directiveName} is not "defer" or "stream":
1577+
- Continue to the next {directive}.
1578+
- Let {ancestor} be the ancestor operation or fragment definition of
1579+
{directive}.
1580+
- If {ancestor} is a fragment definition:
1581+
- If the fragment name of {ancestor} is not present in
1582+
{subscriptionFragments}:
1583+
- Continue to the next {directive}.
1584+
- If {ancestor} is not a subscription operation:
1585+
- Continue to the next {directive}.
1586+
- Let {if} be the argument named "if" on {directive}.
1587+
- {if} must be defined.
1588+
- Let {argumentValue} be the value passed to {if}.
1589+
- {argumentValue} must be a variable, or the boolean value "false".
1590+
1591+
**Explanatory Text**
1592+
1593+
The defer and stream directives can not be used to defer or stream data in
1594+
subscription operations. If these directives appear in a subscription operation
1595+
they must be disabled using the "if" argument. This rule will not permit any
1596+
defer or stream directives on a subscription operation that cannot be disabled
1597+
using the "if" argument.
1598+
1599+
For example, the following document will not pass validation because `@defer`
1600+
has been used in a subscription operation with no "if" argument defined:
1601+
1602+
```raw graphql counter-example
1603+
subscription sub {
1604+
newMessage {
1605+
... @defer {
1606+
body
1607+
}
1608+
}
1609+
}
1610+
```
1611+
1612+
### Defer And Stream Directive Labels Are Unique
1613+
1614+
** Formal Specification **
1615+
1616+
- Let {labelValues} be an empty set.
1617+
- For every {directive} in the document:
1618+
- Let {directiveName} be the name of {directive}.
1619+
- If {directiveName} is "defer" or "stream":
1620+
- For every {argument} in {directive}:
1621+
- Let {argumentName} be the name of {argument}.
1622+
- Let {argumentValue} be the value passed to {argument}.
1623+
- If {argumentName} is "label":
1624+
- {argumentValue} must not be a variable.
1625+
- {argumentValue} must not be present in {labelValues}.
1626+
- Append {argumentValue} to {labelValues}.
1627+
1628+
**Explanatory Text**
1629+
1630+
The `@defer` and `@stream` directives each accept an argument "label". This
1631+
label may be used by GraphQL clients to uniquely identify response payloads. If
1632+
a label is passed, it must not be a variable and it must be unique within all
1633+
other `@defer` and `@stream` directives in the document.
1634+
1635+
For example the following document is valid:
1636+
1637+
```graphql example
1638+
{
1639+
dog {
1640+
...fragmentOne
1641+
...fragmentTwo @defer(label: "dogDefer")
1642+
}
1643+
pets @stream(label: "petStream") {
1644+
name
1645+
}
1646+
}
1647+
1648+
fragment fragmentOne on Dog {
1649+
name
1650+
}
1651+
1652+
fragment fragmentTwo on Dog {
1653+
owner {
1654+
name
1655+
}
1656+
}
1657+
```
1658+
1659+
For example, the following document will not pass validation because the same
1660+
label is used in different `@defer` and `@stream` directives.:
1661+
1662+
```raw graphql counter-example
1663+
{
1664+
dog {
1665+
...fragmentOne @defer(label: "MyLabel")
1666+
}
1667+
pets @stream(label: "MyLabel") {
1668+
name
1669+
}
1670+
}
1671+
1672+
fragment fragmentOne on Dog {
1673+
name
1674+
}
1675+
```
1676+
1677+
### Stream Directives Are Used On List Fields
1678+
1679+
**Formal Specification**
1680+
1681+
- For every {directive} in a document.
1682+
- Let {directiveName} be the name of {directive}.
1683+
- If {directiveName} is "stream":
1684+
- Let {adjacent} be the AST node the directive affects.
1685+
- {adjacent} must be a List type.
1686+
1687+
**Explanatory Text**
1688+
1689+
GraphQL directive locations do not provide enough granularity to distinguish the
1690+
type of fields used in a GraphQL document. Since the stream directive is only
1691+
valid on list fields, an additional validation rule must be used to ensure it is
1692+
used correctly.
1693+
1694+
For example, the following document will only pass validation if `field` is
1695+
defined as a List type in the associated schema.
1696+
1697+
```graphql counter-example
1698+
query {
1699+
field @stream(initialCount: 0)
1700+
}
1701+
```
1702+
15241703
## Variables
15251704

15261705
### Variable Uniqueness

0 commit comments

Comments
 (0)