graphql 2.2.5 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/schema.erb +3 -0
- data/lib/graphql/analysis/ast/field_usage.rb +36 -9
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime.rb +9 -0
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +2 -3
- data/lib/graphql/language/lexer.rb +48 -30
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +25 -11
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +60 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +3 -3
- data/lib/graphql/schema/argument.rb +1 -0
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/input_object.rb +1 -2
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/resolver.rb +19 -10
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +119 -28
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +32 -6
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +2 -2
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -2
- metadata +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
| @@ -3,7 +3,7 @@ module GraphQL | |
| 3 3 | 
             
              module Language
         | 
| 4 4 |  | 
| 5 5 | 
             
                class Lexer
         | 
| 6 | 
            -
                  def initialize(graphql_str, filename: nil)
         | 
| 6 | 
            +
                  def initialize(graphql_str, filename: nil, max_tokens: nil)
         | 
| 7 7 | 
             
                    if !(graphql_str.encoding == Encoding::UTF_8 || graphql_str.ascii_only?)
         | 
| 8 8 | 
             
                      graphql_str = graphql_str.dup.force_encoding(Encoding::UTF_8)
         | 
| 9 9 | 
             
                    end
         | 
| @@ -11,6 +11,8 @@ module GraphQL | |
| 11 11 | 
             
                    @filename = filename
         | 
| 12 12 | 
             
                    @scanner = StringScanner.new(graphql_str)
         | 
| 13 13 | 
             
                    @pos = nil
         | 
| 14 | 
            +
                    @max_tokens = max_tokens || Float::INFINITY
         | 
| 15 | 
            +
                    @tokens_count = 0
         | 
| 14 16 | 
             
                  end
         | 
| 15 17 |  | 
| 16 18 | 
             
                  def eos?
         | 
| @@ -22,6 +24,10 @@ module GraphQL | |
| 22 24 | 
             
                  def advance
         | 
| 23 25 | 
             
                    @scanner.skip(IGNORE_REGEXP)
         | 
| 24 26 | 
             
                    return false if @scanner.eos?
         | 
| 27 | 
            +
                    @tokens_count += 1
         | 
| 28 | 
            +
                    if @tokens_count > @max_tokens
         | 
| 29 | 
            +
                      raise_parse_error("This query is too large to execute.")
         | 
| 30 | 
            +
                    end
         | 
| 25 31 | 
             
                    @pos = @scanner.pos
         | 
| 26 32 | 
             
                    next_byte = @string.getbyte(@pos)
         | 
| 27 33 | 
             
                    next_byte_is_for = FIRST_BYTES[next_byte]
         | 
| @@ -52,6 +58,17 @@ module GraphQL | |
| 52 58 | 
             
                      :IDENTIFIER
         | 
| 53 59 | 
             
                    when ByteFor::NUMBER
         | 
| 54 60 | 
             
                      @scanner.skip(NUMERIC_REGEXP)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      if GraphQL.reject_numbers_followed_by_names
         | 
| 63 | 
            +
                        new_pos = @scanner.pos
         | 
| 64 | 
            +
                        peek_byte = @string.getbyte(new_pos)
         | 
| 65 | 
            +
                        next_first_byte = FIRST_BYTES[peek_byte]
         | 
| 66 | 
            +
                        if next_first_byte == ByteFor::NAME || next_first_byte == ByteFor::IDENTIFIER
         | 
| 67 | 
            +
                          number_part = token_value
         | 
| 68 | 
            +
                          name_part = @scanner.scan(IDENTIFIER_REGEXP)
         | 
| 69 | 
            +
                          raise_parse_error("Name after number is not allowed (in `#{number_part}#{name_part}`)")
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
                      end
         | 
| 55 72 | 
             
                      # Check for a matched decimal:
         | 
| 56 73 | 
             
                      @scanner[1] ? :FLOAT : :INT
         | 
| 57 74 | 
             
                    when ByteFor::ELLIPSIS
         | 
| @@ -89,6 +106,8 @@ module GraphQL | |
| 89 106 | 
             
                      "..."
         | 
| 90 107 | 
             
                    elsif token_name == :STRING
         | 
| 91 108 | 
             
                      string_value
         | 
| 109 | 
            +
                    elsif @scanner.matched_size.nil?
         | 
| 110 | 
            +
                      @scanner.peek(1)
         | 
| 92 111 | 
             
                    else
         | 
| 93 112 | 
             
                      token_value
         | 
| 94 113 | 
             
                    end
         | 
| @@ -107,29 +126,27 @@ module GraphQL | |
| 107 126 | 
             
                  }
         | 
| 108 127 | 
             
                  UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i
         | 
| 109 128 | 
             
                  VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
         | 
| 129 | 
            +
                  ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o
         | 
| 110 130 |  | 
| 111 131 | 
             
                  def string_value
         | 
| 112 132 | 
             
                    str = token_value
         | 
| 113 133 | 
             
                    is_block = str.start_with?('"""')
         | 
| 114 134 | 
             
                    if is_block
         | 
| 115 135 | 
             
                      str.gsub!(/\A"""|"""\z/, '')
         | 
| 136 | 
            +
                      return Language::BlockString.trim_whitespace(str)
         | 
| 116 137 | 
             
                    else
         | 
| 117 138 | 
             
                      str.gsub!(/\A"|"\z/, '')
         | 
| 118 | 
            -
                    end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                    if is_block
         | 
| 121 | 
            -
                      str = Language::BlockString.trim_whitespace(str)
         | 
| 122 | 
            -
                    end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                    if !str.valid_encoding? || !str.match?(VALID_STRING)
         | 
| 125 | 
            -
                      raise_parse_error("Bad unicode escape in #{str.inspect}")
         | 
| 126 | 
            -
                    else
         | 
| 127 | 
            -
                      Lexer.replace_escaped_characters_in_place(str)
         | 
| 128 139 |  | 
| 129 | 
            -
                      if !str.valid_encoding?
         | 
| 140 | 
            +
                      if !str.valid_encoding? || !str.match?(VALID_STRING)
         | 
| 130 141 | 
             
                        raise_parse_error("Bad unicode escape in #{str.inspect}")
         | 
| 131 142 | 
             
                      else
         | 
| 132 | 
            -
                        str
         | 
| 143 | 
            +
                        Lexer.replace_escaped_characters_in_place(str)
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                        if !str.valid_encoding?
         | 
| 146 | 
            +
                          raise_parse_error("Bad unicode escape in #{str.inspect}")
         | 
| 147 | 
            +
                        else
         | 
| 148 | 
            +
                          str
         | 
| 149 | 
            +
                        end
         | 
| 133 150 | 
             
                      end
         | 
| 134 151 | 
             
                    end
         | 
| 135 152 | 
             
                  end
         | 
| @@ -156,6 +173,7 @@ module GraphQL | |
| 156 173 | 
             
                  INT_REGEXP =        /-?(?:[0]|[1-9][0-9]*)/
         | 
| 157 174 | 
             
                  FLOAT_DECIMAL_REGEXP = /[.][0-9]+/
         | 
| 158 175 | 
             
                  FLOAT_EXP_REGEXP =     /[eE][+-]?[0-9]+/
         | 
| 176 | 
            +
                  # TODO: FLOAT_EXP_REGEXP should not be allowed to follow INT_REGEXP, integers are not allowed to have exponent parts.
         | 
| 159 177 | 
             
                  NUMERIC_REGEXP =  /#{INT_REGEXP}(#{FLOAT_DECIMAL_REGEXP}#{FLOAT_EXP_REGEXP}|#{FLOAT_DECIMAL_REGEXP}|#{FLOAT_EXP_REGEXP})?/
         | 
| 160 178 |  | 
| 161 179 | 
             
                  KEYWORDS = [
         | 
| @@ -250,11 +268,10 @@ module GraphQL | |
| 250 268 | 
             
                  FOUR_DIGIT_UNICODE = /#{UNICODE_DIGIT}{4}/
         | 
| 251 269 | 
             
                  N_DIGIT_UNICODE = %r{#{Punctuation::LCURLY}#{UNICODE_DIGIT}{4,}#{Punctuation::RCURLY}}x
         | 
| 252 270 | 
             
                  UNICODE_ESCAPE = %r{\\u(?:#{FOUR_DIGIT_UNICODE}|#{N_DIGIT_UNICODE})}
         | 
| 253 | 
            -
                  # # https://graphql.github.io/graphql-spec/June2018/#sec-String-Value
         | 
| 254 271 | 
             
                  STRING_ESCAPE = %r{[\\][\\/bfnrt]}
         | 
| 255 272 | 
             
                  BLOCK_QUOTE =   '"""'
         | 
| 256 273 | 
             
                  ESCAPED_QUOTE = /\\"/;
         | 
| 257 | 
            -
                  STRING_CHAR = /#{ESCAPED_QUOTE}|[^" | 
| 274 | 
            +
                  STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\\n\r]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/
         | 
| 258 275 | 
             
                  QUOTED_STRING_REGEXP = %r{#{QUOTE} (?:#{STRING_CHAR})* #{QUOTE}}x
         | 
| 259 276 | 
             
                  BLOCK_STRING_REGEXP = %r{
         | 
| 260 277 | 
             
                    #{BLOCK_QUOTE}
         | 
| @@ -299,24 +316,25 @@ module GraphQL | |
| 299 316 | 
             
                  # Replace any escaped unicode or whitespace with the _actual_ characters
         | 
| 300 317 | 
             
                  # To avoid allocating more strings, this modifies the string passed into it
         | 
| 301 318 | 
             
                  def self.replace_escaped_characters_in_place(raw_string)
         | 
| 302 | 
            -
                    raw_string.gsub!( | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 308 | 
            -
             | 
| 309 | 
            -
             | 
| 310 | 
            -
                            ( | 
| 311 | 
            -
             | 
| 312 | 
            -
                           | 
| 313 | 
            -
             | 
| 319 | 
            +
                    raw_string.gsub!(ESCAPED) do |matched_str|
         | 
| 320 | 
            +
                      if (point_str_1 = $1 || $2)
         | 
| 321 | 
            +
                        codepoint_1 = point_str_1.to_i(16)
         | 
| 322 | 
            +
                        if (codepoint_2 = $3)
         | 
| 323 | 
            +
                          codepoint_2 = codepoint_2.to_i(16)
         | 
| 324 | 
            +
                          if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
         | 
| 325 | 
            +
                              (codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
         | 
| 326 | 
            +
                            # A surrogate pair
         | 
| 327 | 
            +
                            combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
         | 
| 328 | 
            +
                            [combined].pack('U'.freeze)
         | 
| 329 | 
            +
                          else
         | 
| 330 | 
            +
                            # Two separate code points
         | 
| 331 | 
            +
                            [codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
         | 
| 332 | 
            +
                          end
         | 
| 314 333 | 
             
                        else
         | 
| 315 | 
            -
                           | 
| 316 | 
            -
                          [codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
         | 
| 334 | 
            +
                          [codepoint_1].pack('U'.freeze)
         | 
| 317 335 | 
             
                        end
         | 
| 318 336 | 
             
                      else
         | 
| 319 | 
            -
                        [ | 
| 337 | 
            +
                        ESCAPES_REPLACE[matched_str]
         | 
| 320 338 | 
             
                      end
         | 
| 321 339 | 
             
                    end
         | 
| 322 340 | 
             
                    nil
         | 
| @@ -512,7 +512,7 @@ module GraphQL | |
| 512 512 | 
             
                  # An operation-level query variable
         | 
| 513 513 | 
             
                  class VariableDefinition < AbstractNode
         | 
| 514 514 | 
             
                    scalar_methods :name, :type, :default_value
         | 
| 515 | 
            -
                    children_methods  | 
| 515 | 
            +
                    children_methods(directives: Directive)
         | 
| 516 516 | 
             
                    # @!attribute default_value
         | 
| 517 517 | 
             
                    #   @return [String, Integer, Float, Boolean, Array, NullValue] A Ruby value to use if no other value is provided
         | 
| 518 518 |  | 
| @@ -12,8 +12,8 @@ module GraphQL | |
| 12 12 | 
             
                  class << self
         | 
| 13 13 | 
             
                    attr_accessor :cache
         | 
| 14 14 |  | 
| 15 | 
            -
                    def parse(graphql_str, filename: nil, trace: Tracing::NullTrace)
         | 
| 16 | 
            -
                      self.new(graphql_str, filename: filename, trace: trace).parse
         | 
| 15 | 
            +
                    def parse(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil)
         | 
| 16 | 
            +
                      self.new(graphql_str, filename: filename, trace: trace, max_tokens: max_tokens).parse
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 19 | 
             
                    def parse_file(filename, trace: Tracing::NullTrace)
         | 
| @@ -27,14 +27,15 @@ module GraphQL | |
| 27 27 | 
             
                    end
         | 
| 28 28 | 
             
                  end
         | 
| 29 29 |  | 
| 30 | 
            -
                  def initialize(graphql_str, filename: nil, trace: Tracing::NullTrace)
         | 
| 30 | 
            +
                  def initialize(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil)
         | 
| 31 31 | 
             
                    if graphql_str.nil?
         | 
| 32 32 | 
             
                      raise GraphQL::ParseError.new("No query string was present", nil, nil, nil)
         | 
| 33 33 | 
             
                    end
         | 
| 34 | 
            -
                    @lexer = Lexer.new(graphql_str, filename: filename)
         | 
| 34 | 
            +
                    @lexer = Lexer.new(graphql_str, filename: filename, max_tokens: max_tokens)
         | 
| 35 35 | 
             
                    @graphql_str = graphql_str
         | 
| 36 36 | 
             
                    @filename = filename
         | 
| 37 37 | 
             
                    @trace = trace
         | 
| 38 | 
            +
                    @dedup_identifiers = false
         | 
| 38 39 | 
             
                  end
         | 
| 39 40 |  | 
| 40 41 | 
             
                  def parse
         | 
| @@ -121,7 +122,17 @@ module GraphQL | |
| 121 122 | 
             
                            value
         | 
| 122 123 | 
             
                          end
         | 
| 123 124 |  | 
| 124 | 
            -
                           | 
| 125 | 
            +
                          directives = parse_directives
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                          defs << Nodes::VariableDefinition.new(
         | 
| 128 | 
            +
                            pos: loc,
         | 
| 129 | 
            +
                            name: var_name,
         | 
| 130 | 
            +
                            type: var_type,
         | 
| 131 | 
            +
                            default_value: default_value,
         | 
| 132 | 
            +
                            directives: directives,
         | 
| 133 | 
            +
                            filename: @filename,
         | 
| 134 | 
            +
                            source_string: @graphql_str
         | 
| 135 | 
            +
                          )
         | 
| 125 136 | 
             
                        end
         | 
| 126 137 | 
             
                        expect_token(:RPAREN)
         | 
| 127 138 | 
             
                        defs
         | 
| @@ -722,6 +733,9 @@ module GraphQL | |
| 722 733 | 
             
                  # Only use when we care about the expected token's value
         | 
| 723 734 | 
             
                  def expect_token_value(tok)
         | 
| 724 735 | 
             
                    token_value = @lexer.token_value
         | 
| 736 | 
            +
                    if @dedup_identifiers
         | 
| 737 | 
            +
                      token_value = -token_value
         | 
| 738 | 
            +
                    end
         | 
| 725 739 | 
             
                    expect_token(tok)
         | 
| 726 740 | 
             
                    token_value
         | 
| 727 741 | 
             
                  end
         | 
| @@ -729,12 +743,12 @@ module GraphQL | |
| 729 743 | 
             
                  # token_value works for when the scanner matched something
         | 
| 730 744 | 
             
                  # which is usually fine and it's good for it to be fast at that.
         | 
| 731 745 | 
             
                  def debug_token_value
         | 
| 732 | 
            -
                     | 
| 733 | 
            -
             | 
| 734 | 
            -
             | 
| 735 | 
            -
             | 
| 736 | 
            -
             | 
| 737 | 
            -
                      @ | 
| 746 | 
            +
                    @lexer.debug_token_value(token_name)
         | 
| 747 | 
            +
                  end
         | 
| 748 | 
            +
                  class SchemaParser < Parser
         | 
| 749 | 
            +
                    def initialize(*args, **kwargs)
         | 
| 750 | 
            +
                      super
         | 
| 751 | 
            +
                      @dedup_identifiers = true
         | 
| 738 752 | 
             
                    end
         | 
| 739 753 | 
             
                  end
         | 
| 740 754 | 
             
                end
         | 
| @@ -208,6 +208,10 @@ module GraphQL | |
| 208 208 | 
             
                      print_string(" = ")
         | 
| 209 209 | 
             
                      print_node(variable_definition.default_value)
         | 
| 210 210 | 
             
                    end
         | 
| 211 | 
            +
                    variable_definition.directives.each do |dir|
         | 
| 212 | 
            +
                      print_string(" ")
         | 
| 213 | 
            +
                      print_directive(dir)
         | 
| 214 | 
            +
                    end
         | 
| 211 215 | 
             
                  end
         | 
| 212 216 |  | 
| 213 217 | 
             
                  def print_variable_identifier(variable_identifier)
         | 
    
        data/lib/graphql/language.rb
    CHANGED
    
    | @@ -12,6 +12,7 @@ require "graphql/language/static_visitor" | |
| 12 12 | 
             
            require "graphql/language/token"
         | 
| 13 13 | 
             
            require "graphql/language/visitor"
         | 
| 14 14 | 
             
            require "graphql/language/definition_slice"
         | 
| 15 | 
            +
            require "strscan"
         | 
| 15 16 |  | 
| 16 17 | 
             
            module GraphQL
         | 
| 17 18 | 
             
              module Language
         | 
| @@ -32,6 +33,65 @@ module GraphQL | |
| 32 33 | 
             
                  else
         | 
| 33 34 | 
             
                    JSON.generate(value, quirks_mode: true)
         | 
| 34 35 | 
             
                  end
         | 
| 36 | 
            +
                rescue JSON::GeneratorError
         | 
| 37 | 
            +
                  if Float::INFINITY == value
         | 
| 38 | 
            +
                    "Infinity"
         | 
| 39 | 
            +
                  else
         | 
| 40 | 
            +
                    raise
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # Returns a new string if any single-quoted newlines were escaped.
         | 
| 45 | 
            +
                # Otherwise, returns `query_str` unchanged.
         | 
| 46 | 
            +
                # @return [String]
         | 
| 47 | 
            +
                def self.escape_single_quoted_newlines(query_str)
         | 
| 48 | 
            +
                  scanner = StringScanner.new(query_str)
         | 
| 49 | 
            +
                  inside_single_quoted_string = false
         | 
| 50 | 
            +
                  new_query_str = nil
         | 
| 51 | 
            +
                  while !scanner.eos?
         | 
| 52 | 
            +
                    if (match = scanner.scan(/(?:\\"|[^"\n\r]|""")+/m)) && new_query_str
         | 
| 53 | 
            +
                      new_query_str << match
         | 
| 54 | 
            +
                    elsif scanner.scan('"')
         | 
| 55 | 
            +
                      new_query_str && (new_query_str << '"')
         | 
| 56 | 
            +
                      inside_single_quoted_string = !inside_single_quoted_string
         | 
| 57 | 
            +
                    elsif scanner.scan("\n")
         | 
| 58 | 
            +
                      if inside_single_quoted_string
         | 
| 59 | 
            +
                        new_query_str ||= query_str[0, scanner.pos - 1]
         | 
| 60 | 
            +
                        new_query_str << '\\n'
         | 
| 61 | 
            +
                      else
         | 
| 62 | 
            +
                        new_query_str && (new_query_str << "\n")
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    elsif scanner.scan("\r")
         | 
| 65 | 
            +
                      if inside_single_quoted_string
         | 
| 66 | 
            +
                        new_query_str ||= query_str[0, scanner.pos - 1]
         | 
| 67 | 
            +
                        new_query_str << '\\r'
         | 
| 68 | 
            +
                      else
         | 
| 69 | 
            +
                        new_query_str && (new_query_str << "\r")
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    elsif scanner.eos?
         | 
| 72 | 
            +
                      break
         | 
| 73 | 
            +
                    else
         | 
| 74 | 
            +
                      raise ArgumentError, "Unmatchable string scanner segment: #{scanner.rest.inspect}"
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                  new_query_str || query_str
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP = %r{
         | 
| 81 | 
            +
                  (
         | 
| 82 | 
            +
                    ((?<num>#{Lexer::INT_REGEXP}(#{Lexer::FLOAT_EXP_REGEXP})?)(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
         | 
| 83 | 
            +
                    |
         | 
| 84 | 
            +
                    ((?<num>#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP}#{Lexer::FLOAT_EXP_REGEXP})(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
         | 
| 85 | 
            +
                    |
         | 
| 86 | 
            +
                    ((?<num>#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP})(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
         | 
| 87 | 
            +
                  )}x
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def self.add_space_between_numbers_and_names(query_str)
         | 
| 90 | 
            +
                  if query_str.match?(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP)
         | 
| 91 | 
            +
                    query_str.gsub(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP, "\\k<num> \\k<name>:")
         | 
| 92 | 
            +
                  else
         | 
| 93 | 
            +
                    query_str
         | 
| 94 | 
            +
                  end
         | 
| 35 95 | 
             
                end
         | 
| 36 96 | 
             
              end
         | 
| 37 97 | 
             
            end
         | 
| @@ -35,10 +35,10 @@ module GraphQL | |
| 35 35 | 
             
                  def load_nodes
         | 
| 36 36 | 
             
                    @nodes ||= begin
         | 
| 37 37 | 
             
                      sliced_nodes = if before && after
         | 
| 38 | 
            -
                        end_idx = index_from_cursor(before)- | 
| 38 | 
            +
                        end_idx = index_from_cursor(before) - 2
         | 
| 39 39 | 
             
                        end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || []
         | 
| 40 40 | 
             
                      elsif before
         | 
| 41 | 
            -
                        end_idx = index_from_cursor(before)-2
         | 
| 41 | 
            +
                        end_idx = index_from_cursor(before) - 2
         | 
| 42 42 | 
             
                        end_idx < 0 ? [] : items[0..end_idx] || []
         | 
| 43 43 | 
             
                      elsif after
         | 
| 44 44 | 
             
                        items[index_from_cursor(after)..-1] || []
         | 
| @@ -56,12 +56,12 @@ module GraphQL | |
| 56 56 | 
             
                        false
         | 
| 57 57 | 
             
                      end
         | 
| 58 58 |  | 
| 59 | 
            -
                      @has_next_page = if  | 
| 60 | 
            -
                        # There are more items after these items
         | 
| 61 | 
            -
                        sliced_nodes.count > first
         | 
| 62 | 
            -
                      elsif before
         | 
| 59 | 
            +
                      @has_next_page = if before
         | 
| 63 60 | 
             
                        # The original array is longer than the `before` index
         | 
| 64 61 | 
             
                        index_from_cursor(before) < items.length + 1
         | 
| 62 | 
            +
                      elsif first
         | 
| 63 | 
            +
                        # There are more items after these items
         | 
| 64 | 
            +
                        sliced_nodes.count > first
         | 
| 65 65 | 
             
                      else
         | 
| 66 66 | 
             
                        false
         | 
| 67 67 | 
             
                      end
         | 
| @@ -6,36 +6,6 @@ module GraphQL | |
| 6 6 | 
             
                # Expose some query-specific info to field resolve functions.
         | 
| 7 7 | 
             
                # It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
         | 
| 8 8 | 
             
                class Context
         | 
| 9 | 
            -
                  module SharedMethods
         | 
| 10 | 
            -
                    # Return this value to tell the runtime
         | 
| 11 | 
            -
                    # to exclude this field from the response altogether
         | 
| 12 | 
            -
                    def skip
         | 
| 13 | 
            -
                      GraphQL::Execution::SKIP
         | 
| 14 | 
            -
                    end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                    # Add error at query-level.
         | 
| 17 | 
            -
                    # @param error [GraphQL::ExecutionError] an execution error
         | 
| 18 | 
            -
                    # @return [void]
         | 
| 19 | 
            -
                    def add_error(error)
         | 
| 20 | 
            -
                      if !error.is_a?(ExecutionError)
         | 
| 21 | 
            -
                        raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
         | 
| 22 | 
            -
                      end
         | 
| 23 | 
            -
                      errors << error
         | 
| 24 | 
            -
                      nil
         | 
| 25 | 
            -
                    end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    # @example Print the GraphQL backtrace during field resolution
         | 
| 28 | 
            -
                    #   puts ctx.backtrace
         | 
| 29 | 
            -
                    #
         | 
| 30 | 
            -
                    # @return [GraphQL::Backtrace] The backtrace for this point in query execution
         | 
| 31 | 
            -
                    def backtrace
         | 
| 32 | 
            -
                      GraphQL::Backtrace.new(self)
         | 
| 33 | 
            -
                    end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                    def execution_errors
         | 
| 36 | 
            -
                      @execution_errors ||= ExecutionErrors.new(self)
         | 
| 37 | 
            -
                    end
         | 
| 38 | 
            -
                  end
         | 
| 39 9 |  | 
| 40 10 | 
             
                  class ExecutionErrors
         | 
| 41 11 | 
             
                    def initialize(ctx)
         | 
| @@ -59,7 +29,6 @@ module GraphQL | |
| 59 29 | 
             
                    alias :push :add
         | 
| 60 30 | 
             
                  end
         | 
| 61 31 |  | 
| 62 | 
            -
                  include SharedMethods
         | 
| 63 32 | 
             
                  extend Forwardable
         | 
| 64 33 |  | 
| 65 34 | 
             
                  # @return [Array<GraphQL::ExecutionError>] errors returned during execution
         | 
| @@ -77,11 +46,10 @@ module GraphQL | |
| 77 46 | 
             
                  # Make a new context which delegates key lookup to `values`
         | 
| 78 47 | 
             
                  # @param query [GraphQL::Query] the query who owns this context
         | 
| 79 48 | 
             
                  # @param values [Hash] A hash of arbitrary values which will be accessible at query-time
         | 
| 80 | 
            -
                  def initialize(query:, schema: query.schema, values | 
| 49 | 
            +
                  def initialize(query:, schema: query.schema, values:)
         | 
| 81 50 | 
             
                    @query = query
         | 
| 82 51 | 
             
                    @schema = schema
         | 
| 83 52 | 
             
                    @provided_values = values || {}
         | 
| 84 | 
            -
                    @object = object
         | 
| 85 53 | 
             
                    # Namespaced storage, where user-provided values are in `nil` namespace:
         | 
| 86 54 | 
             
                    @storage = Hash.new { |h, k| h[k] = {} }
         | 
| 87 55 | 
             
                    @storage[nil] = @provided_values
         | 
| @@ -140,6 +108,35 @@ module GraphQL | |
| 140 108 | 
             
                    end
         | 
| 141 109 | 
             
                  end
         | 
| 142 110 |  | 
| 111 | 
            +
                  # Return this value to tell the runtime
         | 
| 112 | 
            +
                  # to exclude this field from the response altogether
         | 
| 113 | 
            +
                  def skip
         | 
| 114 | 
            +
                    GraphQL::Execution::SKIP
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  # Add error at query-level.
         | 
| 118 | 
            +
                  # @param error [GraphQL::ExecutionError] an execution error
         | 
| 119 | 
            +
                  # @return [void]
         | 
| 120 | 
            +
                  def add_error(error)
         | 
| 121 | 
            +
                    if !error.is_a?(ExecutionError)
         | 
| 122 | 
            +
                      raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                    errors << error
         | 
| 125 | 
            +
                    nil
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  # @example Print the GraphQL backtrace during field resolution
         | 
| 129 | 
            +
                  #   puts ctx.backtrace
         | 
| 130 | 
            +
                  #
         | 
| 131 | 
            +
                  # @return [GraphQL::Backtrace] The backtrace for this point in query execution
         | 
| 132 | 
            +
                  def backtrace
         | 
| 133 | 
            +
                    GraphQL::Backtrace.new(self)
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  def execution_errors
         | 
| 137 | 
            +
                    @execution_errors ||= ExecutionErrors.new(self)
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
             | 
| 143 140 | 
             
                  def current_path
         | 
| 144 141 | 
             
                    current_runtime_state = Thread.current[:__graphql_runtime_info]
         | 
| 145 142 | 
             
                    query_runtime_state = current_runtime_state && current_runtime_state[@query]
         | 
| @@ -14,7 +14,7 @@ module GraphQL | |
| 14 14 | 
             
                #
         | 
| 15 15 | 
             
                # @api private
         | 
| 16 16 | 
             
                class ValidationPipeline
         | 
| 17 | 
            -
                  attr_reader :max_depth, :max_complexity
         | 
| 17 | 
            +
                  attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
         | 
| 18 18 |  | 
| 19 19 | 
             
                  def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
         | 
| 20 20 | 
             
                    @validation_errors = []
         | 
| @@ -71,7 +71,7 @@ module GraphQL | |
| 71 71 | 
             
                      validator = @query.static_validator || @schema.static_validator
         | 
| 72 72 | 
             
                      validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
         | 
| 73 73 | 
             
                      @validation_errors.concat(validation_result[:errors])
         | 
| 74 | 
            -
             | 
| 74 | 
            +
                      @validate_timeout_remaining = validation_result[:remaining_timeout]
         | 
| 75 75 | 
             
                      if @validation_errors.empty?
         | 
| 76 76 | 
             
                        @validation_errors.concat(@query.variables.errors)
         | 
| 77 77 | 
             
                      end
         | 
| @@ -26,7 +26,7 @@ module GraphQL | |
| 26 26 | 
             
                      # - Then, fall back to the default value from the query string
         | 
| 27 27 | 
             
                      # If it's still nil, raise an error if it's required.
         | 
| 28 28 | 
             
                      variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
         | 
| 29 | 
            -
                      if variable_type.nil?
         | 
| 29 | 
            +
                      if variable_type.nil? || !variable_type.unwrap.kind.input?
         | 
| 30 30 | 
             
                        # Pass -- it will get handled by a validator
         | 
| 31 31 | 
             
                      else
         | 
| 32 32 | 
             
                        variable_name = ast_variable.name
         | 
| @@ -80,12 +80,12 @@ module GraphQL | |
| 80 80 | 
             
                    else
         | 
| 81 81 | 
             
                      val
         | 
| 82 82 | 
             
                    end
         | 
| 83 | 
            -
                  end | 
| 83 | 
            +
                  end
         | 
| 84 84 |  | 
| 85 85 | 
             
                  def add_max_errors_reached_message
         | 
| 86 86 | 
             
                    message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
         | 
| 87 87 | 
             
                    validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
         | 
| 88 | 
            -
                    errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message) | 
| 88 | 
            +
                    errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
         | 
| 89 89 | 
             
                  end
         | 
| 90 90 | 
             
                end
         | 
| 91 91 | 
             
              end
         | 
    
        data/lib/graphql/query.rb
    CHANGED
    
    | @@ -99,7 +99,7 @@ module GraphQL | |
| 99 99 | 
             
                  # Even if `variables: nil` is passed, use an empty hash for simpler logic
         | 
| 100 100 | 
             
                  variables ||= {}
         | 
| 101 101 | 
             
                  @schema = schema
         | 
| 102 | 
            -
                  @context = schema.context_class.new(query: self,  | 
| 102 | 
            +
                  @context = schema.context_class.new(query: self, values: context)
         | 
| 103 103 | 
             
                  @warden = warden
         | 
| 104 104 | 
             
                  @subscription_topic = subscription_topic
         | 
| 105 105 | 
             
                  @root_value = root_value
         | 
| @@ -317,7 +317,7 @@ module GraphQL | |
| 317 317 | 
             
                end
         | 
| 318 318 |  | 
| 319 319 | 
             
                def_delegators :validation_pipeline, :validation_errors,
         | 
| 320 | 
            -
                               :analyzers, :ast_analyzers, :max_depth, :max_complexity
         | 
| 320 | 
            +
                               :analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
         | 
| 321 321 |  | 
| 322 322 | 
             
                attr_accessor :analysis_errors
         | 
| 323 323 | 
             
                def valid?
         | 
| @@ -395,7 +395,7 @@ module GraphQL | |
| 395 395 | 
             
                  parse_error = nil
         | 
| 396 396 | 
             
                  @document ||= begin
         | 
| 397 397 | 
             
                    if query_string
         | 
| 398 | 
            -
                      GraphQL.parse(query_string, trace: self.current_trace)
         | 
| 398 | 
            +
                      GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
         | 
| 399 399 | 
             
                    end
         | 
| 400 400 | 
             
                  rescue GraphQL::ParseError => err
         | 
| 401 401 | 
             
                    parse_error = err
         | 
| @@ -290,6 +290,7 @@ module GraphQL | |
| 290 290 | 
             
                        # TODO code smell to access such a deeply-nested constant in a distant module
         | 
| 291 291 | 
             
                        argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
         | 
| 292 292 | 
             
                          value: resolved_loaded_value,
         | 
| 293 | 
            +
                          original_value: resolved_coerced_value,
         | 
| 293 294 | 
             
                          definition: self,
         | 
| 294 295 | 
             
                          default_used: default_used,
         | 
| 295 296 | 
             
                        )
         | 
| @@ -1,18 +1,16 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'graphql/schema/base_64_bp'
         | 
| 4 | 
            -
             | 
| 2 | 
            +
            require "base64"
         | 
| 5 3 | 
             
            module GraphQL
         | 
| 6 4 | 
             
              class Schema
         | 
| 7 5 | 
             
                # @api private
         | 
| 8 6 | 
             
                module Base64Encoder
         | 
| 9 7 | 
             
                  def self.encode(unencoded_text, nonce: false)
         | 
| 10 | 
            -
                     | 
| 8 | 
            +
                    Base64.urlsafe_encode64(unencoded_text, padding: false)
         | 
| 11 9 | 
             
                  end
         | 
| 12 10 |  | 
| 13 11 | 
             
                  def self.decode(encoded_text, nonce: false)
         | 
| 14 12 | 
             
                    # urlsafe_decode64 is for forward compatibility
         | 
| 15 | 
            -
                     | 
| 13 | 
            +
                    Base64.urlsafe_decode64(encoded_text)
         | 
| 16 14 | 
             
                  rescue ArgumentError
         | 
| 17 15 | 
             
                    raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
         | 
| 18 16 | 
             
                  end
         | 
| @@ -7,10 +7,16 @@ module GraphQL | |
| 7 7 | 
             
                  class << self
         | 
| 8 8 | 
             
                    # @see {Schema.from_definition}
         | 
| 9 9 | 
             
                    def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
         | 
| 10 | 
            +
                      if defined?(parser::SchemaParser)
         | 
| 11 | 
            +
                        parser = parser::SchemaParser
         | 
| 12 | 
            +
                      end
         | 
| 10 13 | 
             
                      from_document(schema_superclass, parser.parse(definition_string), **kwargs)
         | 
| 11 14 | 
             
                    end
         | 
| 12 15 |  | 
| 13 16 | 
             
                    def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
         | 
| 17 | 
            +
                      if defined?(parser::SchemaParser)
         | 
| 18 | 
            +
                        parser = parser::SchemaParser
         | 
| 19 | 
            +
                      end
         | 
| 14 20 | 
             
                      from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
         | 
| 15 21 | 
             
                    end
         | 
| 16 22 |  | 
| @@ -120,10 +126,12 @@ module GraphQL | |
| 120 126 |  | 
| 121 127 | 
             
                      builder = self
         | 
| 122 128 |  | 
| 129 | 
            +
                      found_types = types.values
         | 
| 123 130 | 
             
                      schema_class = Class.new(schema_superclass) do
         | 
| 124 131 | 
             
                        begin
         | 
| 125 132 | 
             
                          # Add these first so that there's some chance of resolving late-bound types
         | 
| 126 | 
            -
                           | 
| 133 | 
            +
                          add_type_and_traverse(found_types, root: false)
         | 
| 134 | 
            +
                          orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
         | 
| 127 135 | 
             
                          query query_root_type
         | 
| 128 136 | 
             
                          mutation mutation_root_type
         | 
| 129 137 | 
             
                          subscription subscription_root_type
         | 
    
        data/lib/graphql/schema/field.rb
    CHANGED
    
    | @@ -471,6 +471,8 @@ module GraphQL | |
| 471 471 | 
             
                        if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
         | 
| 472 472 | 
             
                          max_possible_page_size = arguments[:last]
         | 
| 473 473 | 
             
                        end
         | 
| 474 | 
            +
                      elsif arguments.is_a?(GraphQL::UnauthorizedError)
         | 
| 475 | 
            +
                        raise arguments
         | 
| 474 476 | 
             
                      end
         | 
| 475 477 |  | 
| 476 478 | 
             
                      if max_possible_page_size.nil?
         | 
| @@ -483,41 +485,24 @@ module GraphQL | |
| 483 485 | 
             
                        metadata_complexity = 0
         | 
| 484 486 | 
             
                        lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
         | 
| 485 487 |  | 
| 486 | 
            -
                         | 
| 487 | 
            -
                           | 
| 488 | 
            -
                           | 
| 489 | 
            -
             | 
| 490 | 
            -
             | 
| 491 | 
            -
             | 
| 492 | 
            -
             | 
| 488 | 
            +
                        lookahead.selections.each do |next_lookahead|
         | 
| 489 | 
            +
                          # this includes `pageInfo`, `nodes` and `edges` and any custom fields
         | 
| 490 | 
            +
                          # TODO this doesn't support procs yet -- unlikely to need it.
         | 
| 491 | 
            +
                          metadata_complexity += next_lookahead.field.complexity
         | 
| 492 | 
            +
                          if next_lookahead.name != :nodes && next_lookahead.name != :edges
         | 
| 493 | 
            +
                            # subfields, eg, for pageInfo -- assumes no subselections
         | 
| 494 | 
            +
                            metadata_complexity += next_lookahead.selections.size
         | 
| 495 | 
            +
                          end
         | 
| 493 496 | 
             
                        end
         | 
| 494 497 |  | 
| 495 | 
            -
                        nodes_edges_complexity = 0
         | 
| 496 | 
            -
                        nodes_edges_complexity += 1 if lookahead.selects?(:edges)
         | 
| 497 | 
            -
                        nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
         | 
| 498 | 
            -
             | 
| 499 498 | 
             
                        # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
         | 
| 500 | 
            -
                        items_complexity = child_complexity - metadata_complexity | 
| 501 | 
            -
                         | 
| 502 | 
            -
                         | 
| 499 | 
            +
                        items_complexity = child_complexity - metadata_complexity
         | 
| 500 | 
            +
                        subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
         | 
| 501 | 
            +
                        # Apply this field's own complexity
         | 
| 502 | 
            +
                        apply_own_complexity_to(subfields_complexity, query, nodes)
         | 
| 503 503 | 
             
                      end
         | 
| 504 504 | 
             
                    else
         | 
| 505 | 
            -
                       | 
| 506 | 
            -
                      case defined_complexity
         | 
| 507 | 
            -
                      when Proc
         | 
| 508 | 
            -
                        arguments = query.arguments_for(nodes.first, self)
         | 
| 509 | 
            -
                        if arguments.is_a?(GraphQL::ExecutionError)
         | 
| 510 | 
            -
                          return child_complexity
         | 
| 511 | 
            -
                        elsif arguments.respond_to?(:keyword_arguments)
         | 
| 512 | 
            -
                          arguments = arguments.keyword_arguments
         | 
| 513 | 
            -
                        end
         | 
| 514 | 
            -
             | 
| 515 | 
            -
                        defined_complexity.call(query.context, arguments, child_complexity)
         | 
| 516 | 
            -
                      when Numeric
         | 
| 517 | 
            -
                        defined_complexity + child_complexity
         | 
| 518 | 
            -
                      else
         | 
| 519 | 
            -
                        raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
         | 
| 520 | 
            -
                      end
         | 
| 505 | 
            +
                      apply_own_complexity_to(child_complexity, query, nodes)
         | 
| 521 506 | 
             
                    end
         | 
| 522 507 | 
             
                  end
         | 
| 523 508 |  | 
| @@ -882,6 +867,24 @@ ERR | |
| 882 867 | 
             
                      yield(obj, args)
         | 
| 883 868 | 
             
                    end
         | 
| 884 869 | 
             
                  end
         | 
| 870 | 
            +
             | 
| 871 | 
            +
                  def apply_own_complexity_to(child_complexity, query, nodes)
         | 
| 872 | 
            +
                    case (own_complexity = complexity)
         | 
| 873 | 
            +
                    when Numeric
         | 
| 874 | 
            +
                      own_complexity + child_complexity
         | 
| 875 | 
            +
                    when Proc
         | 
| 876 | 
            +
                      arguments = query.arguments_for(nodes.first, self)
         | 
| 877 | 
            +
                      if arguments.is_a?(GraphQL::ExecutionError)
         | 
| 878 | 
            +
                        return child_complexity
         | 
| 879 | 
            +
                      elsif arguments.respond_to?(:keyword_arguments)
         | 
| 880 | 
            +
                        arguments = arguments.keyword_arguments
         | 
| 881 | 
            +
                      end
         | 
| 882 | 
            +
             | 
| 883 | 
            +
                      own_complexity.call(query.context, arguments, child_complexity)
         | 
| 884 | 
            +
                    else
         | 
| 885 | 
            +
                      raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
         | 
| 886 | 
            +
                    end
         | 
| 887 | 
            +
                  end
         | 
| 885 888 | 
             
                end
         | 
| 886 889 | 
             
              end
         | 
| 887 890 | 
             
            end
         |