diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index 92f222cb3..945e60751 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -43,7 +43,14 @@ Token :: - FloatValue - StringValue -Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } +Punctuator :: + +- DotPunctuator +- OtherPunctuator + +DotPunctuator :: `.` [lookahead != {`.`, Digit}] + +OtherPunctuator :: one of ! $ & ( ) ... : = @ [ ] { | } Name :: @@ -412,3 +419,21 @@ 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 : ) diff --git a/spec/Section 2 -- Language.md b/spec/Section 2 -- Language.md index c163ace99..81316f0cc 100644 --- a/spec/Section 2 -- Language.md +++ b/spec/Section 2 -- Language.md @@ -176,12 +176,25 @@ and is {Ignored}. ### Punctuators -Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } +Punctuator :: + +- DotPunctuator +- OtherPunctuator + +DotPunctuator :: `.` [lookahead != {`.`, Digit}] + +OtherPunctuator :: one of ! $ & ( ) ... : = @ [ ] { | } GraphQL documents include punctuation in order to describe structure. GraphQL is a data description language and not a programming language, therefore GraphQL lacks the punctuation often used to describe mathematical expressions. +The {`.`} punctuator must not be followed by a {`.`} or {Digit}. This ensures +that the source {"..."} can only be interpreted as a single {`...`} and not +three {`.`}. It also avoids any potential ambiguity with {FloatValue}. As an +example the source {".123"} has no valid lexical representation (without this +restriction it would have been interpreted as {`.`} followed by {IntValue}). + ### Names Name :: diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index a3914d158..b85993e04 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2168,3 +2168,157 @@ 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 : ) + +:: 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. + +:: The _containing element_ of a _schema element_ is the schema element with one +fewer {Name} token that syntactically contains it. Specifically: + +- {MemberCoordinate} has a {TypeCoordinate} containing element. +- {ArgumentCoordinate} has a {MemberCoordinate} containing element. +- {DirectiveArgumentCoordinate} has a {DirectiveCoordinate} containing element. +- {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. Meta-fields may also be +referenced. For example, `Business.__typename` is a valid schema coordinate. + +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. + +**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}. + +TypeCoordinate : Name + +1. Let {typeName} be the value of {Name}. +2. Return the type in the {schema} named {typeName}, or {null} if no such type + exists. + +MemberCoordinate : Name . Name + +1. Let {typeName} be the value of the first {Name}. +2. Let {type} be the type in the {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}, or {null} if no + such value 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}, or {null} if no + such input field exists. +6. Otherwise: + 1. Let {fieldName} be the value of the second {Name}. + 2. Return the field of {type} named {fieldName}, or {null} if no such field + exists. + +ArgumentCoordinate : Name . Name ( Name : ) + +1. Let {typeName} be the value of the first {Name}. +2. Let {type} be the type in the {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}, or {null} if no + such argument exists. + +DirectiveCoordinate : @ Name + +1. Let {directiveName} be the value of {Name}. +2. Return the directive in the {schema} named {directiveName}, or {null} if no + such directive exists. + +DirectiveArgumentCoordinate : @ Name ( Name : ) + +1. Let {directiveName} be the value of the first {Name}. +2. Let {directive} be the directive in the {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}, or {null} + if no such argument 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 +```