Skip to content

Commit ef9af63

Browse files
authored
Improve errors when receiving bad introspection data (graphql#1063)
I've seen a few examples of passing bad introspection data to buildClientSchema() result in a hard to understand internal error. This instead prints a more understandable error which includes a print out of the bad value in question. Hopefully this aids debugging. This also adds a comment to the description of this function recommending that the "errors" field of a server response be checked before continuing to avoid partial results.
1 parent 6e7d51d commit ef9af63

File tree

2 files changed

+78
-29
lines changed

2 files changed

+78
-29
lines changed

src/utilities/__tests__/buildClientSchema-test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -736,9 +736,8 @@ describe('Type System: build schema from introspection', () => {
736736
expect(
737737
() => buildClientSchema(incompleteIntrospection)
738738
).to.throw(
739-
'Invalid or incomplete schema, unknown kind: undefined. Ensure ' +
740-
'that a full introspection query is used in order to build a ' +
741-
'client schema.'
739+
'Invalid or incomplete introspection result. Ensure that a full ' +
740+
'introspection query is used in order to build a client schema'
742741
);
743742
});
744743

src/utilities/buildClientSchema.js

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ import type {
8080
* tools, but cannot be used to execute a query, as introspection does not
8181
* represent the "resolver", "parse" or "serialize" functions or any other
8282
* server-internal mechanisms.
83+
*
84+
* This function expects a complete introspection result. Don't forget to check
85+
* the "errors" field of a server response before calling this function.
8386
*/
8487
export function buildClientSchema(
8588
introspection: IntrospectionQuery
@@ -135,6 +138,9 @@ export function buildClientSchema(
135138
);
136139
return new GraphQLNonNull(nullableType);
137140
}
141+
if (!typeRef.name) {
142+
throw new Error('Unknown type reference: ' + JSON.stringify(typeRef));
143+
}
138144
return getNamedType(typeRef.name);
139145
}
140146

@@ -193,30 +199,30 @@ export function buildClientSchema(
193199
return type;
194200
}
195201

196-
197202
// Given a type's introspection result, construct the correct
198203
// GraphQLType instance.
199204
function buildType(type: IntrospectionType): GraphQLNamedType {
200-
switch (type.kind) {
201-
case TypeKind.SCALAR:
202-
return buildScalarDef(type);
203-
case TypeKind.OBJECT:
204-
return buildObjectDef(type);
205-
case TypeKind.INTERFACE:
206-
return buildInterfaceDef(type);
207-
case TypeKind.UNION:
208-
return buildUnionDef(type);
209-
case TypeKind.ENUM:
210-
return buildEnumDef(type);
211-
case TypeKind.INPUT_OBJECT:
212-
return buildInputObjectDef(type);
213-
default:
214-
throw new Error(
215-
`Invalid or incomplete schema, unknown kind: ${type.kind}. Ensure ` +
216-
'that a full introspection query is used in order to build a ' +
217-
'client schema.'
218-
);
205+
if (type && type.name && type.kind) {
206+
switch (type.kind) {
207+
case TypeKind.SCALAR:
208+
return buildScalarDef(type);
209+
case TypeKind.OBJECT:
210+
return buildObjectDef(type);
211+
case TypeKind.INTERFACE:
212+
return buildInterfaceDef(type);
213+
case TypeKind.UNION:
214+
return buildUnionDef(type);
215+
case TypeKind.ENUM:
216+
return buildEnumDef(type);
217+
case TypeKind.INPUT_OBJECT:
218+
return buildInputObjectDef(type);
219+
}
219220
}
221+
throw new Error(
222+
'Invalid or incomplete introspection result. Ensure that a full ' +
223+
'introspection query is used in order to build a client schema:' +
224+
JSON.stringify(type)
225+
);
220226
}
221227

222228
function buildScalarDef(
@@ -238,6 +244,12 @@ export function buildClientSchema(
238244
function buildObjectDef(
239245
objectIntrospection: IntrospectionObjectType
240246
): GraphQLObjectType {
247+
if (!objectIntrospection.interfaces) {
248+
throw new Error(
249+
'Introspection result missing interfaces: ' +
250+
JSON.stringify(objectIntrospection)
251+
);
252+
}
241253
return new GraphQLObjectType({
242254
name: objectIntrospection.name,
243255
description: objectIntrospection.description,
@@ -260,6 +272,12 @@ export function buildClientSchema(
260272
function buildUnionDef(
261273
unionIntrospection: IntrospectionUnionType
262274
): GraphQLUnionType {
275+
if (!unionIntrospection.possibleTypes) {
276+
throw new Error(
277+
'Introspection result missing possibleTypes: ' +
278+
JSON.stringify(unionIntrospection)
279+
);
280+
}
263281
return new GraphQLUnionType({
264282
name: unionIntrospection.name,
265283
description: unionIntrospection.description,
@@ -271,6 +289,12 @@ export function buildClientSchema(
271289
function buildEnumDef(
272290
enumIntrospection: IntrospectionEnumType
273291
): GraphQLEnumType {
292+
if (!enumIntrospection.enumValues) {
293+
throw new Error(
294+
'Introspection result missing enumValues: ' +
295+
JSON.stringify(enumIntrospection)
296+
);
297+
}
274298
return new GraphQLEnumType({
275299
name: enumIntrospection.name,
276300
description: enumIntrospection.description,
@@ -288,6 +312,12 @@ export function buildClientSchema(
288312
function buildInputObjectDef(
289313
inputObjectIntrospection: IntrospectionInputObjectType
290314
): GraphQLInputObjectType {
315+
if (!inputObjectIntrospection.inputFields) {
316+
throw new Error(
317+
'Introspection result missing inputFields: ' +
318+
JSON.stringify(inputObjectIntrospection)
319+
);
320+
}
291321
return new GraphQLInputObjectType({
292322
name: inputObjectIntrospection.name,
293323
description: inputObjectIntrospection.description,
@@ -296,15 +326,29 @@ export function buildClientSchema(
296326
}
297327

298328
function buildFieldDefMap(typeIntrospection) {
329+
if (!typeIntrospection.fields) {
330+
throw new Error(
331+
'Introspection result missing fields: ' +
332+
JSON.stringify(typeIntrospection)
333+
);
334+
}
299335
return keyValMap(
300336
typeIntrospection.fields,
301337
fieldIntrospection => fieldIntrospection.name,
302-
fieldIntrospection => ({
303-
description: fieldIntrospection.description,
304-
deprecationReason: fieldIntrospection.deprecationReason,
305-
type: getOutputType(fieldIntrospection.type),
306-
args: buildInputValueDefMap(fieldIntrospection.args),
307-
})
338+
fieldIntrospection => {
339+
if (!fieldIntrospection.args) {
340+
throw new Error(
341+
'Introspection result missing field args: ' +
342+
JSON.stringify(fieldIntrospection)
343+
);
344+
}
345+
return {
346+
description: fieldIntrospection.description,
347+
deprecationReason: fieldIntrospection.deprecationReason,
348+
type: getOutputType(fieldIntrospection.type),
349+
args: buildInputValueDefMap(fieldIntrospection.args),
350+
};
351+
}
308352
);
309353
}
310354

@@ -349,6 +393,12 @@ export function buildClientSchema(
349393
DirectiveLocation.INLINE_FRAGMENT,
350394
]
351395
);
396+
if (!directiveIntrospection.args) {
397+
throw new Error(
398+
'Introspection result missing directive args: ' +
399+
JSON.stringify(directiveIntrospection)
400+
);
401+
}
352402
return new GraphQLDirective({
353403
name: directiveIntrospection.name,
354404
description: directiveIntrospection.description,

0 commit comments

Comments
 (0)