From 4a2aadcbf8a3cf762bd8ed161c41fc36a79bd4e3 Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Wed, 1 Nov 2017 18:12:24 -0400 Subject: [PATCH 01/13] update introspection and validation --- spec/Section 3 -- Type System.md | 79 +++++++++++++++++++++++++----- spec/Section 4 -- Introspection.md | 3 +- spec/Section 5 -- Validation.md | 8 +-- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 9677a17f4..e24f07fae 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -249,9 +249,10 @@ type in the system, allowing the definition of arbitrary type hierarchies. GraphQL supports two abstract types: interfaces and unions. -An `Interface` defines a list of fields; `Object` types that implement that -interface are guaranteed to implement those fields. Whenever the type system -claims it will return an interface, it will return a valid implementing type. +An `Interface` defines a list of fields; `Object` types and other Interface +types that implement the interface are guaranteed to implement those fields. +Whenever the type system claims it will return an interface, it will return a +valid implementing `Object` type. A `Union` defines a list of possible types; similar to interfaces, whenever the type system claims a union will be returned, one of the possible types will be @@ -804,8 +805,8 @@ of rules must be adhered to by every Object type in a GraphQL schema. characters {"__"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. -4. An object type may declare that it implements one or more unique interfaces. -5. An object type must be a super-set of all interfaces it implements: +3. An object type may declare that it implements one or more unique interfaces. +4. An object type must be a super-set of all interfaces it implements: 1. The object type must include a field of the same name for every field defined in an interface. 1. The object field must be of a type which is equal to or a sub-type of @@ -816,11 +817,15 @@ of rules must be adhered to by every Object type in a GraphQL schema. the interface field type is either an Interface type or a Union type and the object field type is a possible type of the interface field type. - 3. An object field type is a valid sub-type if it is a List type and + 3. An object field type is a valid sub-type if it is an Interface type + and the interface field type is either an Interface type or a Union + type and the object field type is a possible type of the interface + field type. + 4. An object field type is a valid sub-type if it is a List type and the interface field type is also a List type and the list-item type of the object field type is a valid sub-type of the list-item type of the interface field type. - 4. An object field type is a valid sub-type if it is a Non-Null variant + 5. An object field type is a valid sub-type if it is a Non-Null variant of a valid sub-type of the interface field type. 2. The object field must include an argument of the same name for every argument defined in the interface field. @@ -947,8 +952,8 @@ Object type extensions have the potential to be invalid if incorrectly defined. InterfaceTypeDefinition : Description? interface Name Directives[Const]? FieldsDefinition? GraphQL interfaces represent a list of named fields and their arguments. GraphQL -objects can then implement these interfaces which requires that the object type -will define all fields defined by those interfaces. +objects and interfaces can then implement these interfaces which requires that +the object type will define all fields defined by those interfaces. Fields on a GraphQL interface have the same rules as fields on a GraphQL object; their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping @@ -1038,6 +1043,22 @@ interface. Querying for `age` is only valid when the result of `entity` is a } ``` +When defining an interface that implements another interface, the implementing +interface must define each field that is specified by the implemented interface. +For example, the interface Resource must define the field id to implement the +Node interface: + +```graphql example +interface Node { + id: ID! +} + +interface Resource implements Node { + id: ID! + url: String +} +``` + **Result Coercion** The interface type should have some way of determining which object a given @@ -1065,6 +1086,38 @@ Interface types have the potential to be invalid if incorrectly defined. characters {"__"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. +3. An interface type may declare that it implements one or more unique + interfaces, but may not implement itself. +4. An interface type must be a super-set of all interfaces it implements: + 1. The implementing interface type must include a field of the same name for + every field defined in an implemented interface. + 1. The implementing interface field must be of a type which is equal to or + a sub-type of the implemented interface field (covariant). + 1. An implementing interface field type is a valid sub-type if it is + equal to (the same type as) the implemented interface field type. + 2. An implementing interface field type is a valid sub-type if it is an + Object type and the implemented interface field type is either an + Interface type or a Union type and the implementing interface field + type is a possible type of the implemented interface field type. + 3. An implementing interface field type is a valid sub-type if it is an + Interface type and the implemented interface field type is either an + Interface type or a Union type and the implementing interface field + type is a possible type of the implemented interface field type. + 4. An implementing interface field type is a valid sub-type if it is a + List type and the implemented interface field type is also a List + type and the list-item type of the implementing interface field type + is a valid sub-type of the list-item type of the implemented + interface field type. + 5. An implementing interface field type is a valid sub-type if it is a + Non-Null variant of a valid sub-type of the implemented interface + field type. + 2. The implementing interface field must include an argument of the same + name for every argument defined in the implemented interface field. + 1. The implementing interface field argument must accept the same type + (invariant) as the implemented interface field argument. + 3. The implementing interface field may include additional arguments not + defined in the implemented interface field, but any additional argument + must not be required. ### Interface Extensions @@ -1114,10 +1167,12 @@ Interface type extensions have the potential to be invalid if incorrectly define fields may share the same name. 3. Any fields of an Interface type extension must not be already defined on the original Interface type. -4. Any Object type which implemented the original Interface type must also be a - super-set of the fields of the Interface type extension (which may be due to - Object type extension). +4. Any Object or Interface type which implemented the original Interface type + must also be a super-set of the fields of the Interface type extension (which + may be due to Object type extension). 5. Any directives provided must not already apply to the original Interface type. +6. The resulting extended interface type must be a super-set of all interfaces + it implements. ## Unions diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 8a24e2882..43d53df39 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -131,8 +131,6 @@ type __Type { # OBJECT and INTERFACE only fields(includeDeprecated: Boolean = false): [__Field!] - - # OBJECT only interfaces: [__Type!] # INTERFACE and UNION only @@ -291,6 +289,7 @@ Fields * `fields`: The set of fields required by this interface. * Accepts the argument `includeDeprecated` which defaults to {false}. If {true}, deprecated fields are also returned. +* `interfaces`: The set of interfaces that an interface implements. * `possibleTypes` returns the list of types that implement this interface. They must be object types. * All other fields must return {null}. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index d16debb79..691294d1e 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1131,8 +1131,8 @@ fragment catInDogFragmentInvalid on Dog { ##### Abstract Spreads in Object Scope -In scope of an object type, unions or interface spreads can be used -if the object type implements the interface or is a member of the union. +In scope of an object or interface type, unions or interface spreads can be +used if the type implements the interface or is a member of the union. For example @@ -1172,8 +1172,8 @@ declaration, not its body. ##### Object Spreads In Abstract Scope Union or interface spreads can be used within the context of an object type -fragment, but only if the object type is one of the possible types of -that interface or union. +fragment or interface type fragment, but only if the type is one of the +possible types of that interface or union. For example, the following fragments are valid: From 06f012315a07c3c0c1cc3143d56f45c36e74d11d Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Tue, 23 Jul 2019 14:50:35 -0700 Subject: [PATCH 02/13] Add language to require declaring transitive interfaces --- spec/Section 3 -- Type System.md | 57 ++++++++++++++++++++++++-------- spec/Section 5 -- Validation.md | 4 +-- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index d3dcb92b3..6ee7aa1ca 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -814,13 +814,11 @@ of rules must be adhered to by every Object type in a GraphQL schema. 1. An object field type is a valid sub-type if it is equal to (the same type as) the interface field type. 2. An object field type is a valid sub-type if it is an Object type and - the interface field type is either an Interface type or a Union type - and the object field type is a possible type of the interface field - type. - 3. An object field type is a valid sub-type if it is an Interface type - and the interface field type is either an Interface type or a Union - type and the object field type is a possible type of the interface - field type. + the interface field type is a Union type and the object field type + is a possible type of the interface field type. + 3. An object field type is a valid sub-type if it is an Object or + Interface type and the interface field type is an Interface type and + the object field type implements the interface field type. 4. An object field type is a valid sub-type if it is a List type and the interface field type is also a List type and the list-item type of the object field type is a valid sub-type of the list-item type @@ -834,6 +832,9 @@ of rules must be adhered to by every Object type in a GraphQL schema. 3. The object field may include additional arguments not defined in the interface field, but any additional argument must not be required, e.g. must not be of a non-nullable type. +5. If an object type declares that it implements one or more interfaces that + implement other interfaces, it must also include each transitively + implemented interface in its list of implemented interfaces. ### Field Arguments @@ -953,7 +954,7 @@ InterfaceTypeDefinition : Description? interface Name Directives[Const]? FieldsD GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires that -the object type will define all fields defined by those interfaces. +the implementing type will define all fields defined by those interfaces. Fields on a GraphQL interface have the same rules as fields on a GraphQL object; their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping @@ -1043,6 +1044,8 @@ interface. Querying for `age` is only valid when the result of `entity` is a } ``` +**Interfaces Implementing Interfaces** + When defining an interface that implements another interface, the implementing interface must define each field that is specified by the implemented interface. For example, the interface Resource must define the field id to implement the @@ -1059,6 +1062,29 @@ interface Resource implements Node { } ``` +Transitively implemented interfaces (interfaces implemented by the interface +that is being implemented) must also be defined on an implementing type or +interface. For example, `Image` cannot implement `Resource` without also +implementing `Node`: + +```graphql example +interface Node { + id: ID! +} + +interface Resource implements Node { + id: ID! + url: String +} + +interface Image implements Resource & Node { + id: ID! + url: String + thumbnail: String +} +``` + + **Result Coercion** The interface type should have some way of determining which object a given @@ -1096,13 +1122,13 @@ Interface types have the potential to be invalid if incorrectly defined. 1. An implementing interface field type is a valid sub-type if it is equal to (the same type as) the implemented interface field type. 2. An implementing interface field type is a valid sub-type if it is an - Object type and the implemented interface field type is either an - Interface type or a Union type and the implementing interface field - type is a possible type of the implemented interface field type. + Object type and the implemented interface field type is a Union type + and the implementing interface field type is a possible type of the + implemented interface field type. 3. An implementing interface field type is a valid sub-type if it is an - Interface type and the implemented interface field type is either an - Interface type or a Union type and the implementing interface field - type is a possible type of the implemented interface field type. + Object or Interface type and the implemented interface field type is + an Interface type and the implementing interface field type + implements the implemented interface field type. 4. An implementing interface field type is a valid sub-type if it is a List type and the implemented interface field type is also a List type and the list-item type of the implementing interface field type @@ -1118,6 +1144,9 @@ Interface types have the potential to be invalid if incorrectly defined. 3. The implementing interface field may include additional arguments not defined in the implemented interface field, but any additional argument must not be required. +5. If an interface type declares that it implements one or more interfaces that + implement other interfaces, it must also include each transitively + implemented interface in its list of implemented interfaces. ### Interface Extensions diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 00ba447fa..805cb453b 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1131,8 +1131,8 @@ fragment catInDogFragmentInvalid on Dog { ##### Abstract Spreads in Object Scope -In scope of an object or interface type, unions or interface spreads can be -used if the type implements the interface or is a member of the union. +In scope of an object type, unions or interface spreads can be used +if the type implements the interface or is a member of the union. For example From 1e662560b434035d1543b7133177ab8e5ec3de35 Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Sun, 15 Sep 2019 10:20:23 -0700 Subject: [PATCH 03/13] Forbid cyclic references --- spec/Section 3 -- Type System.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 6ee7aa1ca..2e7d7598f 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1084,6 +1084,21 @@ interface Image implements Resource & Node { } ``` +Interfaces definitions must not contain cyclic references. This example is +invalid because Node and Named implement each other. + +```graphgl counter-example +interface Node implements Named { + id: ID! + name: String +} + +interface Named implementes Node { + id: ID! + name: String +} +``` + **Result Coercion** @@ -1147,6 +1162,8 @@ Interface types have the potential to be invalid if incorrectly defined. 5. If an interface type declares that it implements one or more interfaces that implement other interfaces, it must also include each transitively implemented interface in its list of implemented interfaces. +6. An interface cannot implement an interface that in turn implements the + original interface. ### Interface Extensions From 90e523367befa0adf50f1031ceb7f40377a1e7db Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Sun, 15 Sep 2019 10:46:02 -0700 Subject: [PATCH 04/13] Fix small typos --- spec/Section 3 -- Type System.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 2e7d7598f..236fe97ee 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1084,8 +1084,8 @@ interface Image implements Resource & Node { } ``` -Interfaces definitions must not contain cyclic references. This example is -invalid because Node and Named implement each other. +Interface definitions must not contain cyclic references. This example is +invalid because `Node` and `Named` implement each other. ```graphgl counter-example interface Node implements Named { From e84cad01041a1c58f5b46f46cbf1128288a24bb2 Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Mon, 16 Sep 2019 09:28:57 -0700 Subject: [PATCH 05/13] fix another typo --- spec/Section 3 -- Type System.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 236fe97ee..1ea82432a 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1093,7 +1093,7 @@ interface Node implements Named { name: String } -interface Named implementes Node { +interface Named implements Node { id: ID! name: String } From 707848baa9dfd25f0de4a53f52fcb704e7fc9483 Mon Sep 17 00:00:00 2001 From: Mike Marcacci Date: Thu, 26 Sep 2019 11:51:10 -0700 Subject: [PATCH 06/13] Use simpler language for cycles --- spec/Section 3 -- Type System.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 1ea82432a..efea5867d 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1088,12 +1088,12 @@ Interface definitions must not contain cyclic references. This example is invalid because `Node` and `Named` implement each other. ```graphgl counter-example -interface Node implements Named { +interface Node implements Named & Node { id: ID! name: String } -interface Named implements Node { +interface Named implements Node & Named { id: ID! name: String } @@ -1162,8 +1162,7 @@ Interface types have the potential to be invalid if incorrectly defined. 5. If an interface type declares that it implements one or more interfaces that implement other interfaces, it must also include each transitively implemented interface in its list of implemented interfaces. -6. An interface cannot implement an interface that in turn implements the - original interface. +6. An interface cannot implement itself. ### Interface Extensions From ce97ee085945256eed87c9a08891e6e85f14a4a5 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 15:14:09 -0800 Subject: [PATCH 07/13] Apply suggestions from code review --- spec/Section 3 -- Type System.md | 10 +++++----- spec/Section 4 -- Introspection.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 173d142a3..e2b0c1bb5 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -251,9 +251,9 @@ type in the system, allowing the definition of arbitrary type hierarchies. GraphQL supports two abstract types: interfaces and unions. An `Interface` defines a list of fields; `Object` types and other Interface -types that implement the interface are guaranteed to implement those fields. -Whenever the type system claims it will return an interface, it will return a -valid implementing `Object` type. +types which implement this Interface are guaranteed to implement those fields. +Whenever a field claims it will return an Interface type, it will return a +valid implementing Object type during execution. A `Union` defines a list of possible types; similar to interfaces, whenever the type system claims a union will be returned, one of the possible types will be @@ -821,7 +821,7 @@ of rules must be adhered to by every Object type in a GraphQL schema. type as) the interface field type. 2. An object field type is a valid sub-type if it is an Object type and the interface field type is a Union type and the object field type - is a possible type of the interface field type. + is a possible type of that Union. 3. An object field type is a valid sub-type if it is an Object or Interface type and the interface field type is an Interface type and the object field type implements the interface field type. @@ -1222,7 +1222,7 @@ Interface type extensions have the potential to be invalid if incorrectly define 4. Any Object or Interface type which implemented the original Interface type must also be a super-set of the fields of the Interface type extension (which may be due to Object type extension). -5. Any non-repeatable directives provided must not already apply to the +5. Any non-repeatable directives provided must not already apply to the original Interface type. 6. The resulting extended Interface type must be a super-set of all Interfaces it implements. diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 5f0e86aed..d2f85c55c 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -292,7 +292,7 @@ Fields * `fields`: The set of fields required by this interface. * Accepts the argument `includeDeprecated` which defaults to {false}. If {true}, deprecated fields are also returned. -* `interfaces`: The set of interfaces that an interface implements. +* `interfaces`: The set of interfaces that this interface implements. * `possibleTypes` returns the list of types that implement this interface. They must be object types. * All other fields must return {null}. From 91c498348015d158d097f0d15a86b963f95f7724 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 15:20:22 -0800 Subject: [PATCH 08/13] Move implementing interfaces into "super-set of all interfaces it implements" --- spec/Section 3 -- Type System.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index e2b0c1bb5..67d0f9c67 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -813,7 +813,10 @@ of rules must be adhered to by every Object type in a GraphQL schema. returns {true}. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: - 1. The object type must include a field of the same name for every field + 1. If the interface type declares it implements any interfaces, the object + type must include those same interfaces in its list of + implemented interfaces. + 2. The object type must include a field of the same name for every field defined in an interface. 1. The object field must be of a type which is equal to or a sub-type of the interface field (covariant). @@ -838,9 +841,6 @@ of rules must be adhered to by every Object type in a GraphQL schema. 3. The object field may include additional arguments not defined in the interface field, but any additional argument must not be required, e.g. must not be of a non-nullable type. -5. If an object type declares that it implements one or more interfaces that - implement other interfaces, it must also include each transitively - implemented interface in its list of implemented interfaces. ### Field Arguments @@ -1137,7 +1137,10 @@ Interface types have the potential to be invalid if incorrectly defined. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. 4. An interface type must be a super-set of all interfaces it implements: - 1. The implementing interface type must include a field of the same name for + 1. If the implemented type declares it implements any interfaces, the + implementing type must include those same interfaces in its list of + implemented interfaces. + 2. The implementing interface type must include a field of the same name for every field defined in an implemented interface. 1. The implementing interface field must be of a type which is equal to or a sub-type of the implemented interface field (covariant). @@ -1166,10 +1169,6 @@ Interface types have the potential to be invalid if incorrectly defined. 3. The implementing interface field may include additional arguments not defined in the implemented interface field, but any additional argument must not be required. -5. If an interface type declares that it implements one or more interfaces that - implement other interfaces, it must also include each transitively - implemented interface in its list of implemented interfaces. -6. An interface cannot implement itself. ### Interface Extensions From 9be0ccfe16855fee3f78863df11c4814f4db6d03 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 16:05:46 -0800 Subject: [PATCH 09/13] factor implementation validity check into reusable algo --- spec/Section 3 -- Type System.md | 114 +++++++++++++++---------------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 67d0f9c67..3aa95c0a6 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -813,34 +813,57 @@ of rules must be adhered to by every Object type in a GraphQL schema. returns {true}. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: - 1. If the interface type declares it implements any interfaces, the object - type must include those same interfaces in its list of - implemented interfaces. - 2. The object type must include a field of the same name for every field - defined in an interface. - 1. The object field must be of a type which is equal to or a sub-type of - the interface field (covariant). - 1. An object field type is a valid sub-type if it is equal to (the same - type as) the interface field type. - 2. An object field type is a valid sub-type if it is an Object type and - the interface field type is a Union type and the object field type - is a possible type of that Union. - 3. An object field type is a valid sub-type if it is an Object or - Interface type and the interface field type is an Interface type and - the object field type implements the interface field type. - 4. An object field type is a valid sub-type if it is a List type and - the interface field type is also a List type and the list-item type - of the object field type is a valid sub-type of the list-item type - of the interface field type. - 5. An object field type is a valid sub-type if it is a Non-Null variant - of a valid sub-type of the interface field type. - 2. The object field must include an argument of the same name for every - argument defined in the interface field. - 1. The object field argument must accept the same type (invariant) as - the interface field argument. - 3. The object field may include additional arguments not defined in the - interface field, but any additional argument must not be required, e.g. - must not be of a non-nullable type. + 1. Let this object type be {objectType}. + 2. For each interface declared implemented as {interfaceType}, + {IsValidImplementingTypeOf(objectType, interfaceType)} must be {true}. + +IsValidImplementingTypeOf(implementingType, implementedType): + + 1. If {implementedType} declares it implements any interfaces, + {implementingType} must also declare it implements those interfaces. + 2. The {implementingType} must include a field of the same name for every field + defined in {implementedType}. + 1. The {implementingType} field must include an argument of the same name + for every argument defined in the {implementedType} field. + 1. The {implementingType} field argument must accept the same type + (invariant) as the {implementedType} field argument. + 2. The {implementingType} field may include additional arguments not + defined in the {implementingType} field, but any additional argument + must not be required, e.g. must not be of a non-nullable type. + 3. The {implementingType} field must return a type which is equal to or a + sub-type of the {implementedType} field's return type (covariant): + 1. Let {implementingFieldType} be the return type of the + {implementingType} field. + 2. Let {implementedFieldType} be the return type of the + {implementedType} field. + 3. {IsValidImplementingFieldTypeOf(implementingFieldType, implementedFieldType)} + must be {true}. + +IsValidImplementingFieldTypeOf(implementingFieldType, implementedFieldType): + 1. If {implementingFieldType} is a Non-Null type: + 1. Let {implementingNullableType} be the unwrapped nullable type + of {implementingFieldType}. + 2. Let {implementedNullableType} be the unwrapped nullable type + of {implementedFieldType} if it is a Non-Null type, otherwise let it be + {implementedFieldType} directly. + 3. Return {IsValidImplementingFieldTypeOf(implementingNullableType, implementedNullableType)}. + 2. Otherwise, if {implementedFieldType} is a Non-Null type, return {false}. + 3. If {implementingFieldType} is a List type and {implementedFieldType} is + also a List type: + 1. Let {implementingItemType} be the unwrapped item type + of {implementingFieldType}. + 2. Let {implementedItemType} be the unwrapped item type + of {implementedFieldType}. + 3. Return {IsValidImplementingFieldTypeOf(implementingItemType, implementedItemType)}. + 4. If {implementingFieldType} is the same type as {implementedFieldType} + then return {true}. + 5. If {implementingFieldType} is an Object type and {implementedFieldType} is + a Union type and {implementingFieldType} is a possible type of + {implementedFieldType} then return {true}. + 6. If {implementingFieldType} is an Object or Interface type and + {implementedFieldType} is an Interface type and {implementingFieldType} + declares it implements {implementedFieldType} then return {true}. + 7. Otherwise return {false}. ### Field Arguments @@ -1137,38 +1160,9 @@ Interface types have the potential to be invalid if incorrectly defined. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. 4. An interface type must be a super-set of all interfaces it implements: - 1. If the implemented type declares it implements any interfaces, the - implementing type must include those same interfaces in its list of - implemented interfaces. - 2. The implementing interface type must include a field of the same name for - every field defined in an implemented interface. - 1. The implementing interface field must be of a type which is equal to or - a sub-type of the implemented interface field (covariant). - 1. An implementing interface field type is a valid sub-type if it is - equal to (the same type as) the implemented interface field type. - 2. An implementing interface field type is a valid sub-type if it is an - Object type and the implemented interface field type is a Union type - and the implementing interface field type is a possible type of the - implemented interface field type. - 3. An implementing interface field type is a valid sub-type if it is an - Object or Interface type and the implemented interface field type is - an Interface type and the implementing interface field type - implements the implemented interface field type. - 4. An implementing interface field type is a valid sub-type if it is a - List type and the implemented interface field type is also a List - type and the list-item type of the implementing interface field type - is a valid sub-type of the list-item type of the implemented - interface field type. - 5. An implementing interface field type is a valid sub-type if it is a - Non-Null variant of a valid sub-type of the implemented interface - field type. - 2. The implementing interface field must include an argument of the same - name for every argument defined in the implemented interface field. - 1. The implementing interface field argument must accept the same type - (invariant) as the implemented interface field argument. - 3. The implementing interface field may include additional arguments not - defined in the implemented interface field, but any additional argument - must not be required. + 1. Let this interface type be {implementingType}. + 2. For each interface declared implemented as {implementedType}, + {IsValidImplementingTypeOf(interfaceType, implementedType)} must be {true}. ### Interface Extensions From c86a6fa5500f8682d4fc63b83f9a0d8964ab8493 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 16:28:49 -0800 Subject: [PATCH 10/13] Expand section on Validation with example of now-valid spread --- spec/Section 5 -- Validation.md | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index f48aeafdc..664ae2b28 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1133,7 +1133,7 @@ fragment catInDogFragmentInvalid on Dog { ##### Abstract Spreads in Object Scope In scope of an object type, unions or interface spreads can be used -if the type implements the interface or is a member of the union. +if the object type implements the interface or is a member of the union. For example @@ -1173,8 +1173,8 @@ declaration, not its body. ##### Object Spreads In Abstract Scope Union or interface spreads can be used within the context of an object type -fragment or interface type fragment, but only if the type is one of the -possible types of that interface or union. +fragment, but only if the object type is one of the possible types of +that interface or union. For example, the following fragments are valid: @@ -1258,6 +1258,34 @@ is not valid because there exists no type that implements both {Pet} and {Sentient}. +**Interface Spreads in implemented Interface Scope** + +Additionally, an interface type fragment can always be spread into an +interface scope which it implements. + +In the example below, the `...resourceFragment` fragments spreads is valid, +since `Resource` implements `Node`. + +```graphql example +interface Node { + id: ID! +} + +interface Resource implements Node { + id: ID! + url: String +} + +fragment interfaceWithInterface on Node { + ...resourceFragment +} + +fragment resourceFragment on Resource { + url +} +``` + + ## Values From 18ecc05b47815e167567ce72a80c1def6e1de84f Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 17:00:40 -0800 Subject: [PATCH 11/13] algorithm cleanups --- spec/Section 3 -- Type System.md | 69 +++++++++++++++----------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 3aa95c0a6..ee2dd3fdb 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -815,55 +815,50 @@ of rules must be adhered to by every Object type in a GraphQL schema. 4. An object type must be a super-set of all interfaces it implements: 1. Let this object type be {objectType}. 2. For each interface declared implemented as {interfaceType}, - {IsValidImplementingTypeOf(objectType, interfaceType)} must be {true}. + {IsValidImplementation(objectType, interfaceType)} must be {true}. -IsValidImplementingTypeOf(implementingType, implementedType): +IsValidImplementation(type, implementedType): 1. If {implementedType} declares it implements any interfaces, - {implementingType} must also declare it implements those interfaces. - 2. The {implementingType} must include a field of the same name for every field + {type} must also declare it implements those interfaces. + 2. {type} must include a field of the same name for every field defined in {implementedType}. - 1. The {implementingType} field must include an argument of the same name - for every argument defined in the {implementedType} field. - 1. The {implementingType} field argument must accept the same type - (invariant) as the {implementedType} field argument. - 2. The {implementingType} field may include additional arguments not - defined in the {implementingType} field, but any additional argument - must not be required, e.g. must not be of a non-nullable type. - 3. The {implementingType} field must return a type which is equal to or a - sub-type of the {implementedType} field's return type (covariant): - 1. Let {implementingFieldType} be the return type of the - {implementingType} field. - 2. Let {implementedFieldType} be the return type of the - {implementedType} field. - 3. {IsValidImplementingFieldTypeOf(implementingFieldType, implementedFieldType)} + 1. Let {field} be that named field on {type}. + 2. Let {implementedField} be that named field on {implementedType}. + 1. {field} must include an argument of the same name for every argument + defined in {implementedField}. + 1. That named argument on {field} must accept the same type + (invariant) as that named argument on {implementedField}. + 2. {field} may include additional arguments not defined in + {implementedField}, but any additional argument must not be required, + e.g. must not be of a non-nullable type. + 3. {field} must return a type which is equal to or a sub-type of + (covariant) the return type of {implementedField} field's return type: + 1. Let {fieldType} be the return type of {field}. + 2. Let {implementedFieldType} be the return type of {implementedField}. + 3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} must be {true}. -IsValidImplementingFieldTypeOf(implementingFieldType, implementedFieldType): - 1. If {implementingFieldType} is a Non-Null type: - 1. Let {implementingNullableType} be the unwrapped nullable type - of {implementingFieldType}. +IsValidImplementationFieldType(fieldType, implementedFieldType): + 1. If {fieldType} is a Non-Null type: + 1. Let {nullableType} be the unwrapped nullable type of {fieldType}. 2. Let {implementedNullableType} be the unwrapped nullable type of {implementedFieldType} if it is a Non-Null type, otherwise let it be {implementedFieldType} directly. - 3. Return {IsValidImplementingFieldTypeOf(implementingNullableType, implementedNullableType)}. - 2. Otherwise, if {implementedFieldType} is a Non-Null type, return {false}. - 3. If {implementingFieldType} is a List type and {implementedFieldType} is - also a List type: - 1. Let {implementingItemType} be the unwrapped item type - of {implementingFieldType}. + 3. Return {IsValidImplementationField(nullableType, implementedNullableType)}. + 2. If {fieldType} is a List type and {implementedFieldType} is also a List type: + 1. Let {itemType} be the unwrapped item type of {fieldType}. 2. Let {implementedItemType} be the unwrapped item type of {implementedFieldType}. - 3. Return {IsValidImplementingFieldTypeOf(implementingItemType, implementedItemType)}. - 4. If {implementingFieldType} is the same type as {implementedFieldType} + 3. Return {IsValidImplementationField(itemType, implementedItemType)}. + 3. If {fieldType} is the same type as {implementedFieldType} then return {true}. + 4. If {fieldType} is an Object type and {implementedFieldType} is + a Union type and {fieldType} is a possible type of {implementedFieldType} then return {true}. - 5. If {implementingFieldType} is an Object type and {implementedFieldType} is - a Union type and {implementingFieldType} is a possible type of + 5. If {fieldType} is an Object or Interface type and {implementedFieldType} + is an Interface type and {fieldType} declares it implements {implementedFieldType} then return {true}. - 6. If {implementingFieldType} is an Object or Interface type and - {implementedFieldType} is an Interface type and {implementingFieldType} - declares it implements {implementedFieldType} then return {true}. - 7. Otherwise return {false}. + 6. Otherwise return {false}. ### Field Arguments @@ -1162,7 +1157,7 @@ Interface types have the potential to be invalid if incorrectly defined. 4. An interface type must be a super-set of all interfaces it implements: 1. Let this interface type be {implementingType}. 2. For each interface declared implemented as {implementedType}, - {IsValidImplementingTypeOf(interfaceType, implementedType)} must be {true}. + {IsValidImplementation(implementingType, implementedType)} must be {true}. ### Interface Extensions From 8bf0a3e5a3cb530275bef296f543e023943d6166 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 17:04:31 -0800 Subject: [PATCH 12/13] Add explaining note about not implementing itself --- spec/Section 3 -- Type System.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index ee2dd3fdb..18097a75c 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1109,8 +1109,9 @@ interface Image implements Resource & Node { } ``` -Interface definitions must not contain cyclic references. This example is -invalid because `Node` and `Named` implement each other. +Interface definitions must not contain cyclic references nor implement +themselves. This example is invalid because `Node` and `Named` implement +themselves and each other: ```graphgl counter-example interface Node implements Named & Node { From db7b7e119f8f23b3033ec70da482f2894f045f3f Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Fri, 10 Jan 2020 17:06:27 -0800 Subject: [PATCH 13/13] Fix broken reference --- spec/Section 3 -- Type System.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 18097a75c..5cbcb3be8 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -845,12 +845,12 @@ IsValidImplementationFieldType(fieldType, implementedFieldType): 2. Let {implementedNullableType} be the unwrapped nullable type of {implementedFieldType} if it is a Non-Null type, otherwise let it be {implementedFieldType} directly. - 3. Return {IsValidImplementationField(nullableType, implementedNullableType)}. + 3. Return {IsValidImplementationFieldType(nullableType, implementedNullableType)}. 2. If {fieldType} is a List type and {implementedFieldType} is also a List type: 1. Let {itemType} be the unwrapped item type of {fieldType}. 2. Let {implementedItemType} be the unwrapped item type of {implementedFieldType}. - 3. Return {IsValidImplementationField(itemType, implementedItemType)}. + 3. Return {IsValidImplementationFieldType(itemType, implementedItemType)}. 3. If {fieldType} is the same type as {implementedFieldType} then return {true}. 4. If {fieldType} is an Object type and {implementedFieldType} is a Union type and {fieldType} is a possible type of {implementedFieldType}