Skip to content

Commit a925469

Browse files
committed
Handle fragments on unknown types #35
1 parent b9113d6 commit a925469

File tree

3 files changed

+92
-24
lines changed

3 files changed

+92
-24
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"types": "dist/index.d.ts",
77
"scripts": {
88
"lint": "eslint src/**/*.ts",
9+
"lint:fix": "eslint --fix src/**/*.ts",
910
"clean": "rimraf dist/*",
1011
"build": "tsc",
1112
"test": "npm run lint && npm run testonly",

src/QueryComplexity.ts

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
GraphQLField, isCompositeType, GraphQLCompositeType, GraphQLFieldMap,
2020
GraphQLSchema, DocumentNode, TypeInfo,
2121
visit, visitWithTypeInfo,
22-
GraphQLDirective, isAbstractType,
22+
GraphQLDirective, isAbstractType, GraphQLNamedType,
2323
} from 'graphql';
2424
import {
2525
GraphQLUnionType,
@@ -278,35 +278,40 @@ export default class QueryComplexity {
278278
}
279279
case Kind.FRAGMENT_SPREAD: {
280280
const fragment = this.context.getFragment(childNode.name.value);
281-
if (fragment) {
282-
const fragmentType = assertCompositeType(
283-
this.context.getSchema().getType(fragment.typeCondition.name.value)
281+
// Unknown fragment, should be caught by other validation rules
282+
if (!fragment) {
283+
break;
284+
}
285+
const fragmentType = this.context.getSchema().getType(fragment.typeCondition.name.value);
286+
// Invalid fragment type, ignore. Should be caught by other validation rules
287+
if (!isCompositeType(fragmentType)) {
288+
break;
289+
}
290+
const nodeComplexity = this.nodeComplexity(fragment, fragmentType);
291+
if (isAbstractType(fragmentType)) {
292+
// Add fragment complexity for all possible types
293+
complexities = addComplexities(
294+
nodeComplexity,
295+
complexities,
296+
this.context.getSchema().getPossibleTypes(fragmentType).map(t => t.name),
297+
);
298+
} else {
299+
// Add complexity for object type
300+
complexities = addComplexities(
301+
nodeComplexity,
302+
complexities,
303+
[fragmentType.name],
284304
);
285-
const nodeComplexity = this.nodeComplexity(fragment, fragmentType);
286-
if (isAbstractType(fragmentType)) {
287-
// Add fragment complexity for all possible types
288-
complexities = addComplexities(
289-
nodeComplexity,
290-
complexities,
291-
this.context.getSchema().getPossibleTypes(fragmentType).map(t => t.name),
292-
);
293-
} else {
294-
// Add complexity for object type
295-
complexities = addComplexities(
296-
nodeComplexity,
297-
complexities,
298-
[fragmentType.name],
299-
);
300-
}
301305
}
302306
break;
303307
}
304308
case Kind.INLINE_FRAGMENT: {
305-
let inlineFragmentType = typeDef;
309+
let inlineFragmentType: GraphQLNamedType = typeDef;
306310
if (childNode.typeCondition && childNode.typeCondition.name) {
307-
inlineFragmentType = assertCompositeType(
308-
this.context.getSchema().getType(childNode.typeCondition.name.value)
309-
);
311+
inlineFragmentType = this.context.getSchema().getType(childNode.typeCondition.name.value);
312+
if (!isCompositeType(inlineFragmentType)) {
313+
break;
314+
}
310315
}
311316

312317
const nodeComplexity = this.nodeComplexity(childNode, inlineFragmentType);

src/__tests__/QueryComplexity-test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,49 @@ describe('QueryComplexity analysis', () => {
186186
expect(visitor.complexity).to.equal(10);
187187
});
188188

189+
it('should ignore inline fragment on unknown type', () => {
190+
const ast = parse(`
191+
query {
192+
...on UnknownType {
193+
variableScalar(count: 100)
194+
}
195+
}
196+
`);
197+
198+
const context = new CompatibleValidationContext(schema, ast, typeInfo);
199+
const visitor = new ComplexityVisitor(context, {
200+
maximumComplexity: 100,
201+
estimators: [
202+
simpleEstimator({defaultComplexity: 10})
203+
]
204+
});
205+
206+
visit(ast, visitWithTypeInfo(typeInfo, visitor));
207+
expect(visitor.complexity).to.equal(0);
208+
});
209+
210+
it('should ignore fragment on unknown type', () => {
211+
const ast = parse(`
212+
query {
213+
...F
214+
}
215+
fragment F on UnknownType {
216+
variableScalar(count: 100)
217+
}
218+
`);
219+
220+
const context = new CompatibleValidationContext(schema, ast, typeInfo);
221+
const visitor = new ComplexityVisitor(context, {
222+
maximumComplexity: 100,
223+
estimators: [
224+
simpleEstimator({defaultComplexity: 10})
225+
]
226+
});
227+
228+
visit(ast, visitWithTypeInfo(typeInfo, visitor));
229+
expect(visitor.complexity).to.equal(0);
230+
});
231+
189232
it('should ignore unused variables', () => {
190233
const ast = parse(`
191234
query ($unusedVar: ID!) {
@@ -205,6 +248,25 @@ describe('QueryComplexity analysis', () => {
205248
expect(visitor.complexity).to.equal(10);
206249
});
207250

251+
it('should ignore unknown field', () => {
252+
const ast = parse(`
253+
query {
254+
unknownField
255+
}
256+
`);
257+
258+
const context = new CompatibleValidationContext(schema, ast, typeInfo);
259+
const visitor = new ComplexityVisitor(context, {
260+
maximumComplexity: 100,
261+
estimators: [
262+
simpleEstimator({defaultComplexity: 10})
263+
]
264+
});
265+
266+
visit(ast, visitWithTypeInfo(typeInfo, visitor));
267+
expect(visitor.complexity).to.equal(0);
268+
});
269+
208270
it('should report error above threshold', () => {
209271
const ast = parse(`
210272
query {

0 commit comments

Comments
 (0)