Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Allow interfaces to implement other interfaces #2084

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/__fixtures__/schema-kitchen-sink.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ schema {
This is a description
of the `Foo` type.
"""
type Foo implements Bar & Baz {
type Foo implements Bar & Baz & Two {
"Description of the `one` field."
one: Type
"""
Expand Down Expand Up @@ -50,12 +50,18 @@ interface AnnotatedInterface @onInterface {

interface UndefinedInterface

extend interface Bar {
extend interface Bar implements Two {
two(argument: InputType!): Type
}

extend interface Bar @onInterface

interface Baz implements Bar & Two {
one: Type
two(argument: InputType!): Type
four(argument: String = "string"): String
}

union Feed =
| Story
| Article
Expand Down
173 changes: 152 additions & 21 deletions src/execution/__tests__/union-interface-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,32 @@ import { execute } from '../execute';
class Dog {
name: string;
barks: boolean;
mother: ?Dog;
father: ?Dog;
progeny: Array<Dog>;

constructor(name, barks) {
this.name = name;
this.barks = barks;
this.mother = null;
this.father = null;
this.progeny = [];
}
}

class Cat {
name: string;
meows: boolean;
mother: ?Cat;
father: ?Cat;
progeny: Array<Cat>;

constructor(name, meows) {
this.name = name;
this.meows = meows;
this.mother = null;
this.father = null;
this.progeny = [];
}
}

Expand All @@ -55,23 +67,46 @@ const NamedType = new GraphQLInterfaceType({
},
});

const LifeType = new GraphQLInterfaceType({
name: 'Life',
fields: () => ({
progeny: { type: GraphQLList(LifeType) },
}),
});

const MammalType = new GraphQLInterfaceType({
name: 'Mammal',
interfaces: [LifeType],
fields: () => ({
progeny: { type: GraphQLList(MammalType) },
mother: { type: MammalType },
father: { type: MammalType },
}),
});

const DogType = new GraphQLObjectType({
name: 'Dog',
interfaces: [NamedType],
fields: {
interfaces: [MammalType, LifeType, NamedType],
fields: () => ({
name: { type: GraphQLString },
barks: { type: GraphQLBoolean },
},
progeny: { type: GraphQLList(DogType) },
mother: { type: DogType },
father: { type: DogType },
}),
isTypeOf: value => value instanceof Dog,
});

const CatType = new GraphQLObjectType({
name: 'Cat',
interfaces: [NamedType],
fields: {
interfaces: [MammalType, LifeType, NamedType],
fields: () => ({
name: { type: GraphQLString },
meows: { type: GraphQLBoolean },
},
progeny: { type: GraphQLList(CatType) },
mother: { type: CatType },
father: { type: CatType },
}),
isTypeOf: value => value instanceof Cat,
});

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

const PersonType = new GraphQLObjectType({
name: 'Person',
interfaces: [NamedType],
fields: {
interfaces: [NamedType, MammalType, LifeType],
fields: () => ({
name: { type: GraphQLString },
pets: { type: GraphQLList(PetType) },
friends: { type: GraphQLList(NamedType) },
},
progeny: { type: GraphQLList(PersonType) },
mother: { type: PersonType },
father: { type: PersonType },
}),
isTypeOf: value => value instanceof Person,
});

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

const garfield = new Cat('Garfield', false);
garfield.mother = new Cat("Garfield's Mom", false);
garfield.mother.progeny = [garfield];

const odie = new Dog('Odie', true);
odie.mother = new Dog("Odie's Mom", true);
odie.mother.progeny = [odie];

const liz = new Person('Liz');
const john = new Person('John', [garfield, odie], [liz, odie]);

Expand All @@ -122,6 +166,15 @@ describe('Execute: Union and intersection types', () => {
enumValues { name }
inputFields { name }
}
Mammal: __type(name: "Mammal") {
kind
name
fields { name }
interfaces { name }
possibleTypes { name }
enumValues { name }
inputFields { name }
}
Pet: __type(name: "Pet") {
kind
name
Expand All @@ -140,7 +193,16 @@ describe('Execute: Union and intersection types', () => {
kind: 'INTERFACE',
name: 'Named',
fields: [{ name: 'name' }],
interfaces: null,
interfaces: [],
possibleTypes: [{ name: 'Person' }, { name: 'Dog' }, { name: 'Cat' }],
enumValues: null,
inputFields: null,
},
Mammal: {
kind: 'INTERFACE',
name: 'Mammal',
fields: [{ name: 'progeny' }, { name: 'mother' }, { name: 'father' }],
interfaces: [{ name: 'Life' }],
possibleTypes: [{ name: 'Person' }, { name: 'Dog' }, { name: 'Cat' }],
enumValues: null,
inputFields: null,
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -178,8 +240,16 @@ describe('Execute: Union and intersection types', () => {
__typename: 'Person',
name: 'John',
pets: [
{ __typename: 'Cat', name: 'Garfield', meows: false },
{ __typename: 'Dog', name: 'Odie', barks: true },
{
__typename: 'Cat',
name: 'Garfield',
meows: false,
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
},
],
},
});
Expand Down Expand Up @@ -210,8 +280,16 @@ describe('Execute: Union and intersection types', () => {
__typename: 'Person',
name: 'John',
pets: [
{ __typename: 'Cat', name: 'Garfield', meows: false },
{ __typename: 'Dog', name: 'Odie', barks: true },
{
__typename: 'Cat',
name: 'Garfield',
meows: false,
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
},
],
},
});
Expand Down Expand Up @@ -259,6 +337,20 @@ describe('Execute: Union and intersection types', () => {
... on Cat {
meows
}

... on Mammal {
mother {
__typename
... on Dog {
name
barks
}
... on Cat {
name
meows
}
}
}
}
}
`);
Expand All @@ -268,8 +360,17 @@ describe('Execute: Union and intersection types', () => {
__typename: 'Person',
name: 'John',
friends: [
{ __typename: 'Person', name: 'Liz' },
{ __typename: 'Dog', name: 'Odie', barks: true },
{
__typename: 'Person',
name: 'Liz',
mother: null,
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
mother: { __typename: 'Dog', name: "Odie's Mom", barks: true },
},
],
},
});
Expand All @@ -280,7 +381,14 @@ describe('Execute: Union and intersection types', () => {
{
__typename
name
pets { ...PetFields }
pets {
...PetFields,
...on Mammal {
mother {
...ProgenyFields
}
}
}
friends { ...FriendFields }
}

Expand All @@ -306,19 +414,42 @@ describe('Execute: Union and intersection types', () => {
meows
}
}

fragment ProgenyFields on Life {
progeny {
__typename
}
}
`);

expect(execute(schema, ast, john)).to.deep.equal({
data: {
__typename: 'Person',
name: 'John',
pets: [
{ __typename: 'Cat', name: 'Garfield', meows: false },
{ __typename: 'Dog', name: 'Odie', barks: true },
{
__typename: 'Cat',
name: 'Garfield',
meows: false,
mother: { progeny: [{ __typename: 'Cat' }] },
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
mother: { progeny: [{ __typename: 'Dog' }] },
},
],
friends: [
{ __typename: 'Person', name: 'Liz' },
{ __typename: 'Dog', name: 'Odie', barks: true },
{
__typename: 'Person',
name: 'Liz',
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
},
],
},
});
Expand Down
4 changes: 2 additions & 2 deletions src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ function doesFragmentConditionMatch(
return true;
}
if (isAbstractType(conditionalType)) {
return exeContext.schema.isPossibleType(conditionalType, type);
return exeContext.schema.isSubType(conditionalType, type);
}
return false;
}
Expand Down Expand Up @@ -1021,7 +1021,7 @@ function ensureValidRuntimeType(
);
}

if (!exeContext.schema.isPossibleType(returnType, runtimeType)) {
if (!exeContext.schema.isSubType(returnType, runtimeType)) {
throw new GraphQLError(
`Runtime Object type "${runtimeType.name}" is not a possible type for "${returnType.name}".`,
fieldNodes,
Expand Down
Loading