Class: GraphQL::Analysis::QueryComplexity

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/analysis/query_complexity.rb

Overview

Calculate the complexity of a query, using Field#complexity values.

Examples:

Log the complexity of incoming queries

MySchema.query_analyzers << GraphQL::AnalysisQueryComplexity.new do |query, complexity|
  Rails.logger.info("Complexity: #{complexity}")
end

Direct Known Subclasses

MaxQueryComplexity

Defined Under Namespace

Classes: TypeComplexity

Instance Method Summary collapse

Constructor Details

#initialize {|query, complexity| ... } ⇒ QueryComplexity

Returns a new instance of QueryComplexity.

Yields:

  • (query, complexity)

    Called for each query analyzed by the schema, before executing it

Yield Parameters:

  • query (GraphQL::Query)

    The query that was analyzed

  • complexity (Numeric)

    The complexity for this query



14
15
16
# File 'lib/graphql/analysis/query_complexity.rb', line 14

def initialize(&block)
  @complexity_handler = block
end

Instance Method Details

#call(memo, visit_type, irep_node) ⇒ Object

Implement the query analyzer API



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/graphql/analysis/query_complexity.rb', line 32

def call(memo, visit_type, irep_node)
  if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
    if visit_type == :enter
      if irep_node.skipped?
        memo[:skip_depth] += 1
      elsif memo[:skip_depth] == 0
        memo[:complexities_on_type].push(TypeComplexity.new)
      end
    else
      if memo[:skip_depth] > 0
        if irep_node.skipped?
          memo[:skip_depth] -= 1
        end
      else
        type_complexities = memo[:complexities_on_type].pop
        child_complexity = type_complexities.max_possible_complexity
        own_complexity = get_complexity(irep_node, memo[:query], child_complexity)
        memo[:complexities_on_type].last.merge(irep_node.definitions, own_complexity)
      end
    end
  end
  memo
end

#final_value(reduced_value) ⇒ Object, GraphQL::AnalysisError

Send the query and complexity to the block

Returns:



58
59
60
61
# File 'lib/graphql/analysis/query_complexity.rb', line 58

def final_value(reduced_value)
  total_complexity = reduced_value[:complexities_on_type].pop.max_possible_complexity
  @complexity_handler.call(reduced_value[:query], total_complexity)
end

#initial_value(query) ⇒ Object

State for the query complexity calcuation:

  • ‘query` is needed for variables, then passed to handler

  • ‘complexities_on_type` holds complexity scores for each type in an IRep node

  • ‘skip_depth` increments for each skipped node, then decrements on the way out. While it’s greater than ‘0`, we’re visiting a skipped part of the query.



23
24
25
26
27
28
29
# File 'lib/graphql/analysis/query_complexity.rb', line 23

def initial_value(query)
  {
    query: query,
    complexities_on_type: [TypeComplexity.new],
    skip_depth: 0,
  }
end