11import { GraphQLError } from 'graphql' ;
22import logger from '../../logger' ;
33
4- function calculateQueryComplexity ( operation , fragments ) {
4+ function calculateQueryComplexity ( operation , fragments , limits = { } ) {
55 let maxDepth = 0 ;
66 let totalFields = 0 ;
7+ const fragmentCache = new Map ( ) ;
8+ const { maxDepth : allowedMaxDepth , maxFields : allowedMaxFields } = limits ;
79
810 function visitSelectionSet ( selectionSet , depth , visitedFragments ) {
911 if ( ! selectionSet ) {
1012 return ;
1113 }
14+ if (
15+ ( allowedMaxFields !== undefined && allowedMaxFields !== - 1 && totalFields > allowedMaxFields ) ||
16+ ( allowedMaxDepth !== undefined && allowedMaxDepth !== - 1 && maxDepth > allowedMaxDepth )
17+ ) {
18+ return ;
19+ }
1220 for ( const selection of selectionSet . selections ) {
1321 if ( selection . kind === 'Field' ) {
1422 totalFields ++ ;
@@ -23,14 +31,30 @@ function calculateQueryComplexity(operation, fragments) {
2331 visitSelectionSet ( selection . selectionSet , depth , visitedFragments ) ;
2432 } else if ( selection . kind === 'FragmentSpread' ) {
2533 const name = selection . name . value ;
34+ if ( fragmentCache . has ( name ) ) {
35+ const cached = fragmentCache . get ( name ) ;
36+ totalFields += cached . fields ;
37+ const adjustedDepth = depth + cached . maxDepthDelta ;
38+ if ( adjustedDepth > maxDepth ) {
39+ maxDepth = adjustedDepth ;
40+ }
41+ continue ;
42+ }
2643 if ( visitedFragments . has ( name ) ) {
2744 continue ;
2845 }
2946 const fragment = fragments [ name ] ;
3047 if ( fragment ) {
31- const branchVisited = new Set ( visitedFragments ) ;
32- branchVisited . add ( name ) ;
33- visitSelectionSet ( fragment . selectionSet , depth , branchVisited ) ;
48+ visitedFragments . add ( name ) ;
49+ const savedFields = totalFields ;
50+ const savedMaxDepth = maxDepth ;
51+ maxDepth = depth ;
52+ visitSelectionSet ( fragment . selectionSet , depth , visitedFragments ) ;
53+ const fieldsContribution = totalFields - savedFields ;
54+ const maxDepthDelta = maxDepth - depth ;
55+ fragmentCache . set ( name , { fields : fieldsContribution , maxDepthDelta } ) ;
56+ maxDepth = Math . max ( savedMaxDepth , maxDepth ) ;
57+ visitedFragments . delete ( name ) ;
3458 }
3559 }
3660 }
@@ -69,7 +93,8 @@ function createComplexityValidationPlugin(getConfig) {
6993
7094 const { depth, fields } = calculateQueryComplexity (
7195 requestContext . operation ,
72- fragments
96+ fragments ,
97+ { maxDepth : graphQLDepth , maxFields : graphQLFields }
7398 ) ;
7499
75100 if ( graphQLDepth !== - 1 && depth > graphQLDepth ) {
0 commit comments