Skip to content

Commit 4c2f4b7

Browse files
committed
complexity analysis is considering @includes and @Skip directives
1 parent 7ac1b45 commit 4c2f4b7

File tree

2 files changed

+63
-32
lines changed

2 files changed

+63
-32
lines changed

src/analysis/ASTParser.ts

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
SelectionSetNode,
55
DefinitionNode,
66
Kind,
7+
DirectiveNode,
78
SelectionNode,
9+
getArgumentValues,
810
} from 'graphql';
911
import { FieldWeight, TypeWeightObject, Variables } from '../@types/buildTypeWeights';
1012
/**
@@ -139,42 +141,70 @@ class ASTParser {
139141
}
140142
}
141143

144+
directiveCheck(directive: DirectiveNode): boolean {
145+
// let directive;
146+
// if (directives) [directive] = directives;
147+
if (directive?.arguments) {
148+
const argument = directive.arguments[0];
149+
const argumentHasVariables =
150+
argument.value.kind === Kind.VARIABLE && argument.name.value === 'if';
151+
152+
let directiveArgumentValue;
153+
if (argument.value.kind === Kind.BOOLEAN) {
154+
directiveArgumentValue = Boolean(argument.value.value);
155+
} else if (argumentHasVariables) {
156+
directiveArgumentValue = Boolean(this.variables[argument.value.name.value]);
157+
}
158+
159+
return (
160+
(directive.name.value === 'include' && directiveArgumentValue === true) ||
161+
(directive.name.value === 'skip' && directiveArgumentValue === false)
162+
);
163+
}
164+
return true;
165+
}
166+
142167
private selectionNode(node: SelectionNode, parentName: string): number {
143168
let complexity = 0;
144-
this.depth += 1;
145-
if (this.depth > this.maxDepth) this.maxDepth = this.depth;
146-
// check the kind property against the set of selection nodes that are possible
147-
if (node.kind === Kind.FIELD) {
148-
// call the function that handle field nodes
149-
complexity += this.fieldNode(node, parentName.toLowerCase());
150-
} else if (node.kind === Kind.FRAGMENT_SPREAD) {
151-
// add complexity and depth from fragment cache
152-
const { complexity: fragComplexity, depth: fragDepth } =
153-
this.fragmentCache[node.name.value];
154-
complexity += fragComplexity;
155-
this.depth += fragDepth;
169+
const directive = node.directives;
170+
if (directive && this.directiveCheck(directive[0])) {
171+
this.depth += 1;
156172
if (this.depth > this.maxDepth) this.maxDepth = this.depth;
157-
this.depth -= fragDepth;
173+
// check the kind property against the set of selection nodes that are possible
174+
if (node.kind === Kind.FIELD) {
175+
// call the function that handle field nodes
176+
complexity += this.fieldNode(node, parentName.toLowerCase());
177+
} else if (node.kind === Kind.FRAGMENT_SPREAD) {
178+
// add complexity and depth from fragment cache
179+
const { complexity: fragComplexity, depth: fragDepth } =
180+
this.fragmentCache[node.name.value];
181+
complexity += fragComplexity;
182+
this.depth += fragDepth;
183+
if (this.depth > this.maxDepth) this.maxDepth = this.depth;
184+
this.depth -= fragDepth;
185+
186+
// This is a leaf
187+
// need to parse fragment definition at root and get the result here
188+
} else if (node.kind === Kind.INLINE_FRAGMENT) {
189+
const { typeCondition } = node;
158190

159-
// This is a leaf
160-
// need to parse fragment definition at root and get the result here
161-
} else if (node.kind === Kind.INLINE_FRAGMENT) {
162-
const { typeCondition } = node;
191+
// named type is the type from which inner fields should be take
192+
// If the TypeCondition is omitted, an inline fragment is considered to be of the same type as the enclosing context
193+
const namedType = typeCondition
194+
? typeCondition.name.value.toLowerCase()
195+
: parentName;
163196

164-
// named type is the type from which inner fields should be take
165-
// If the TypeCondition is omitted, an inline fragment is considered to be of the same type as the enclosing context
166-
const namedType = typeCondition ? typeCondition.name.value.toLowerCase() : parentName;
197+
// TODO: Handle directives like @include and @skip
198+
// subtract 1 before, and add one after, entering the fragment selection to negate the additional level of depth added
199+
this.depth -= 1;
200+
complexity += this.selectionSetNode(node.selectionSet, namedType);
201+
this.depth += 1;
202+
} else {
203+
throw new Error(`ERROR: ASTParser.selectionNode: node type not supported`);
204+
}
167205

168-
// TODO: Handle directives like @include and @skip
169-
// subtract 1 before, and add one after, entering the fragment selection to negate the additional level of depth added
170206
this.depth -= 1;
171-
complexity += this.selectionSetNode(node.selectionSet, namedType);
172-
this.depth += 1;
173-
} else {
174-
throw new Error(`ERROR: ASTParser.selectionNode: node type not supported`);
175207
}
176-
177-
this.depth -= 1;
178208
return complexity;
179209
}
180210

test/analysis/typeComplexityAnalysis.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ describe('Test getQueryTypeComplexity function', () => {
855855
});
856856

857857
// TODO: refine complexity analysis to consider directives includes and skip
858-
xdescribe('with directives @includes and @skip', () => {
858+
describe('with directives @includes and @skip', () => {
859859
test('@includes on interfaces', () => {
860860
query = `
861861
query {
@@ -990,7 +990,7 @@ describe('Test getQueryTypeComplexity function', () => {
990990
test('with arguments and varibales', () => {
991991
variables = { directive: false };
992992
queryParser = new ASTParser(typeWeights, variables);
993-
query = `query (directive: $Boolean!){
993+
query = `query ($directive: Boolean!){
994994
hero(episode: EMPIRE) {
995995
id, name
996996
}
@@ -1002,8 +1002,9 @@ describe('Test getQueryTypeComplexity function', () => {
10021002
}`;
10031003
// 1 query + 1 hero + 1 human
10041004
expect(queryParser.processQuery(parse(query))).toBe(3);
1005-
1006-
query = `query (directive: $Boolean!){
1005+
variables = { directive: true };
1006+
queryParser = new ASTParser(typeWeights, variables);
1007+
query = `query ($directive: Boolean!){
10071008
hero(episode: EMPIRE) {
10081009
id, name
10091010
}

0 commit comments

Comments
 (0)