diff --git a/src/QueryComplexity.ts b/src/QueryComplexity.ts index f724a01..c14c201 100644 --- a/src/QueryComplexity.ts +++ b/src/QueryComplexity.ts @@ -4,19 +4,22 @@ import { getArgumentValues, + getDirectiveValues, } from 'graphql/execution/values'; import { ValidationContext, FragmentDefinitionNode, OperationDefinitionNode, + DirectiveNode, FieldNode, FragmentSpreadNode, InlineFragmentNode, assertCompositeType, GraphQLField, isCompositeType, GraphQLCompositeType, GraphQLFieldMap, GraphQLSchema, DocumentNode, TypeInfo, - visit, visitWithTypeInfo + visit, visitWithTypeInfo, + GraphQLDirective, } from 'graphql'; import { GraphQLUnionType, @@ -102,6 +105,8 @@ export default class QueryComplexity { options: QueryComplexityOptions; OperationDefinition: Object; estimators: Array; + includeDirectiveDef: GraphQLDirective; + skipDirectiveDef: GraphQLDirective; constructor( context: ValidationContext, @@ -115,6 +120,9 @@ export default class QueryComplexity { this.complexity = 0; this.options = options; + this.includeDirectiveDef = this.context.getSchema().getDirective('include'); + this.skipDirectiveDef = this.context.getSchema().getDirective('skip'); + if (!options.estimators) { console.warn( 'DEPRECATION WARNING: Estimators should be configured in the queryComplexity options.' @@ -183,6 +191,29 @@ export default class QueryComplexity { (total: number, childNode: FieldNode | FragmentSpreadNode | InlineFragmentNode) => { let nodeComplexity = 0; + let includeNode = true; + let skipNode = false; + + childNode.directives.forEach((directive: DirectiveNode) => { + const directiveName = directive.name.value; + switch (directiveName) { + case 'include': { + const values = getDirectiveValues(this.includeDirectiveDef, childNode, this.options.variables || {}); + includeNode = values.if; + break; + } + case 'skip': { + const values = getDirectiveValues(this.skipDirectiveDef, childNode, this.options.variables || {}); + skipNode = values.if; + break; + } + } + }); + + if (!includeNode || skipNode) { + return total; + } + switch (childNode.kind) { case Kind.FIELD: { const field = fields[childNode.name.value]; diff --git a/src/__tests__/QueryComplexity-test.ts b/src/__tests__/QueryComplexity-test.ts index 5c907cd..b66f62c 100644 --- a/src/__tests__/QueryComplexity-test.ts +++ b/src/__tests__/QueryComplexity-test.ts @@ -41,6 +41,91 @@ describe('QueryComplexity analysis', () => { expect(complexity).to.equal(1); }); + it('should respect @include(if: false)', () => { + const ast = parse(` + query { + variableScalar(count: 10) @include(if: false) + } + `); + + const complexity = getComplexity({ + estimators: [ + simpleEstimator({defaultComplexity: 1}) + ], + schema, + query: ast + }); + expect(complexity).to.equal(0); + }); + + it('should respect @include(if: true)', () => { + const ast = parse(` + query { + variableScalar(count: 10) @include(if: true) + } + `); + + const complexity = getComplexity({ + estimators: [ + simpleEstimator({defaultComplexity: 1}) + ], + schema, + query: ast + }); + expect(complexity).to.equal(1); + }); + + it('should respect @skip(if: true)', () => { + const ast = parse(` + query { + variableScalar(count: 10) @skip(if: true) + } + `); + + const complexity = getComplexity({ + estimators: [ + simpleEstimator({defaultComplexity: 1}) + ], + schema, + query: ast + }); + expect(complexity).to.equal(0); + }); + + it('should respect @skip(if: false)', () => { + const ast = parse(` + query { + variableScalar(count: 10) @skip(if: false) + } + `); + + const complexity = getComplexity({ + estimators: [ + simpleEstimator({defaultComplexity: 1}) + ], + schema, + query: ast + }); + expect(complexity).to.equal(1); + }); + + it('should respect @skip(if: false) @include(if: true)', () => { + const ast = parse(` + query { + variableScalar(count: 10) @skip(if: false) @include(if: true) + } + `); + + const complexity = getComplexity({ + estimators: [ + simpleEstimator({defaultComplexity: 1}) + ], + schema, + query: ast + }); + expect(complexity).to.equal(1); + }); + it('should calculate complexity with variables', () => { const ast = parse(` query Q($count: Int) { @@ -341,7 +426,7 @@ describe('QueryComplexity analysis', () => { }); expect(Number.isNaN(complexity)).to.equal(true); }); - + it('should skip complexity calculation by directiveEstimator when no astNode available on field', () => { const ast = parse(` query {