@@ -19,7 +19,7 @@ import {
1919 GraphQLField , isCompositeType , GraphQLCompositeType , GraphQLFieldMap ,
2020 GraphQLSchema , DocumentNode , TypeInfo ,
2121 visit , visitWithTypeInfo ,
22- GraphQLDirective ,
22+ GraphQLDirective , isAbstractType ,
2323} from 'graphql' ;
2424import {
2525 GraphQLUnionType ,
@@ -48,6 +48,11 @@ export type ComplexityEstimator = (options: ComplexityEstimatorArgs) => number |
4848// Complexity can be anything that is supported by the configured estimators
4949export type Complexity = any ;
5050
51+ // Map of complexities for possible types (of Union, Interface types)
52+ type ComplexityMap = {
53+ [ typeName : string ] : number ,
54+ }
55+
5156export interface QueryComplexityOptions {
5257 // The maximum allowed query complexity, queries above this threshold will be rejected
5358 maximumComplexity : number ,
@@ -179,16 +184,25 @@ export default class QueryComplexity {
179184 nodeComplexity (
180185 node : FieldNode | FragmentDefinitionNode | InlineFragmentNode | OperationDefinitionNode ,
181186 typeDef : GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType ,
182- complexity : number = 0
183187 ) : number {
184188 if ( node . selectionSet ) {
185189 let fields :GraphQLFieldMap < any , any > = { } ;
186190 if ( typeDef instanceof GraphQLObjectType || typeDef instanceof GraphQLInterfaceType ) {
187191 fields = typeDef . getFields ( ) ;
188192 }
189- return complexity + node . selectionSet . selections . reduce (
190- ( total : number , childNode : FieldNode | FragmentSpreadNode | InlineFragmentNode ) => {
191- let nodeComplexity = 0 ;
193+
194+ // Determine all possible types of the current node
195+ let possibleTypeNames : string [ ] ;
196+ if ( isAbstractType ( typeDef ) ) {
197+ possibleTypeNames = this . context . getSchema ( ) . getPossibleTypes ( typeDef ) . map ( t => t . name ) ;
198+ } else {
199+ possibleTypeNames = [ typeDef . name ] ;
200+ }
201+
202+ // Collect complexities for all possible types individually
203+ const selectionSetComplexities : ComplexityMap = node . selectionSet . selections . reduce (
204+ ( complexities : ComplexityMap , childNode : FieldNode | FragmentSpreadNode | InlineFragmentNode ) => {
205+ // let nodeComplexity = 0;
192206
193207 let includeNode = true ;
194208 let skipNode = false ;
@@ -210,7 +224,7 @@ export default class QueryComplexity {
210224 } ) ;
211225
212226 if ( ! includeNode || skipNode ) {
213- return total ;
227+ return complexities ;
214228 }
215229
216230 switch ( childNode . kind ) {
@@ -248,7 +262,11 @@ export default class QueryComplexity {
248262 const tmpComplexity = estimator ( estimatorArgs ) ;
249263
250264 if ( typeof tmpComplexity === 'number' && ! isNaN ( tmpComplexity ) ) {
251- nodeComplexity = tmpComplexity ;
265+ complexities = addComplexities (
266+ tmpComplexity ,
267+ complexities ,
268+ possibleTypeNames ,
269+ ) ;
252270 return true ;
253271 }
254272
@@ -269,30 +287,69 @@ export default class QueryComplexity {
269287 const fragmentType = assertCompositeType (
270288 this . context . getSchema ( ) . getType ( fragment . typeCondition . name . value )
271289 ) ;
272- nodeComplexity = this . nodeComplexity ( fragment , fragmentType ) ;
290+ const nodeComplexity = this . nodeComplexity ( fragment , fragmentType ) ;
291+ if ( isAbstractType ( fragmentType ) ) {
292+ // Add fragment complexity for all possible types
293+ complexities = addComplexities (
294+ nodeComplexity ,
295+ complexities ,
296+ this . context . getSchema ( ) . getPossibleTypes ( fragmentType ) . map ( t => t . name ) ,
297+ ) ;
298+ } else {
299+ // Add complexity for object type
300+ complexities = addComplexities (
301+ nodeComplexity ,
302+ complexities ,
303+ [ fragmentType . name ] ,
304+ ) ;
305+ }
273306 break ;
274307 }
275308 case Kind . INLINE_FRAGMENT : {
276309 let inlineFragmentType = typeDef ;
277310 if ( childNode . typeCondition && childNode . typeCondition . name ) {
278- // $FlowFixMe: Not sure why flow thinks this can still be NULL
279311 inlineFragmentType = assertCompositeType (
280312 this . context . getSchema ( ) . getType ( childNode . typeCondition . name . value )
281313 ) ;
282314 }
283315
284- nodeComplexity = this . nodeComplexity ( childNode , inlineFragmentType ) ;
316+ const nodeComplexity = this . nodeComplexity ( childNode , inlineFragmentType ) ;
317+ if ( isAbstractType ( inlineFragmentType ) ) {
318+ // Add fragment complexity for all possible types
319+ complexities = addComplexities (
320+ nodeComplexity ,
321+ complexities ,
322+ this . context . getSchema ( ) . getPossibleTypes ( inlineFragmentType ) . map ( t => t . name ) ,
323+ ) ;
324+ } else {
325+ // Add complexity for object type
326+ complexities = addComplexities (
327+ nodeComplexity ,
328+ complexities ,
329+ [ inlineFragmentType . name ] ,
330+ ) ;
331+ }
285332 break ;
286333 }
287334 default : {
288- nodeComplexity = this . nodeComplexity ( childNode , typeDef ) ;
335+ complexities = addComplexities (
336+ this . nodeComplexity ( childNode , typeDef ) ,
337+ complexities ,
338+ possibleTypeNames ,
339+ ) ;
289340 break ;
290341 }
291342 }
292- return Math . max ( nodeComplexity , 0 ) + total ;
293- } , complexity ) ;
343+
344+ return complexities ;
345+ } , { } ) ;
346+ // Only return max complexity of all possible types
347+ if ( ! selectionSetComplexities ) {
348+ return NaN ;
349+ }
350+ return Math . max ( ...Object . values ( selectionSetComplexities ) , 0 ) ;
294351 }
295- return complexity ;
352+ return 0 ;
296353 }
297354
298355 createError ( ) : GraphQLError {
@@ -308,3 +365,24 @@ export default class QueryComplexity {
308365 ) ) ;
309366 }
310367}
368+
369+ /**
370+ * Adds a complexity to the complexity map for all possible types
371+ * @param complexity
372+ * @param complexityMap
373+ * @param possibleTypes
374+ */
375+ function addComplexities (
376+ complexity : number ,
377+ complexityMap : ComplexityMap ,
378+ possibleTypes : string [ ] ,
379+ ) : ComplexityMap {
380+ for ( const type of possibleTypes ) {
381+ if ( complexityMap . hasOwnProperty ( type ) ) {
382+ complexityMap [ type ] = complexityMap [ type ] + complexity ;
383+ } else {
384+ complexityMap [ type ] = complexity ;
385+ }
386+ }
387+ return complexityMap ;
388+ }
0 commit comments