Skip to content

Commit

Permalink
[RFC] Proposed change to directive location introspection
Browse files Browse the repository at this point in the history
This proposes a change to how we represent the ability to validate the locations of directives via introspection.

Specifically, this *removes* `onField`, `onFragment`, and `onOperation` in favor of `locations` which is a list of `__DirectiveLocation`.

In addition, this changes the specification of the `@skip` and `@include` directives to no longer allow their use on fragment directives (but are still allowed on fragment spreads and inline fragments). Because of this, a section on "merging fragment directives" is no longer useful.

**Rationale:**

This allows for a more fine-grained validation of directive placement, now you can assert that a directive is allowed on queries but not mutations, or allowed on fragment definitions but not on fragment spreads.

Also, this makes expanding the locations a directive is allowed to be placed easier to do, as future expansions will not affect the introspection API. This should be considered a prereq to supporting directives in more locations.

Finally, this is a prereq to a forthcoming RFC to add directives to the type schema language, one of the last missing pieces to represent a full schema using this language.

**Drawbacks:**

Any change to the introspection API is a challenge. Tools like Graph*i*QL should continue to support the older representation if possible.
  • Loading branch information
leebyron committed Mar 19, 2016
1 parent 582eb32 commit 1c38e6a
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 48 deletions.
41 changes: 0 additions & 41 deletions spec/Section 2 -- Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -909,44 +909,3 @@ and operations.

As future versions of GraphQL adopts new configurable execution capabilities,
they may be exposed via directives.

#### Fragment Directives

Fragments may include directives to alter their behavior. At runtime, the directives provided on a fragment spread override those described on the
definition.

For example, the following query:

```graphql
query hasConditionalFragment($condition: Boolean) {
...maybeFragment @include(if: $condition)
}

fragment maybeFragment on Query {
me {
name
}
}
```

Will have identical runtime behavior as

```graphql
query hasConditionalFragment($condition: Boolean) {
...maybeFragment
}

fragment maybeFragment on Query @include(if: $condition) {
me {
name
}
}
```

FragmentSpreadDirectives(fragmentSpread) :
* Let {directives} be the set of directives on {fragmentSpread}
* Let {fragmentDefinition} be the FragmentDefinition in the document named {fragmentSpread} refers to.
* For each {directive} in directives on {fragmentDefinition}
* If {directives} does not contain a directive named {directive}
* Add {directive} into {directives}
* Return {directives}
10 changes: 6 additions & 4 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -767,8 +767,9 @@ GraphQL implementations should provide the `@skip` and `@include` directives.

### @skip

The `@skip` directive may be provided for fields or fragments, and allows
for conditional exclusion during execution as described by the if argument.
The `@skip` directive may be provided for fields, fragment spreads, and
inline fragments, and allows for conditional exclusion during execution as
described by the if argument.

In this example `experimentalField` will be queried only if the `$someTest` is
provided a `false` value.
Expand All @@ -781,8 +782,9 @@ query myQuery($someTest: Boolean) {

### @include

The `@include` directive may be provided for fields or fragments, and allows
for conditional inclusion during execution as described by the if argument.
The `@include` directive may be provided for fields, fragment spreads, and
inline fragments, and allows for conditional inclusion during execution as
described by the if argument.

In this example `experimentalField` will be queried only if the `$someTest` is
provided a `true` value.
Expand Down
28 changes: 25 additions & 3 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,18 @@ enum __TypeKind {
type __Directive {
name: String!
description: String
locations: [__DirectiveLocation!]!
args: [__InputValue!]!
onOperation: Boolean!
onFragment: Boolean!
onField: Boolean!
}
enum __DirectiveLocation {
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
}
```

Expand Down Expand Up @@ -375,3 +383,17 @@ Fields
* `defaultValue` may return a String encoding (using the GraphQL language) the
default value used by this input value in the condition a value is not
provided at runtime. If this input value has no default value, returns {null}.


### The __Directive Type

The `__Directive` type represents a Directive that a server supports.

Fields

* `name` must return a String
* `description` may return a String or {null}
* `locations` returns a List of `__DirectiveLocation` representing the valid
locations this directive may be placed.
* `args` returns a List of `__InputValue` representing the arguments this
directive accepts.
28 changes: 28 additions & 0 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,34 @@ For example the following query will not pass validation.
GraphQL servers define what directives they support. For each
usage of a directive, the directive must be available on that server.


### Directives Are In Valid Locations

** Formal Specification **

* For every {directive} in a document.
* Let {directiveName} be the name of {directive}.
* Let {directiveDefinition} be the directive named {directiveName}.
* Let {locations} be the valid locations for {directiveDefinition}.
* Let {adjacent} be the AST node the directive effects.
* {adjacent} must be represented by an item within {locations}.

** Explanatory Text **

GraphQL servers define what directives they support and where they support them.
For each usage of a directive, the directive must be used in a location that the
server has declared support for.

For example the following query will not pass validation because `@skip` does
not provide `QUERY` as a valid location.

```!graphql
query @skip(if: $foo) {
field
}
```


## Variables

### Variable Uniqueness
Expand Down

0 comments on commit 1c38e6a

Please sign in to comment.