From f973735bae6f2124cb9d188d6dc241a0b24b685e Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Tue, 5 Apr 2022 11:57:03 +0300 Subject: [PATCH 1/4] Add spec text for unions implementing interfaces --- spec/Section 3 -- Type System.md | 79 +++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index b539b936e..5aa759281 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1051,7 +1051,9 @@ InterfaceTypeDefinition : GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires that -the implementing type will define all fields defined by those interfaces. +the implementing type will define all fields defined by those interfaces. Unions +can also implement interfaces, as long as each union member implements 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 @@ -1144,9 +1146,9 @@ 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 -Node interface: +type 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: ```raw graphql example interface Node { @@ -1160,9 +1162,8 @@ 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`: +that is being implemented) must also be defined on the implementing type. For +example, `Image` cannot implement `Resource` without also implementing `Node`: ```raw graphql example interface Node { @@ -1181,6 +1182,8 @@ interface Image implements Resource & Node { } ``` +Similar syntax for unions implementing interfaces is detailed below. + Interface definitions must not contain cyclic references nor implement themselves. This example is invalid because `Node` and `Named` implement themselves and each other: @@ -1293,8 +1296,8 @@ defined. ## Unions -UnionTypeDefinition : Description? union Name Directives[Const]? -UnionMemberTypes? +UnionTypeDefinition : Description? union Name ImplementsInterfaces? +Directives[Const]? UnionMemberTypes? UnionMemberTypes : @@ -1370,6 +1373,50 @@ union SearchResult = | Person ``` +**Unions Implementing Interfaces** + +When defining unions that implement interfaces, each union member must +explicitly implement each interface implemented by the union. For example, the +member types of union SearchResult must each explicitly implement the Resource +interface: + +```raw graphql example +interface Resource { + url: String +} + +union SearchResult implements Resource = Photo | Article + +type Article implements Resource { + url: String + title: String +} + +type Image implements Resource { + url: String + height: Int + width: Int +} +``` + +Transitively implemented interfaces (interfaces implemented by the interface +that is being implemented) must also be defined on the implementing union. For +example, `SearchResult` cannot implement `Resource` without also implementing +`Node`: + +```raw graphql example +interface Node { + id: ID! +} + +interface Resource implements Node { + id: ID! + url: String +} + +union SearchResult implements Resource & Node = Photo | Article +``` + **Result Coercion** The union type should have some way of determining which object a given result @@ -1388,13 +1435,21 @@ Union types have the potential to be invalid if incorrectly defined. 2. The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union. +3. An union type may declare that it implements one or more unique interfaces. +4. Each member of a union must be a super-set of all union-implemented + interfaces: + 1. Let this union type be {implementingType}. + 2. For each member type of {implementingType}: + 1. Let this member type be {memberType}. + 2. For each interface declared implemented as {implementedType}, + {IsValidImplementation(memberType, implementedType)} must be {true}. ### Union Extensions UnionTypeExtension : -- extend union Name Directives[Const]? UnionMemberTypes -- extend union Name Directives[Const] +- extend union Name ImplementsInterfaces? Directives[Const]? UnionMemberTypes +- extend union Name ImplementsInterfaces? Directives[Const] Union type extensions are used to represent a union type which has been extended from some original union type. For example, this might be used to represent @@ -1414,6 +1469,8 @@ Union type extensions have the potential to be invalid if incorrectly defined. the original Union type. 5. Any non-repeatable directives provided must not already apply to the original Union type. +6. All member types of the resulting extended Union type must be a super-set of + all Interfaces it implements. ## Enums From 1f0f1e0113f1685c362c77bc8a96cd614a401e61 Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Wed, 6 Apr 2022 05:28:08 +0300 Subject: [PATCH 2/4] review feedback --- spec/Section 3 -- Type System.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 5aa759281..256b0250c 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1146,9 +1146,9 @@ 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 -type 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: +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: ```raw graphql example interface Node { @@ -1392,7 +1392,7 @@ type Article implements Resource { title: String } -type Image implements Resource { +type Photo implements Resource { url: String height: Int width: Int @@ -1435,7 +1435,7 @@ Union types have the potential to be invalid if incorrectly defined. 2. The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union. -3. An union type may declare that it implements one or more unique interfaces. +3. A union type may declare that it implements one or more unique interfaces. 4. Each member of a union must be a super-set of all union-implemented interfaces: 1. Let this union type be {implementingType}. From 2b42f6b564df54bd4ef8eeb2a4a6ed92357fd3d6 Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Wed, 6 Apr 2022 05:34:27 +0300 Subject: [PATCH 3/4] adjust validation text --- spec/Section 3 -- Type System.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 256b0250c..24580f049 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1437,12 +1437,17 @@ Union types have the potential to be invalid if incorrectly defined. wrapping types must not be member types of a Union. 3. A union type may declare that it implements one or more unique interfaces. 4. Each member of a union must be a super-set of all union-implemented - interfaces: + interfaces. Moreover, each union member must declare that it implements each + interface declared on the union: 1. Let this union type be {implementingType}. 2. For each member type of {implementingType}: 1. Let this member type be {memberType}. - 2. For each interface declared implemented as {implementedType}, - {IsValidImplementation(memberType, implementedType)} must be {true}. + 2. For each interface declared implemented by {implementingType}. + 1. Let the implemented type be {implementedType}. + 2. {IsValidImplementation(memberType, implementedType)} must be {true}. + 3. Let the set of types declared as implemented by {memberType} be + {memberImplementedTypes}. + 4. {memberImplementedTypes} must contain {implementedType}. ### Union Extensions @@ -1470,7 +1475,8 @@ Union type extensions have the potential to be invalid if incorrectly defined. 5. Any non-repeatable directives provided must not already apply to the original Union type. 6. All member types of the resulting extended Union type must be a super-set of - all Interfaces it implements. + all Interfaces it implements. Moreover, each union member must declare that + it implements each interface that declared on the union. ## Enums From dfb92227479440be4727ac4e0caa018d26223134 Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Tue, 24 May 2022 00:07:29 +0300 Subject: [PATCH 4/4] unions implementing interfaces have fields --- spec/Section 3 -- Type System.md | 31 +++++++++++++++++++++++------- spec/Section 4 -- Introspection.md | 3 +++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 24580f049..b2b9cf315 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1305,16 +1305,17 @@ UnionMemberTypes : - = `|`? NamedType GraphQL Unions represent an object that could be one of a list of GraphQL Object -types, but provides for no guaranteed fields between those types. They also -differ from interfaces in that Object types declare what interfaces they -implement, but are not aware of what unions contain them. +types. They differ from interfaces in that Object types declare what interfaces +they implement, but are not aware of what unions contain them. With interfaces and objects, only those fields defined on the type can be queried directly; to query other fields on an interface, typed fragments must be -used. This is the same as for unions, but unions do not define any fields, so -**no** fields may be queried on this type without the use of type refining -fragments or inline fragments (with the exception of the meta-field -{\_\_typename}). +used. This is the same as for unions, but unions do not directly define any +fields, so the only fields that may be queried on a Union are the meta-field +{\_\_typename} and the fields of the interfaces that the Union declares it +implements (see +[Unions Implementing Interfaces](#sec-Unions.Unions-Implementing-Interfaces)). +Otherwise, type refining fragments or inline fragments must be used. For example, we might define the following types: @@ -1399,6 +1400,22 @@ type Photo implements Resource { } ``` +The following query would then be valid: + +```graphql example +{ + firstSearchResult { + url + ... on Article { + title + } + ... on Photo { + height + } + } +} +``` + Transitively implemented interfaces (interfaces implemented by the interface that is being implemented) must also be defined on the implementing union. For example, `SearchResult` cannot implement `Resource` without also implementing diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 9b32133f8..5c1f362e4 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -306,6 +306,9 @@ Fields\: - `kind` must return `__TypeKind.UNION`. - `name` must return a String. - `description` may return a String or {null}. +- `fields` must return the set of fields that can be selected for this type. + - Accepts the argument `includeDeprecated` which defaults to {false}. If + {true}, deprecated fields are also returned. - `possibleTypes` returns the list of types that can be represented within this union. They must be object types. - All other fields must return {null}.