diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index e75a0f5d4..9ee79584c 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -413,3 +413,24 @@ TypeSystemDirectiveLocation : one of - `ENUM_VALUE` - `INPUT_OBJECT` - `INPUT_FIELD_DEFINITION` + +SchemaCoordinate : + +- TypeCoordinate +- MemberCoordinate +- ArgumentCoordinate +- DirectiveCoordinate +- DirectiveArgumentCoordinate + +TypeCoordinate : Name + +MemberCoordinate : Name . Name + +ArgumentCoordinate : Name . Name ( Name : ) + +DirectiveCoordinate : @ Name + +DirectiveArgumentCoordinate : @ Name ( Name : ) + +Note: This grammar is unique. A _schema coordinate_ may not include any other +tokens. diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 8400233fa..f5ebc9191 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2227,3 +2227,172 @@ to the relevant IETF specification. ```graphql example scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") ``` + +## Schema Coordinates + +SchemaCoordinate : + +- TypeCoordinate +- MemberCoordinate +- ArgumentCoordinate +- DirectiveCoordinate +- DirectiveArgumentCoordinate + +TypeCoordinate : Name + +MemberCoordinate : Name . Name + +ArgumentCoordinate : Name . Name ( Name : ) + +DirectiveCoordinate : @ Name + +DirectiveArgumentCoordinate : @ Name ( Name : ) + +Note: A _schema coordinate_ is defined with its own grammar and may not include +any other tokens. + +:: A _schema coordinate_ is a human readable string that uniquely identifies a +_schema element_ within a GraphQL Schema. + +:: A _schema element_ can be a named type, a field, an input field, an enum +value, a field argument, a directive, or a directive argument defined within a +schema (including built-in types and directives). + +Note: Meta-fields are not defined within a schema, and thus are not _schema +element_. By extension, an introspection type is not a _schema element_. + +:: The _containing element_ of a _schema element_ is the schema element with one +fewer {Name} token that syntactically contains it. Specifically: + +- The containing element of an {ArgumentCoordinate} is a {MemberCoordinate}. +- The containing element of a {MemberCoordinate} is a {TypeCoordinate}. +- The containing element of a {DirectiveArgumentCoordinate} is a + {DirectiveCoordinate}. +- {TypeCoordinate} and {DirectiveCoordinate} have no containing element. + +A _schema coordinate_ is always unique. Each _schema element_ can be referenced +by exactly one possible schema coordinate. + +A _schema coordinate_ may refer to either a defined or built-in _schema +element_. For example, `String` and `@deprecated(reason:)` are both valid schema +coordinates which refer to built-in schema elements. + +Note: Union members are not valid _schema coordinate_ as they reference existing +types in the schema. This preserves the uniqueness property of a _schema +coordinate_ as stated above. + +Note: A {SchemaCoordinate} is not a definition within a GraphQL {Document}, but +a separate standalone grammar, intended to be used by tools to reference types, +fields, and other _schema element_. Examples include: references within +documentation to refer to types and fields in a schema, a lookup key that can be +used in logging tools to track how often particular fields are queried in +production. + +**Lexical Analysis & Syntactic Parse of a Schema Coordinate** + +The source text of a _schema coordinate_ is processed in the same way as that of +a GraphQL document, as laid out in the [Language](#sec-Language) section, with +the exception that only the subset of tokens as described in the grammar above +may be considered. A schema coordinate must therefore not contain whitespace, +line terminators, comments, commas, or a _Byte Order Mark_. This constraint +ensures that schema coordinates are both unambiguous and unique. + +**Resolving a Schema Coordinate** + +To refer to a _schema element_, a _schema coordinate_ must be interpreted in the +context of a GraphQL {schema}. + +If the _schema element_ cannot be found, the resolve function will not yield a +value (without raising an error). However, an error will be raised if any +non-leaf nodes within a _schema coordinate_ cannot be found in the {schema}. + +Note: Although it is syntactically possible to describe a meta-field or element +of the introspection schema with a schema coordinate (e.g. `Business.__typename` +or `__Type.fields(includeDeprecated:)`), they are not _schema element_ and +therefore resolving such coordinates results in implementation-defined behavior. + +TypeCoordinate : Name + +1. Let {typeName} be the value of {Name}. +2. Return the type in {schema} named {typeName} if it exists. + +MemberCoordinate : Name . Name + +1. Let {typeName} be the value of the first {Name}. +2. Let {type} be the type in {schema} named {typeName}. +3. Assert: {type} must exist, and must be an Enum, Input Object, Object or + Interface type. +4. If {type} is an Enum type: + 1. Let {enumValueName} be the value of the second {Name}. + 2. Return the enum value of {type} named {enumValueName} if it exists. +5. Otherwise, if {type} is an Input Object type: + 1. Let {inputFieldName} be the value of the second {Name}. + 2. Return the input field of {type} named {inputFieldName} if it exists. +6. Otherwise: + 1. Let {fieldName} be the value of the second {Name}. + 2. Return the field of {type} named {fieldName} if it exists. + +ArgumentCoordinate : Name . Name ( Name : ) + +1. Let {typeName} be the value of the first {Name}. +2. Let {type} be the type in {schema} named {typeName}. +3. Assert: {type} must exist, and be an Object or Interface type. +4. Let {fieldName} be the value of the second {Name}. +5. Let {field} be the field of {type} named {fieldName}. +6. Assert: {field} must exist. +7. Let {fieldArgumentName} be the value of the third {Name}. +8. Return the argument of {field} named {fieldArgumentName} if it exists. + +DirectiveCoordinate : @ Name + +1. Let {directiveName} be the value of {Name}. +2. Return the directive in {schema} named {directiveName} if it exists. + +DirectiveArgumentCoordinate : @ Name ( Name : ) + +1. Let {directiveName} be the value of the first {Name}. +2. Let {directive} be the directive in {schema} named {directiveName}. +3. Assert: {directive} must exist. +4. Let {directiveArgumentName} be the value of the second {Name}. +5. Return the argument of {directive} named {directiveArgumentName} if it + exists. + +**Examples** + +| Element Kind | _Schema Coordinate_ | _Schema Element_ | +| ------------------ | --------------------------------- | --------------------------------------------------------------------- | +| Named Type | `Business` | `Business` type | +| Field | `Business.name` | `name` field on the `Business` type | +| Input Field | `SearchCriteria.filter` | `filter` input field on the `SearchCriteria` input object type | +| Enum Value | `SearchFilter.OPEN_NOW` | `OPEN_NOW` value of the `SearchFilter` enum | +| Field Argument | `Query.searchBusiness(criteria:)` | `criteria` argument on the `searchBusiness` field on the `Query` type | +| Directive | `@private` | `@private` directive | +| Directive Argument | `@private(scope:)` | `scope` argument on the `@private` directive | + +The table above shows an example of a _schema coordinate_ for every kind of +_schema element_ based on the schema below. + +```graphql +type Query { + searchBusiness(criteria: SearchCriteria!): [Business] +} + +input SearchCriteria { + name: String + filter: SearchFilter +} + +enum SearchFilter { + OPEN_NOW + DELIVERS_TAKEOUT + VEGETARIAN_MENU +} + +type Business { + id: ID + name: String + email: String @private(scope: "loggedIn") +} + +directive @private(scope: String!) on FIELD_DEFINITION +```