Skip to content

Commit 43ff3e3

Browse files
mattkrickleebyron
authored andcommitted
fix #1277 ensure interface has at least 1 concrete type (#1280)
* fix #1277 ensure interface has at least 1 concrete type * fix tests * update error message * fix lint * remove impossible schema invariant and test * lint * test for unimplemented interfaces
1 parent 6f16ba2 commit 43ff3e3

File tree

4 files changed

+56
-30
lines changed

4 files changed

+56
-30
lines changed

src/type/__tests__/schema-test.js

-19
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ const InterfaceType = new GraphQLInterfaceType({
2323
fields: { fieldName: { type: GraphQLString } },
2424
});
2525

26-
const ImplementingType = new GraphQLObjectType({
27-
name: 'Object',
28-
interfaces: [InterfaceType],
29-
fields: { fieldName: { type: GraphQLString, resolve: () => '' } },
30-
});
31-
3226
const DirectiveInputType = new GraphQLInputObjectType({
3327
name: 'DirInput',
3428
fields: {
@@ -76,19 +70,6 @@ const Schema = new GraphQLSchema({
7670
});
7771

7872
describe('Type System: Schema', () => {
79-
describe('Getting possible types', () => {
80-
it('throws human-reable error if schema.types is not defined', () => {
81-
const checkPossible = () => {
82-
return Schema.isPossibleType(InterfaceType, ImplementingType);
83-
};
84-
expect(checkPossible).to.throw(
85-
'Could not find possible implementing types for Interface in schema. ' +
86-
'Check that schema.types is defined and is an array of all possible ' +
87-
'types in the schema.',
88-
);
89-
});
90-
});
91-
9273
describe('Type Map', () => {
9374
it('includes input types only used in directives', () => {
9475
expect(Schema.getTypeMap()).to.include.key('DirInput');

src/type/__tests__/validation-test.js

+35-5
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,22 @@ const SomeScalarType = new GraphQLScalarType({
3131
parseLiteral() {},
3232
});
3333

34+
const SomeInterfaceType = new GraphQLInterfaceType({
35+
name: 'SomeInterface',
36+
fields: { f: { type: GraphQLString } },
37+
});
38+
3439
const SomeObjectType = new GraphQLObjectType({
3540
name: 'SomeObject',
3641
fields: { f: { type: GraphQLString } },
42+
interfaces: [SomeInterfaceType],
3743
});
3844

3945
const SomeUnionType = new GraphQLUnionType({
4046
name: 'SomeUnion',
4147
types: [SomeObjectType],
4248
});
4349

44-
const SomeInterfaceType = new GraphQLInterfaceType({
45-
name: 'SomeInterface',
46-
fields: { f: { type: GraphQLString } },
47-
});
48-
4950
const SomeEnumType = new GraphQLEnumType({
5051
name: 'SomeEnum',
5152
values: {
@@ -772,6 +773,7 @@ describe('Type System: Object fields must have output types', () => {
772773
f: { type: BadObjectType },
773774
},
774775
}),
776+
types: [SomeObjectType],
775777
});
776778
}
777779

@@ -1032,13 +1034,22 @@ describe('Type System: Interface fields must have output types', () => {
10321034
},
10331035
});
10341036

1037+
const BadImplementingType = new GraphQLObjectType({
1038+
name: 'BadImplementing',
1039+
interfaces: [BadInterfaceType],
1040+
fields: {
1041+
badField: { type: fieldType },
1042+
},
1043+
});
1044+
10351045
return new GraphQLSchema({
10361046
query: new GraphQLObjectType({
10371047
name: 'Query',
10381048
fields: {
10391049
f: { type: BadInterfaceType },
10401050
},
10411051
}),
1052+
types: [BadImplementingType, SomeObjectType],
10421053
});
10431054
}
10441055

@@ -1092,6 +1103,25 @@ describe('Type System: Interface fields must have output types', () => {
10921103
},
10931104
]);
10941105
});
1106+
1107+
it('rejects an interface not implemented by at least one object', () => {
1108+
const schema = buildSchema(`
1109+
type Query {
1110+
test: SomeInterface
1111+
}
1112+
1113+
interface SomeInterface {
1114+
foo: String
1115+
}
1116+
`);
1117+
expect(validateSchema(schema)).to.containSubset([
1118+
{
1119+
message:
1120+
'Interface SomeInterface must be implemented by at least one Object type.',
1121+
locations: [{ line: 6, column: 7 }],
1122+
},
1123+
]);
1124+
});
10951125
});
10961126

10971127
describe('Type System: Field arguments must have input types', () => {

src/type/schema.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import {
11+
isAbstractType,
1112
isObjectType,
1213
isInterfaceType,
1314
isUnionType,
@@ -160,6 +161,8 @@ export class GraphQLSchema {
160161
}
161162
}
162163
});
164+
} else if (isAbstractType(type) && !this._implementations[type.name]) {
165+
this._implementations[type.name] = [];
163166
}
164167
});
165168
}
@@ -204,12 +207,6 @@ export class GraphQLSchema {
204207

205208
if (!possibleTypeMap[abstractType.name]) {
206209
const possibleTypes = this.getPossibleTypes(abstractType);
207-
invariant(
208-
Array.isArray(possibleTypes),
209-
`Could not find possible implementing types for ${abstractType.name} ` +
210-
'in schema. Check that schema.types is defined and is an array of ' +
211-
'all possible types in the schema.',
212-
);
213210
possibleTypeMap[abstractType.name] = possibleTypes.reduce(
214211
(map, type) => ((map[type.name] = true), map),
215212
Object.create(null),

src/type/validate.js

+18
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ function validateTypes(context: SchemaValidationContext): void {
257257
} else if (isInterfaceType(type)) {
258258
// Ensure fields are valid.
259259
validateFields(context, type);
260+
261+
// Ensure Interfaces include at least 1 Object type.
262+
validateInterfaces(context, type);
260263
} else if (isUnionType(type)) {
261264
// Ensure Unions include valid member types.
262265
validateUnionMembers(context, type);
@@ -364,6 +367,21 @@ function validateObjectInterfaces(
364367
});
365368
}
366369

370+
function validateInterfaces(
371+
context: SchemaValidationContext,
372+
iface: GraphQLInterfaceType,
373+
): void {
374+
const possibleTypes = context.schema.getPossibleTypes(iface);
375+
376+
if (possibleTypes.length === 0) {
377+
context.reportError(
378+
`Interface ${iface.name} must be implemented ` +
379+
`by at least one Object type.`,
380+
iface.astNode,
381+
);
382+
}
383+
}
384+
367385
function validateObjectImplementsInterface(
368386
context: SchemaValidationContext,
369387
object: GraphQLObjectType,

0 commit comments

Comments
 (0)