Skip to content

Commit

Permalink
introduce new Intersection type
Browse files Browse the repository at this point in the history
Intersections of unions and interfaces can be considered to "implement" their unions and interface members.

A type with a field of type Intersection will satisfy an interface where the field defined in the interface is one of the member types of the intersection.

Alternative to graphql#3527
  • Loading branch information
yaacovCR committed May 3, 2022
1 parent ea1c347 commit c328036
Show file tree
Hide file tree
Showing 43 changed files with 3,005 additions and 116 deletions.
6 changes: 6 additions & 0 deletions docs-old/APIReference-GraphQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ _Type Definitions_
A union type within GraphQL that defines a list of implementations.
</a>
</li>
<li>
<a href="../type/#graphqlintersectiontype">
<pre>class GraphQLIntersectionType</pre>
An intersection type within GraphQL that defines a list of constraining types.
</a>
</li>
<li>
<a href="../type/#graphqlenumtype">
<pre>class GraphQLEnumType</pre>
Expand Down
6 changes: 6 additions & 0 deletions docs-old/APIReference-TypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ _Definitions_
A union type within GraphQL that defines a list of implementations.
</a>
</li>
<li>
<a href="#graphqlintersectiontype">
<pre>class GraphQLIntersectionType</pre>
An intersection type within GraphQL that defines a list of constraining types.
</a>
</li>
<li>
<a href="#graphqlenumtype">
<pre>class GraphQLEnumType</pre>
Expand Down
12 changes: 12 additions & 0 deletions src/__testUtils__/kitchenSinkSDL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ extend union Feed = Photo | Video
extend union Feed @onUnion
intersection Resource = Feed & Node
intersection AnnotatedIntersection @onIntersection = Feed & Node
intersection AnnotatedIntersectionTwo @onIntersection = Feed & Node
intersection UndefinedIntersection
extend intersection Resource = Media & Accessible
extend intersection Resource @onIntersection
scalar CustomScalar
scalar AnnotatedScalar @onScalar
Expand Down
161 changes: 161 additions & 0 deletions src/execution/__tests__/abstract-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { parse } from '../../language/parser';
import {
assertInterfaceType,
GraphQLInterfaceType,
GraphQLIntersectionType,
GraphQLList,
GraphQLObjectType,
GraphQLUnionType,
Expand Down Expand Up @@ -352,6 +353,94 @@ describe('Execute: Handles execution of abstract types', () => {
});
});

it('isTypeOf used to resolve runtime type for Intersection', async () => {
const DogType = new GraphQLObjectType({
name: 'Dog',
isTypeOf(obj, context) {
const isDog = obj instanceof Dog;
return context.async ? Promise.resolve(isDog) : isDog;
},
interfaces: () => [PetType],
fields: {
name: { type: GraphQLString },
woofs: { type: GraphQLBoolean },
},
});

const CatType = new GraphQLObjectType({
name: 'Cat',
isTypeOf(obj, context) {
const isCat = obj instanceof Cat;
return context.async ? Promise.resolve(isCat) : isCat;
},
interfaces: () => [PetType],
fields: {
name: { type: GraphQLString },
meows: { type: GraphQLBoolean },
},
});

const PetType = new GraphQLInterfaceType({
name: 'Pet',
fields: {
name: { type: GraphQLString },
},
});

const CatOrDogType = new GraphQLUnionType({
name: 'CatOrDog',
types: [DogType, CatType],
});

const CatOrDogPetType = new GraphQLIntersectionType({
name: 'CatOrDogPet',
types: [CatOrDogType, PetType],
});

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
catOrDogPets: {
type: new GraphQLList(CatOrDogPetType),
resolve() {
return [new Dog('Odie', true), new Cat('Garfield', false)];
},
},
},
}),
});

const query = `{
catOrDogPets {
... on Pet {
name
}
... on Dog {
woofs
}
... on Cat {
meows
}
}
}`;

expect(await executeQuery({ schema, query })).to.deep.equal({
data: {
catOrDogPets: [
{
name: 'Odie',
woofs: true,
},
{
name: 'Garfield',
meows: false,
},
],
},
});
});

it('resolveType can throw', async () => {
const PetType = new GraphQLInterfaceType({
name: 'Pet',
Expand Down Expand Up @@ -497,6 +586,78 @@ describe('Execute: Handles execution of abstract types', () => {
});
});

it('resolve Intersection type using __typename on source object', async () => {
const schema = buildSchema(`
type Query {
catOrDogPets: [CatOrDogPet]
}
union CatOrDog = Cat | Dog
interface Pet {
name: String
}
intersection CatOrDogPet = CatOrDog & Pet
type Cat implements Pet {
name: String
meows: Boolean
}
type Dog implements Pet {
name: String
woofs: Boolean
}
`);

const query = `
{
catOrDogPets {
... on Pet {
name
}
... on Dog {
woofs
}
... on Cat {
meows
}
}
}
`;

const rootValue = {
catOrDogPets: [
{
__typename: 'Dog',
name: 'Odie',
woofs: true,
},
{
__typename: 'Cat',
name: 'Garfield',
meows: false,
},
],
};

expect(await executeQuery({ schema, query, rootValue })).to.deep.equal({
data: {
catOrDogPets: [
{
name: 'Odie',
woofs: true,
},
{
name: 'Garfield',
meows: false,
},
],
},
});
});

it('resolve Interface type using __typename on source object', async () => {
const schema = buildSchema(`
type Query {
Expand Down
Loading

0 comments on commit c328036

Please sign in to comment.