Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b404d8d

Browse files
edited ComplexityAnalysis fro readabitiy and exported
1 parent b9523c4 commit b404d8d

File tree

3 files changed

+45
-43
lines changed

3 files changed

+45
-43
lines changed

‎src/analysis/ASTParser.ts‎

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
Kind,
77
DirectiveNode,
88
SelectionNode,
9-
getArgumentValues,
109
} from 'graphql';
1110
import { FieldWeight, TypeWeightObject, Variables } from '../@types/buildTypeWeights';
1211
/**
@@ -19,26 +18,27 @@ import { FieldWeight, TypeWeightObject, Variables } from '../@types/buildTypeWei
1918
* |
2019
* Definiton Node
2120
* (operation and fragment definitons)
22-
* / \
23-
* |-----> Selection Set Node not done
21+
* / |
22+
* |-----> Selection Set Node <-------|
2423
* | /
2524
* | Selection Node
2625
* | (Field, Inline fragment and fragment spread)
27-
* | | \ \
28-
* |--Field Node not done not done
29-
*
26+
* | | | \
27+
* |--Field Node | fragmentCache
28+
* | |
29+
* |-------------------|
3030
*/
3131

32-
class ASTParser {
33-
typeWeights: TypeWeightObject;
32+
class ComplexityAnalysis {
33+
privatetypeWeights: TypeWeightObject;
3434

35-
depth: number;
35+
privatedepth: number;
3636

37-
maxDepth: number;
37+
publicmaxDepth: number;
3838

39-
variables: Variables;
39+
privatevariables: Variables;
4040

41-
fragmentCache: { [index: string]: { complexity: number; depth: number } };
41+
privatefragmentCache: { [index: string]: { complexity: number; depth: number } };
4242

4343
constructor(typeWeights: TypeWeightObject, variables: Variables) {
4444
this.typeWeights = typeWeights;
@@ -59,7 +59,6 @@ class ASTParser {
5959
let selectionsCost = 0;
6060
let calculatedWeight = 0;
6161

62-
// call the function to handle selection set node with selectionSet property if it is not undefined
6362
if (node.selectionSet) {
6463
selectionsCost += this.selectionSetNode(node.selectionSet, typeName);
6564
}
@@ -80,22 +79,26 @@ class ASTParser {
8079
private fieldNode(node: FieldNode, parentName: string): number {
8180
try {
8281
let complexity = 0;
82+
// the node must have a parent in typeweights or the analysis will fail. this should never happen
8383
const parentType = this.typeWeights[parentName];
8484
if (!parentType) {
8585
throw new Error(
8686
`ERROR: ASTParser Failed to obtain parentType for parent: ${parentName} and node: ${node.name.value}`
8787
);
8888
}
89+
8990
let typeName: string | undefined;
9091
let typeWeight: FieldWeight | undefined;
91-
if (node.name.value === '__typename') return complexity;
92+
93+
if (node.name.value === '__typename') return complexity; // this will be zero, ie. this field has no complexity
94+
9295
if (node.name.value in this.typeWeights) {
93-
// node is an object type n the typeWeight root
96+
// node is an object type in the typeWeight root
9497
typeName = node.name.value;
9598
typeWeight = this.typeWeights[typeName].weight;
9699
complexity += this.calculateCost(node, parentName, typeName, typeWeight);
97100
} else if (parentType.fields[node.name.value].resolveTo) {
98-
// field resolves to another type in type weights or a list
101+
// node is a field on a typeWeight root, field resolves to another type in type weights or a list
99102
typeName = parentType.fields[node.name.value].resolveTo;
100103
typeWeight = parentType.fields[node.name.value].weight;
101104
// if this is a list typeWeight is a weight function
@@ -147,7 +150,8 @@ class ASTParser {
147150
* 2. there is a directive named inlcude and the value is true
148151
* 3. there is a directive named skip and the value is false
149152
*/
150-
directiveCheck(directive: DirectiveNode): boolean {
153+
// THIS IS NOT CALLED ANYWEHERE. IN PROGRESS
154+
private directiveCheck(directive: DirectiveNode): boolean {
151155
if (directive?.arguments) {
152156
// get the first argument
153157
const argument = directive.arguments[0];
@@ -172,8 +176,9 @@ class ASTParser {
172176

173177
private selectionNode(node: SelectionNode, parentName: string): number {
174178
let complexity = 0;
179+
// TODO: complete implementation of directives include and skip
175180
/**
176-
* process this node if:
181+
* process this node only if:
177182
* 1. there is no directive
178183
* 2. there is a directive named inlcude and the value is true
179184
* 3. there is a directive named skip and the value is false
@@ -182,9 +187,8 @@ class ASTParser {
182187
// if (directive && this.directiveCheck(directive[0])) {
183188
this.depth += 1;
184189
if (this.depth > this.maxDepth) this.maxDepth = this.depth;
185-
// check the kind property against the set of selection nodes that are possible
190+
// the kind of a field node will either be field, fragment spread or inline fragment
186191
if (node.kind === Kind.FIELD) {
187-
// call the function that handle field nodes
188192
complexity += this.fieldNode(node, parentName.toLowerCase());
189193
} else if (node.kind === Kind.FRAGMENT_SPREAD) {
190194
// add complexity and depth from fragment cache
@@ -214,25 +218,23 @@ class ASTParser {
214218
}
215219

216220
this.depth -= 1;
217-
// }
221+
//* }
218222
return complexity;
219223
}
220224

221225
private selectionSetNode(node: SelectionSetNode, parentName: string): number {
222226
let complexity = 0;
223227
let maxFragmentComplexity = 0;
224-
// iterate shrough the 'selections' array on the seletion set node
225228
for (let i = 0; i < node.selections.length; i += 1) {
226-
// call the function to handle seletion nodes
227229
// pass the current parent through because selection sets act only as intermediaries
228230
const selectionNode = node.selections[i];
229-
const selectionCost = this.selectionNode(node.selections[i], parentName);
231+
const selectionCost = this.selectionNode(selectionNode, parentName);
230232

231233
// we need to get the largest possible complexity so we save the largest inline fragment
232-
// FIXME: Consider the case where 2 typed fragments are applicable
233234
// e.g. ...UnionType and ...PartofTheUnion
234235
// this case these complexities should be summed in order to be accurate
235236
// However an estimation suffice
237+
// FIXME: Consider the case where 2 typed fragments are applicable
236238
if (selectionNode.kind === Kind.INLINE_FRAGMENT) {
237239
if (!selectionNode.typeCondition) {
238240
// complexity is always applicable
@@ -248,22 +250,17 @@ class ASTParser {
248250

249251
private definitionNode(node: DefinitionNode): number {
250252
let complexity = 0;
251-
// check the kind property against the set of definiton nodes that are possible
253+
// Operation definition is either query, mutation or subscripiton
252254
if (node.kind === Kind.OPERATION_DEFINITION) {
253-
// check if the operation is in the type weights object.
254255
if (node.operation.toLocaleLowerCase() in this.typeWeights) {
255-
// if it is, it is an object type, add it's type weight to the total
256256
complexity += this.typeWeights[node.operation].weight;
257-
// console.log(`the weight of ${node.operation} is ${complexity}`);
258-
// call the function to handle selection set node with selectionSet property if it is not undefined
259257
if (node.selectionSet) {
260258
complexity += this.selectionSetNode(node.selectionSet, node.operation);
261259
}
262260
}
263261
} else if (node.kind === Kind.FRAGMENT_DEFINITION) {
264262
// Fragments can only be defined on the root type.
265-
// Parse the complexity of this fragment once and store it for use when analyzing other
266-
// nodes. The complexity of a fragment can be added to the selection cost for the query.
263+
// Parse the complexity of this fragment once and store it for use when analyzing other nodes
267264
const namedType = node.typeCondition.name.value;
268265
// Duplicate fragment names are not allowed by the GraphQL spec and an error is thrown if used.
269266
const fragmentName = node.name.value;
@@ -276,10 +273,12 @@ class ASTParser {
276273
// Don't count fragment complexity in the node's complexity. Only when fragment is used.
277274
this.fragmentCache[fragmentName] = {
278275
complexity: fragmentComplexity,
279-
depth: this.maxDepth - 1, // subtract one from the calculated depth of the fragment to correct for the additional depth the fragment ads to the query when used
276+
depth: this.maxDepth - 1, // subtract one from the calculated depth of the fragment to correct for the additional depth the fragment adds to the query when used
280277
};
281-
} // else {
282-
// // TODO: Verify that are no other type definition nodes that need to be handled (see ast.d.ts in 'graphql')
278+
}
279+
// TODO: Verify that there are no other type definition nodes that need to be handled (see ast.d.ts in 'graphql')
280+
// else {
281+
//
283282
// // Other types include TypeSystemDefinitionNode (Schema, Type, Directvie) and
284283
// // TypeSystemExtensionNode(Schema, Type);
285284
// throw new Error(`ERROR: ASTParser.definitionNode: ${node.kind} type not supported`);
@@ -289,21 +288,20 @@ class ASTParser {
289288

290289
private documentNode(node: DocumentNode): number {
291290
let complexity = 0;
292-
// sort the definitions array by kind so that fragments are always parsed first.
291+
// Sort the definitions array by kind so that fragments are always parsed first.
293292
// Fragments must be parsed first so that their complexity is available to other nodes.
294293
const sortedDefinitions = [...node.definitions].sort((a, b) =>
295294
a.kind.localeCompare(b.kind)
296295
);
297296
for (let i = 0; i < sortedDefinitions.length; i += 1) {
298-
// call the function to handle the various types of definition nodes
299297
complexity += this.definitionNode(sortedDefinitions[i]);
300298
}
301299
return complexity;
302300
}
303301

304-
processQuery(queryAST: DocumentNode): number {
302+
publicprocessQuery(queryAST: DocumentNode): number {
305303
return this.documentNode(queryAST);
306304
}
307305
}
308306

309-
export default ASTParser;
307+
export default ComplexityAnalysis;

‎src/index.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
export { default as expressMiddleware } from './middleware/index.js';
1+
export { default as expressGraphQLRateLimiter } from './middleware/index';
22

3-
export { default as rateLimiter } from './middleware/rateLimiterSetup.js';
3+
export { default as rateLimiter } from './middleware/rateLimiterSetup';
4+
5+
export { default as ComplexityAnalysis } from './analysis/ASTParser';
6+
7+
export { default as typeWeightsFromSchema } from './analysis/buildTypeWeights';

‎src/middleware/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import setupRateLimiter from './rateLimiterSetup';
77
import { ExpressMiddlewareConfig, ExpressMiddlewareSet } from '../@types/expressMiddleware';
88
import { RateLimiterResponse } from '../@types/rateLimit';
99
import { connect } from '../utils/redis';
10-
import ASTParser from '../analysis/ASTParser';
10+
import ComplexityAnalysis from '../analysis/ASTParser';
1111

1212
/**
1313
* Primary entry point for adding GraphQL Rate Limiting middleware to an Express Server
@@ -165,7 +165,7 @@ export default function expressGraphQLRateLimiter(
165165
res.status(400).json({ errors: validationErrors });
166166
}
167167

168-
const queryParser = new ASTParser(typeWeightObject, variables);
168+
const queryParser = new ComplexityAnalysis(typeWeightObject, variables);
169169
const queryComplexity = queryParser.processQuery(queryAST);
170170

171171
try {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /