11package graphql .analysis ;
22
3+ import graphql .ExecutionResult ;
34import graphql .PublicApi ;
45import graphql .execution .AbortExecutionException ;
6+ import graphql .execution .ExecutionContext ;
57import graphql .execution .instrumentation .InstrumentationContext ;
8+ import graphql .execution .instrumentation .InstrumentationState ;
69import graphql .execution .instrumentation .SimpleInstrumentation ;
10+ import graphql .execution .instrumentation .parameters .InstrumentationCreateStateParameters ;
11+ import graphql .execution .instrumentation .parameters .InstrumentationExecuteOperationParameters ;
712import graphql .execution .instrumentation .parameters .InstrumentationValidationParameters ;
813import graphql .validation .ValidationError ;
914import org .slf4j .Logger ;
1217import java .util .LinkedHashMap ;
1318import java .util .List ;
1419import java .util .Map ;
20+ import java .util .concurrent .atomic .AtomicReference ;
1521import java .util .function .Function ;
1622
1723import static graphql .Assert .assertNotNull ;
18- import static graphql .execution .instrumentation .SimpleInstrumentationContext .whenCompleted ;
24+ import static graphql .execution .instrumentation .SimpleInstrumentationContext .noOp ;
1925import static java .util .Optional .ofNullable ;
2026
2127/**
@@ -76,41 +82,52 @@ public MaxQueryComplexityInstrumentation(int maxComplexity, FieldComplexityCalcu
7682 this .maxQueryComplexityExceededFunction = maxQueryComplexityExceededFunction ;
7783 }
7884
85+ @ Override
86+ public InstrumentationState createState (InstrumentationCreateStateParameters parameters ) {
87+ return new State ();
88+ }
89+
7990 @ Override
8091 public InstrumentationContext <List <ValidationError >> beginValidation (InstrumentationValidationParameters parameters ) {
81- return whenCompleted ((errors , throwable ) -> {
82- if ((errors != null && errors .size () > 0 ) || throwable != null ) {
83- return ;
84- }
85- QueryTraverser queryTraverser = newQueryTraverser (parameters );
86-
87- Map <QueryVisitorFieldEnvironment , Integer > valuesByParent = new LinkedHashMap <>();
88- queryTraverser .visitPostOrder (new QueryVisitorStub () {
89- @ Override
90- public void visitField (QueryVisitorFieldEnvironment env ) {
91- int childsComplexity = valuesByParent .getOrDefault (env , 0 );
92- int value = calculateComplexity (env , childsComplexity );
93-
94- valuesByParent .compute (env .getParentEnvironment (), (key , oldValue ) ->
95- ofNullable (oldValue ).orElse (0 ) + value
96- );
97- }
98- });
99- int totalComplexity = valuesByParent .getOrDefault (null , 0 );
100- if (log .isDebugEnabled ()) {
101- log .debug ("Query complexity: {}" , totalComplexity );
102- }
103- if (totalComplexity > maxComplexity ) {
104- QueryComplexityInfo queryComplexityInfo = QueryComplexityInfo .newQueryComplexityInfo ()
105- .complexity (totalComplexity )
106- .instrumentationValidationParameters (parameters )
107- .build ();
108- boolean throwAbortException = maxQueryComplexityExceededFunction .apply (queryComplexityInfo );
109- if (throwAbortException ) {
110- throw mkAbortException (totalComplexity , maxComplexity );
111- }
92+ State state = parameters .getInstrumentationState ();
93+ // for API backwards compatibility reasons we capture the validation parameters, so we can put them into QueryComplexityInfo
94+ state .instrumentationValidationParameters .set (parameters );
95+ return noOp ();
96+ }
97+
98+ @ Override
99+ public InstrumentationContext <ExecutionResult > beginExecuteOperation (InstrumentationExecuteOperationParameters instrumentationExecuteOperationParameters ) {
100+ State state = instrumentationExecuteOperationParameters .getInstrumentationState ();
101+ QueryTraverser queryTraverser = newQueryTraverser (instrumentationExecuteOperationParameters .getExecutionContext ());
102+
103+ Map <QueryVisitorFieldEnvironment , Integer > valuesByParent = new LinkedHashMap <>();
104+ queryTraverser .visitPostOrder (new QueryVisitorStub () {
105+ @ Override
106+ public void visitField (QueryVisitorFieldEnvironment env ) {
107+ int childsComplexity = valuesByParent .getOrDefault (env , 0 );
108+ int value = calculateComplexity (env , childsComplexity );
109+
110+ valuesByParent .compute (env .getParentEnvironment (), (key , oldValue ) ->
111+ ofNullable (oldValue ).orElse (0 ) + value
112+ );
112113 }
113114 });
115+ int totalComplexity = valuesByParent .getOrDefault (null , 0 );
116+ if (log .isDebugEnabled ()) {
117+ log .debug ("Query complexity: {}" , totalComplexity );
118+ }
119+ if (totalComplexity > maxComplexity ) {
120+ QueryComplexityInfo queryComplexityInfo = QueryComplexityInfo .newQueryComplexityInfo ()
121+ .complexity (totalComplexity )
122+ .instrumentationValidationParameters (state .instrumentationValidationParameters .get ())
123+ .instrumentationExecuteOperationParameters (instrumentationExecuteOperationParameters )
124+ .build ();
125+ boolean throwAbortException = maxQueryComplexityExceededFunction .apply (queryComplexityInfo );
126+ if (throwAbortException ) {
127+ throw mkAbortException (totalComplexity , maxComplexity );
128+ }
129+ }
130+ return noOp ();
114131 }
115132
116133 /**
@@ -125,12 +142,12 @@ protected AbortExecutionException mkAbortException(int totalComplexity, int maxC
125142 return new AbortExecutionException ("maximum query complexity exceeded " + totalComplexity + " > " + maxComplexity );
126143 }
127144
128- QueryTraverser newQueryTraverser (InstrumentationValidationParameters parameters ) {
145+ QueryTraverser newQueryTraverser (ExecutionContext executionContext ) {
129146 return QueryTraverser .newQueryTraverser ()
130- .schema (parameters . getSchema ())
131- .document (parameters .getDocument ())
132- .operationName (parameters . getOperation ())
133- .variables (parameters .getVariables ())
147+ .schema (executionContext . getGraphQLSchema ())
148+ .document (executionContext .getDocument ())
149+ .operationName (executionContext . getExecutionInput (). getOperationName ())
150+ .variables (executionContext .getVariables ())
134151 .build ();
135152 }
136153
@@ -156,5 +173,8 @@ private FieldComplexityEnvironment convertEnv(QueryVisitorFieldEnvironment query
156173 );
157174 }
158175
176+ private static class State implements InstrumentationState {
177+ AtomicReference <InstrumentationValidationParameters > instrumentationValidationParameters = new AtomicReference <>();
178+ }
159179
160180}
0 commit comments