Skip to content

Commit 48ea2d3

Browse files
mike-marcacciIvanGoncharov
authored andcommitted
RFC: Allow interfaces to implement other interfaces (#2084)
This is an implementation of the RFC described by spec PR #373 graphql/graphql-spec#373
1 parent d85fc11 commit 48ea2d3

39 files changed

+1538
-179
lines changed

src/__fixtures__/schema-kitchen-sink.graphql

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ schema {
77
This is a description
88
of the `Foo` type.
99
"""
10-
type Foo implements Bar & Baz {
10+
type Foo implements Bar & Baz & Two {
1111
"Description of the `one` field."
1212
one: Type
1313
"""
@@ -50,12 +50,18 @@ interface AnnotatedInterface @onInterface {
5050

5151
interface UndefinedInterface
5252

53-
extend interface Bar {
53+
extend interface Bar implements Two {
5454
two(argument: InputType!): Type
5555
}
5656

5757
extend interface Bar @onInterface
5858

59+
interface Baz implements Bar & Two {
60+
one: Type
61+
two(argument: InputType!): Type
62+
four(argument: String = "string"): String
63+
}
64+
5965
union Feed =
6066
| Story
6167
| Article

src/execution/__tests__/union-interface-test.js

Lines changed: 152 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,32 @@ import { execute } from '../execute';
1919
class Dog {
2020
name: string;
2121
barks: boolean;
22+
mother: ?Dog;
23+
father: ?Dog;
24+
progeny: Array<Dog>;
2225

2326
constructor(name, barks) {
2427
this.name = name;
2528
this.barks = barks;
29+
this.mother = null;
30+
this.father = null;
31+
this.progeny = [];
2632
}
2733
}
2834

2935
class Cat {
3036
name: string;
3137
meows: boolean;
38+
mother: ?Cat;
39+
father: ?Cat;
40+
progeny: Array<Cat>;
3241

3342
constructor(name, meows) {
3443
this.name = name;
3544
this.meows = meows;
45+
this.mother = null;
46+
this.father = null;
47+
this.progeny = [];
3648
}
3749
}
3850

@@ -55,23 +67,46 @@ const NamedType = new GraphQLInterfaceType({
5567
},
5668
});
5769

70+
const LifeType = new GraphQLInterfaceType({
71+
name: 'Life',
72+
fields: () => ({
73+
progeny: { type: GraphQLList(LifeType) },
74+
}),
75+
});
76+
77+
const MammalType = new GraphQLInterfaceType({
78+
name: 'Mammal',
79+
interfaces: [LifeType],
80+
fields: () => ({
81+
progeny: { type: GraphQLList(MammalType) },
82+
mother: { type: MammalType },
83+
father: { type: MammalType },
84+
}),
85+
});
86+
5887
const DogType = new GraphQLObjectType({
5988
name: 'Dog',
60-
interfaces: [NamedType],
61-
fields: {
89+
interfaces: [MammalType, LifeType, NamedType],
90+
fields: () => ({
6291
name: { type: GraphQLString },
6392
barks: { type: GraphQLBoolean },
64-
},
93+
progeny: { type: GraphQLList(DogType) },
94+
mother: { type: DogType },
95+
father: { type: DogType },
96+
}),
6597
isTypeOf: value => value instanceof Dog,
6698
});
6799

68100
const CatType = new GraphQLObjectType({
69101
name: 'Cat',
70-
interfaces: [NamedType],
71-
fields: {
102+
interfaces: [MammalType, LifeType, NamedType],
103+
fields: () => ({
72104
name: { type: GraphQLString },
73105
meows: { type: GraphQLBoolean },
74-
},
106+
progeny: { type: GraphQLList(CatType) },
107+
mother: { type: CatType },
108+
father: { type: CatType },
109+
}),
75110
isTypeOf: value => value instanceof Cat,
76111
});
77112

@@ -90,12 +125,15 @@ const PetType = new GraphQLUnionType({
90125

91126
const PersonType = new GraphQLObjectType({
92127
name: 'Person',
93-
interfaces: [NamedType],
94-
fields: {
128+
interfaces: [NamedType, MammalType, LifeType],
129+
fields: () => ({
95130
name: { type: GraphQLString },
96131
pets: { type: GraphQLList(PetType) },
97132
friends: { type: GraphQLList(NamedType) },
98-
},
133+
progeny: { type: GraphQLList(PersonType) },
134+
mother: { type: PersonType },
135+
father: { type: PersonType },
136+
}),
99137
isTypeOf: value => value instanceof Person,
100138
});
101139

@@ -105,7 +143,13 @@ const schema = new GraphQLSchema({
105143
});
106144

107145
const garfield = new Cat('Garfield', false);
146+
garfield.mother = new Cat("Garfield's Mom", false);
147+
garfield.mother.progeny = [garfield];
148+
108149
const odie = new Dog('Odie', true);
150+
odie.mother = new Dog("Odie's Mom", true);
151+
odie.mother.progeny = [odie];
152+
109153
const liz = new Person('Liz');
110154
const john = new Person('John', [garfield, odie], [liz, odie]);
111155

@@ -122,6 +166,15 @@ describe('Execute: Union and intersection types', () => {
122166
enumValues { name }
123167
inputFields { name }
124168
}
169+
Mammal: __type(name: "Mammal") {
170+
kind
171+
name
172+
fields { name }
173+
interfaces { name }
174+
possibleTypes { name }
175+
enumValues { name }
176+
inputFields { name }
177+
}
125178
Pet: __type(name: "Pet") {
126179
kind
127180
name
@@ -140,7 +193,16 @@ describe('Execute: Union and intersection types', () => {
140193
kind: 'INTERFACE',
141194
name: 'Named',
142195
fields: [{ name: 'name' }],
143-
interfaces: null,
196+
interfaces: [],
197+
possibleTypes: [{ name: 'Person' }, { name: 'Dog' }, { name: 'Cat' }],
198+
enumValues: null,
199+
inputFields: null,
200+
},
201+
Mammal: {
202+
kind: 'INTERFACE',
203+
name: 'Mammal',
204+
fields: [{ name: 'progeny' }, { name: 'mother' }, { name: 'father' }],
205+
interfaces: [{ name: 'Life' }],
144206
possibleTypes: [{ name: 'Person' }, { name: 'Dog' }, { name: 'Cat' }],
145207
enumValues: null,
146208
inputFields: null,
@@ -178,8 +240,16 @@ describe('Execute: Union and intersection types', () => {
178240
__typename: 'Person',
179241
name: 'John',
180242
pets: [
181-
{ __typename: 'Cat', name: 'Garfield', meows: false },
182-
{ __typename: 'Dog', name: 'Odie', barks: true },
243+
{
244+
__typename: 'Cat',
245+
name: 'Garfield',
246+
meows: false,
247+
},
248+
{
249+
__typename: 'Dog',
250+
name: 'Odie',
251+
barks: true,
252+
},
183253
],
184254
},
185255
});
@@ -210,8 +280,16 @@ describe('Execute: Union and intersection types', () => {
210280
__typename: 'Person',
211281
name: 'John',
212282
pets: [
213-
{ __typename: 'Cat', name: 'Garfield', meows: false },
214-
{ __typename: 'Dog', name: 'Odie', barks: true },
283+
{
284+
__typename: 'Cat',
285+
name: 'Garfield',
286+
meows: false,
287+
},
288+
{
289+
__typename: 'Dog',
290+
name: 'Odie',
291+
barks: true,
292+
},
215293
],
216294
},
217295
});
@@ -259,6 +337,20 @@ describe('Execute: Union and intersection types', () => {
259337
... on Cat {
260338
meows
261339
}
340+
341+
... on Mammal {
342+
mother {
343+
__typename
344+
... on Dog {
345+
name
346+
barks
347+
}
348+
... on Cat {
349+
name
350+
meows
351+
}
352+
}
353+
}
262354
}
263355
}
264356
`);
@@ -268,8 +360,17 @@ describe('Execute: Union and intersection types', () => {
268360
__typename: 'Person',
269361
name: 'John',
270362
friends: [
271-
{ __typename: 'Person', name: 'Liz' },
272-
{ __typename: 'Dog', name: 'Odie', barks: true },
363+
{
364+
__typename: 'Person',
365+
name: 'Liz',
366+
mother: null,
367+
},
368+
{
369+
__typename: 'Dog',
370+
name: 'Odie',
371+
barks: true,
372+
mother: { __typename: 'Dog', name: "Odie's Mom", barks: true },
373+
},
273374
],
274375
},
275376
});
@@ -280,7 +381,14 @@ describe('Execute: Union and intersection types', () => {
280381
{
281382
__typename
282383
name
283-
pets { ...PetFields }
384+
pets {
385+
...PetFields,
386+
...on Mammal {
387+
mother {
388+
...ProgenyFields
389+
}
390+
}
391+
}
284392
friends { ...FriendFields }
285393
}
286394
@@ -306,19 +414,42 @@ describe('Execute: Union and intersection types', () => {
306414
meows
307415
}
308416
}
417+
418+
fragment ProgenyFields on Life {
419+
progeny {
420+
__typename
421+
}
422+
}
309423
`);
310424

311425
expect(execute(schema, ast, john)).to.deep.equal({
312426
data: {
313427
__typename: 'Person',
314428
name: 'John',
315429
pets: [
316-
{ __typename: 'Cat', name: 'Garfield', meows: false },
317-
{ __typename: 'Dog', name: 'Odie', barks: true },
430+
{
431+
__typename: 'Cat',
432+
name: 'Garfield',
433+
meows: false,
434+
mother: { progeny: [{ __typename: 'Cat' }] },
435+
},
436+
{
437+
__typename: 'Dog',
438+
name: 'Odie',
439+
barks: true,
440+
mother: { progeny: [{ __typename: 'Dog' }] },
441+
},
318442
],
319443
friends: [
320-
{ __typename: 'Person', name: 'Liz' },
321-
{ __typename: 'Dog', name: 'Odie', barks: true },
444+
{
445+
__typename: 'Person',
446+
name: 'Liz',
447+
},
448+
{
449+
__typename: 'Dog',
450+
name: 'Odie',
451+
barks: true,
452+
},
322453
],
323454
},
324455
});

src/execution/execute.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ function doesFragmentConditionMatch(
583583
return true;
584584
}
585585
if (isAbstractType(conditionalType)) {
586-
return exeContext.schema.isPossibleType(conditionalType, type);
586+
return exeContext.schema.isSubType(conditionalType, type);
587587
}
588588
return false;
589589
}
@@ -1021,7 +1021,7 @@ function ensureValidRuntimeType(
10211021
);
10221022
}
10231023

1024-
if (!exeContext.schema.isPossibleType(returnType, runtimeType)) {
1024+
if (!exeContext.schema.isSubType(returnType, runtimeType)) {
10251025
throw new GraphQLError(
10261026
`Runtime Object type "${runtimeType.name}" is not a possible type for "${returnType.name}".`,
10271027
fieldNodes,

0 commit comments

Comments
 (0)