graphql 2.0.0 → 2.0.3
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/graphql/analysis/ast/max_query_complexity.rb +0 -1
- data/lib/graphql/dataloader/null_dataloader.rb +3 -1
- data/lib/graphql/execution/errors.rb +12 -74
- data/lib/graphql/execution/interpreter/runtime.rb +35 -38
- data/lib/graphql/query/context.rb +96 -9
- data/lib/graphql/query/input_validation_result.rb +10 -1
- data/lib/graphql/query/null_context.rb +0 -3
- data/lib/graphql/query.rb +2 -5
- data/lib/graphql/relay/range_add.rb +9 -16
- data/lib/graphql/schema/addition.rb +5 -0
- data/lib/graphql/schema/argument.rb +13 -4
- data/lib/graphql/schema/enum.rb +3 -5
- data/lib/graphql/schema/field.rb +164 -130
- data/lib/graphql/schema/input_object.rb +12 -12
- data/lib/graphql/schema/list.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +36 -6
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_interfaces.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +28 -2
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/object.rb +14 -9
- data/lib/graphql/schema/relay_classic_mutation.rb +32 -14
- data/lib/graphql/schema/resolver/has_payload_type.rb +11 -1
- data/lib/graphql/schema/resolver.rb +23 -45
- data/lib/graphql/schema/scalar.rb +7 -7
- data/lib/graphql/schema/subscription.rb +0 -7
- data/lib/graphql/schema/warden.rb +1 -1
- data/lib/graphql/schema.rb +35 -6
- data/lib/graphql/subscriptions.rb +10 -3
- data/lib/graphql/tracing/data_dog_tracing.rb +3 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +0 -16
- data/lib/graphql/version.rb +1 -1
- metadata +6 -7
- data/lib/graphql/query/literal_input.rb +0 -131
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 35d89f9a897b7897a59009d2b66b436750540da911e3c8e86e185f317d74b0e8
         | 
| 4 | 
            +
              data.tar.gz: 13c06d0144815bbafcdff091815fa939969e51d693e1e33138358131df509783
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '063911bc20713d486f3dca9cec21a881a4558a300b8cf03a19f34a82f23cbcd808f3a9b0b51b886b795c3c42e6b559b954e64b6a2a3f69ddd009145ae09cee4f'
         | 
| 7 | 
            +
              data.tar.gz: cbe4c5cda73c9fcc7bfb6a0e2249ba8c8dbad4e26ad3c2baf7cc5775fac6f7f19ac036baaea25d80b2fbaa58e9cade521a26d7b3c5bdd936b63efc9971c28ce1
         | 
| @@ -11,7 +11,9 @@ module GraphQL | |
| 11 11 | 
             
                  # executed sychronously.
         | 
| 12 12 | 
             
                  def run; end
         | 
| 13 13 | 
             
                  def run_isolated; yield; end
         | 
| 14 | 
            -
                  def yield | 
| 14 | 
            +
                  def yield
         | 
| 15 | 
            +
                    raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
         | 
| 16 | 
            +
                  end
         | 
| 15 17 |  | 
| 16 18 | 
             
                  def append_job
         | 
| 17 19 | 
             
                    yield
         | 
| @@ -2,51 +2,15 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module GraphQL
         | 
| 4 4 | 
             
              module Execution
         | 
| 5 | 
            -
                # A plugin that wraps query execution with error handling. Installed by default.
         | 
| 6 | 
            -
                #
         | 
| 7 | 
            -
                # @example Handling ActiveRecord::NotFound
         | 
| 8 | 
            -
                #
         | 
| 9 | 
            -
                #   class MySchema < GraphQL::Schema
         | 
| 10 | 
            -
                #     rescue_from(ActiveRecord::NotFound) do |err, obj, args, ctx, field|
         | 
| 11 | 
            -
                #       ErrorTracker.log("Not Found: #{err.message}")
         | 
| 12 | 
            -
                #       nil
         | 
| 13 | 
            -
                #     end
         | 
| 14 | 
            -
                #   end
         | 
| 15 | 
            -
                #
         | 
| 16 5 | 
             
                class Errors
         | 
| 17 | 
            -
                  NEW_HANDLER_HASH = ->(h, k) {
         | 
| 18 | 
            -
                    h[k] = {
         | 
| 19 | 
            -
                      class: k,
         | 
| 20 | 
            -
                      handler: nil,
         | 
| 21 | 
            -
                      subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
         | 
| 22 | 
            -
                     }
         | 
| 23 | 
            -
                  }
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  def initialize(schema)
         | 
| 26 | 
            -
                    @schema = schema
         | 
| 27 | 
            -
                    @handlers = {
         | 
| 28 | 
            -
                      class: nil,
         | 
| 29 | 
            -
                      handler: nil,
         | 
| 30 | 
            -
                      subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
         | 
| 31 | 
            -
                    }
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  # @api private
         | 
| 35 | 
            -
                  def each_rescue
         | 
| 36 | 
            -
                    handlers = @handlers.values
         | 
| 37 | 
            -
                    while (handler = handlers.shift) do
         | 
| 38 | 
            -
                      yield(handler[:class], handler[:handler])
         | 
| 39 | 
            -
                      handlers.concat(handler[:subclass_handlers].values)
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
             | 
| 43 6 | 
             
                  # Register this handler, updating the
         | 
| 44 7 | 
             
                  # internal handler index to maintain least-to-most specific.
         | 
| 45 8 | 
             
                  #
         | 
| 46 9 | 
             
                  # @param error_class [Class<Exception>]
         | 
| 10 | 
            +
                  # @param error_handlers [Hash]
         | 
| 47 11 | 
             
                  # @param error_handler [Proc]
         | 
| 48 12 | 
             
                  # @return [void]
         | 
| 49 | 
            -
                  def  | 
| 13 | 
            +
                  def self.register_rescue_from(error_class, error_handlers, error_handler)
         | 
| 50 14 | 
             
                    subclasses_handlers = {}
         | 
| 51 15 | 
             
                    this_level_subclasses = []
         | 
| 52 16 | 
             
                    # During this traversal, do two things:
         | 
| @@ -54,13 +18,12 @@ module GraphQL | |
| 54 18 | 
             
                    #   and gather them up to be inserted _under_ this class
         | 
| 55 19 | 
             
                    # - Find the point in the index where this handler should be inserted
         | 
| 56 20 | 
             
                    #   (That is, _under_ any superclasses, or at top-level, if there are no superclasses registered)
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
                    while (handlers) do
         | 
| 21 | 
            +
                    while (error_handlers) do
         | 
| 59 22 | 
             
                      this_level_subclasses.clear
         | 
| 60 23 | 
             
                      # First, identify already-loaded handlers that belong
         | 
| 61 24 | 
             
                      # _under_ this one. (That is, they're handlers
         | 
| 62 25 | 
             
                      # for subclasses of `error_class`.)
         | 
| 63 | 
            -
                       | 
| 26 | 
            +
                      error_handlers.each do |err_class, handler|
         | 
| 64 27 | 
             
                        if err_class < error_class
         | 
| 65 28 | 
             
                          subclasses_handlers[err_class] = handler
         | 
| 66 29 | 
             
                          this_level_subclasses << err_class
         | 
| @@ -68,13 +31,13 @@ module GraphQL | |
| 68 31 | 
             
                      end
         | 
| 69 32 | 
             
                      # Any handlers that we'll be moving, delete them from this point in the index
         | 
| 70 33 | 
             
                      this_level_subclasses.each do |err_class|
         | 
| 71 | 
            -
                         | 
| 34 | 
            +
                        error_handlers.delete(err_class)
         | 
| 72 35 | 
             
                      end
         | 
| 73 36 |  | 
| 74 37 | 
             
                      # See if any keys in this hash are superclasses of this new class:
         | 
| 75 | 
            -
                      next_index_point =  | 
| 38 | 
            +
                      next_index_point = error_handlers.find { |err_class, handler| error_class < err_class }
         | 
| 76 39 | 
             
                      if next_index_point
         | 
| 77 | 
            -
                         | 
| 40 | 
            +
                        error_handlers = next_index_point[1][:subclass_handlers]
         | 
| 78 41 | 
             
                      else
         | 
| 79 42 | 
             
                        # this new handler doesn't belong to any sub-handlers,
         | 
| 80 43 | 
             
                        # so insert it in the current set of `handlers`
         | 
| @@ -83,40 +46,15 @@ module GraphQL | |
| 83 46 | 
             
                    end
         | 
| 84 47 | 
             
                    # Having found the point at which to insert this handler,
         | 
| 85 48 | 
             
                    # register it and merge any subclass handlers back in at this point.
         | 
| 86 | 
            -
                    this_class_handlers =  | 
| 49 | 
            +
                    this_class_handlers = error_handlers[error_class]
         | 
| 87 50 | 
             
                    this_class_handlers[:handler] = error_handler
         | 
| 88 51 | 
             
                    this_class_handlers[:subclass_handlers].merge!(subclasses_handlers)
         | 
| 89 52 | 
             
                    nil
         | 
| 90 53 | 
             
                  end
         | 
| 91 54 |  | 
| 92 | 
            -
                  # Call the given block with the schema's configured error handlers.
         | 
| 93 | 
            -
                  #
         | 
| 94 | 
            -
                  # If the block returns a lazy value, it's not wrapped with error handling. That area will have to be wrapped itself.
         | 
| 95 | 
            -
                  #
         | 
| 96 | 
            -
                  # @param ctx [GraphQL::Query::Context]
         | 
| 97 | 
            -
                  # @return [Object] Either the result of the given block, or some object to replace the result, in case of error handling.
         | 
| 98 | 
            -
                  def with_error_handling(ctx)
         | 
| 99 | 
            -
                    yield
         | 
| 100 | 
            -
                  rescue StandardError => err
         | 
| 101 | 
            -
                    handler = find_handler_for(err.class)
         | 
| 102 | 
            -
                    if handler
         | 
| 103 | 
            -
                      runtime_info = ctx.namespace(:interpreter) || {}
         | 
| 104 | 
            -
                      obj = runtime_info[:current_object]
         | 
| 105 | 
            -
                      args = runtime_info[:current_arguments]
         | 
| 106 | 
            -
                      args = args && args.keyword_arguments
         | 
| 107 | 
            -
                      field = runtime_info[:current_field]
         | 
| 108 | 
            -
                      if obj.is_a?(GraphQL::Schema::Object)
         | 
| 109 | 
            -
                        obj = obj.object
         | 
| 110 | 
            -
                      end
         | 
| 111 | 
            -
                      handler[:handler].call(err, obj, args, ctx, field)
         | 
| 112 | 
            -
                    else
         | 
| 113 | 
            -
                      raise err
         | 
| 114 | 
            -
                    end
         | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
             | 
| 117 55 | 
             
                  # @return [Proc, nil] The handler for `error_class`, if one was registered on this schema or inherited
         | 
| 118 | 
            -
                  def find_handler_for(error_class)
         | 
| 119 | 
            -
                    handlers =  | 
| 56 | 
            +
                  def self.find_handler_for(schema, error_class)
         | 
| 57 | 
            +
                    handlers = schema.error_handlers[:subclass_handlers]
         | 
| 120 58 | 
             
                    handler = nil
         | 
| 121 59 | 
             
                    while (handlers) do
         | 
| 122 60 | 
             
                      _err_class, next_handler = handlers.find { |err_class, handler| error_class <= err_class }
         | 
| @@ -131,8 +69,8 @@ module GraphQL | |
| 131 69 | 
             
                    end
         | 
| 132 70 |  | 
| 133 71 | 
             
                    # check for a handler from a parent class:
         | 
| 134 | 
            -
                    if  | 
| 135 | 
            -
                      parent_handler =  | 
| 72 | 
            +
                    if schema.superclass.respond_to?(:error_handlers)
         | 
| 73 | 
            +
                      parent_handler = find_handler_for(schema.superclass, error_class)
         | 
| 136 74 | 
             
                    end
         | 
| 137 75 |  | 
| 138 76 | 
             
                    # If the inherited handler is more specific than the one defined here,
         | 
| @@ -230,7 +230,6 @@ module GraphQL | |
| 230 230 | 
             
                              call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
         | 
| 231 231 | 
             
                                evaluate_selections(
         | 
| 232 232 | 
             
                                  path,
         | 
| 233 | 
            -
                                  context.scoped_context,
         | 
| 234 233 | 
             
                                  object_proxy,
         | 
| 235 234 | 
             
                                  root_type,
         | 
| 236 235 | 
             
                                  root_op_type == "mutation",
         | 
| @@ -349,7 +348,7 @@ module GraphQL | |
| 349 348 | 
             
                    NO_ARGS = {}.freeze
         | 
| 350 349 |  | 
| 351 350 | 
             
                    # @return [void]
         | 
| 352 | 
            -
                    def evaluate_selections(path,  | 
| 351 | 
            +
                    def evaluate_selections(path, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
         | 
| 353 352 | 
             
                      set_all_interpreter_context(owner_object, nil, nil, path)
         | 
| 354 353 |  | 
| 355 354 | 
             
                      finished_jobs = 0
         | 
| @@ -357,7 +356,7 @@ module GraphQL | |
| 357 356 | 
             
                      gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
         | 
| 358 357 | 
             
                        @dataloader.append_job {
         | 
| 359 358 | 
             
                          evaluate_selection(
         | 
| 360 | 
            -
                            path, result_name, field_ast_nodes_or_ast_node,  | 
| 359 | 
            +
                            path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
         | 
| 361 360 | 
             
                          )
         | 
| 362 361 | 
             
                          finished_jobs += 1
         | 
| 363 362 | 
             
                          if target_result && finished_jobs == enqueued_jobs
         | 
| @@ -372,7 +371,7 @@ module GraphQL | |
| 372 371 | 
             
                    attr_reader :progress_path
         | 
| 373 372 |  | 
| 374 373 | 
             
                    # @return [void]
         | 
| 375 | 
            -
                    def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node,  | 
| 374 | 
            +
                    def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
         | 
| 376 375 | 
             
                      return if dead_result?(selections_result)
         | 
| 377 376 | 
             
                      # As a performance optimization, the hash key will be a `Node` if
         | 
| 378 377 | 
             
                      # there's only one selection of the field. But if there are multiple
         | 
| @@ -414,8 +413,6 @@ module GraphQL | |
| 414 413 | 
             
                      end
         | 
| 415 414 | 
             
                      # Set this before calling `run_with_directives`, so that the directive can have the latest path
         | 
| 416 415 | 
             
                      set_all_interpreter_context(nil, field_defn, nil, next_path)
         | 
| 417 | 
            -
             | 
| 418 | 
            -
                      context.scoped_context = scoped_context
         | 
| 419 416 | 
             
                      object = owner_object
         | 
| 420 417 |  | 
| 421 418 | 
             
                      if is_introspection
         | 
| @@ -425,19 +422,18 @@ module GraphQL | |
| 425 422 | 
             
                      total_args_count = field_defn.arguments(context).size
         | 
| 426 423 | 
             
                      if total_args_count == 0
         | 
| 427 424 | 
             
                        resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
         | 
| 428 | 
            -
                        evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes,  | 
| 425 | 
            +
                        evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
         | 
| 429 426 | 
             
                      else
         | 
| 430 427 | 
             
                        # TODO remove all arguments(...) usages?
         | 
| 431 428 | 
             
                        @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
         | 
| 432 | 
            -
                          evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes,  | 
| 429 | 
            +
                          evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
         | 
| 433 430 | 
             
                        end
         | 
| 434 431 | 
             
                      end
         | 
| 435 432 | 
             
                    end
         | 
| 436 433 |  | 
| 437 | 
            -
                    def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes,  | 
| 438 | 
            -
                      context.scoped_context = scoped_context
         | 
| 434 | 
            +
                    def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object)  # rubocop:disable Metrics/ParameterLists
         | 
| 439 435 | 
             
                      return_type = field_defn.type
         | 
| 440 | 
            -
                      after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node,  | 
| 436 | 
            +
                      after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
         | 
| 441 437 | 
             
                        if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
         | 
| 442 438 | 
             
                          continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
         | 
| 443 439 | 
             
                          next
         | 
| @@ -502,15 +498,19 @@ module GraphQL | |
| 502 498 | 
             
                        field_result = call_method_on_directives(:resolve, object, directives) do
         | 
| 503 499 | 
             
                          # Actually call the field resolver and capture the result
         | 
| 504 500 | 
             
                          app_result = begin
         | 
| 505 | 
            -
                            query. | 
| 506 | 
            -
                               | 
| 507 | 
            -
                                field_defn.resolve(object, kwarg_arguments, context)
         | 
| 508 | 
            -
                              end
         | 
| 501 | 
            +
                            query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
         | 
| 502 | 
            +
                              field_defn.resolve(object, kwarg_arguments, context)
         | 
| 509 503 | 
             
                            end
         | 
| 510 504 | 
             
                          rescue GraphQL::ExecutionError => err
         | 
| 511 505 | 
             
                            err
         | 
| 506 | 
            +
                          rescue StandardError => err
         | 
| 507 | 
            +
                            begin
         | 
| 508 | 
            +
                              query.handle_or_reraise(err)
         | 
| 509 | 
            +
                            rescue GraphQL::ExecutionError => ex_err
         | 
| 510 | 
            +
                              ex_err
         | 
| 511 | 
            +
                            end
         | 
| 512 512 | 
             
                          end
         | 
| 513 | 
            -
                          after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node,  | 
| 513 | 
            +
                          after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
         | 
| 514 514 | 
             
                            continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
         | 
| 515 515 | 
             
                            if HALT != continue_value
         | 
| 516 516 | 
             
                              continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
         | 
| @@ -688,7 +688,7 @@ module GraphQL | |
| 688 688 | 
             
                        resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
         | 
| 689 689 | 
             
                        resolved_value ||= value
         | 
| 690 690 |  | 
| 691 | 
            -
                        after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node,  | 
| 691 | 
            +
                        after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
         | 
| 692 692 | 
             
                          possible_types = query.possible_types(current_type)
         | 
| 693 693 |  | 
| 694 694 | 
             
                          if !possible_types.include?(resolved_type)
         | 
| @@ -708,7 +708,7 @@ module GraphQL | |
| 708 708 | 
             
                        rescue GraphQL::ExecutionError => err
         | 
| 709 709 | 
             
                          err
         | 
| 710 710 | 
             
                        end
         | 
| 711 | 
            -
                        after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node,  | 
| 711 | 
            +
                        after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
         | 
| 712 712 | 
             
                          continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
         | 
| 713 713 | 
             
                          if HALT != continue_value
         | 
| 714 714 | 
             
                            response_hash = GraphQLResultHash.new(result_name, selection_result)
         | 
| @@ -734,7 +734,6 @@ module GraphQL | |
| 734 734 | 
             
                              call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
         | 
| 735 735 | 
             
                                evaluate_selections(
         | 
| 736 736 | 
             
                                  path,
         | 
| 737 | 
            -
                                  context.scoped_context,
         | 
| 738 737 | 
             
                                  continue_value,
         | 
| 739 738 | 
             
                                  current_type,
         | 
| 740 739 | 
             
                                  false,
         | 
| @@ -757,7 +756,6 @@ module GraphQL | |
| 757 756 | 
             
                        set_result(selection_result, result_name, response_list)
         | 
| 758 757 |  | 
| 759 758 | 
             
                        idx = 0
         | 
| 760 | 
            -
                        scoped_context = context.scoped_context
         | 
| 761 759 | 
             
                        begin
         | 
| 762 760 | 
             
                          value.each do |inner_value|
         | 
| 763 761 | 
             
                            break if dead_result?(response_list)
         | 
| @@ -768,10 +766,10 @@ module GraphQL | |
| 768 766 | 
             
                            idx += 1
         | 
| 769 767 | 
             
                            if use_dataloader_job
         | 
| 770 768 | 
             
                              @dataloader.append_job do
         | 
| 771 | 
            -
                                resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 769 | 
            +
                                resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
         | 
| 772 770 | 
             
                              end
         | 
| 773 771 | 
             
                            else
         | 
| 774 | 
            -
                              resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 772 | 
            +
                              resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
         | 
| 775 773 | 
             
                            end
         | 
| 776 774 | 
             
                          end
         | 
| 777 775 | 
             
                        rescue NoMethodError => err
         | 
| @@ -791,11 +789,11 @@ module GraphQL | |
| 791 789 | 
             
                      end
         | 
| 792 790 | 
             
                    end
         | 
| 793 791 |  | 
| 794 | 
            -
                    def resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 792 | 
            +
                    def resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
         | 
| 795 793 | 
             
                      set_all_interpreter_context(nil, nil, nil, next_path)
         | 
| 796 794 | 
             
                      call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
         | 
| 797 795 | 
             
                        # This will update `response_list` with the lazy
         | 
| 798 | 
            -
                        after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node,  | 
| 796 | 
            +
                        after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
         | 
| 799 797 | 
             
                          continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
         | 
| 800 798 | 
             
                          if HALT != continue_value
         | 
| 801 799 | 
             
                            continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
         | 
| @@ -870,29 +868,28 @@ module GraphQL | |
| 870 868 | 
             
                    # @param eager [Boolean] Set to `true` for mutation root fields only
         | 
| 871 869 | 
             
                    # @param trace [Boolean] If `false`, don't wrap this with field tracing
         | 
| 872 870 | 
             
                    # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
         | 
| 873 | 
            -
                    def after_lazy(lazy_obj, owner:, field:, path:,  | 
| 871 | 
            +
                    def after_lazy(lazy_obj, owner:, field:, path:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
         | 
| 874 872 | 
             
                      if lazy?(lazy_obj)
         | 
| 875 873 | 
             
                        lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
         | 
| 876 874 | 
             
                          set_all_interpreter_context(owner_object, field, arguments, path)
         | 
| 877 | 
            -
                          context.scoped_context = scoped_context
         | 
| 878 875 | 
             
                          # Wrap the execution of _this_ method with tracing,
         | 
| 879 876 | 
             
                          # but don't wrap the continuation below
         | 
| 880 877 | 
             
                          inner_obj = begin
         | 
| 881 | 
            -
                             | 
| 882 | 
            -
                               | 
| 883 | 
            -
                                 | 
| 884 | 
            -
                                  query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
         | 
| 885 | 
            -
                                    schema.sync_lazy(lazy_obj)
         | 
| 886 | 
            -
                                  end
         | 
| 887 | 
            -
                                else
         | 
| 888 | 
            -
                                  schema.sync_lazy(lazy_obj)
         | 
| 889 | 
            -
                                end
         | 
| 890 | 
            -
                              rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
         | 
| 891 | 
            -
                                err
         | 
| 878 | 
            +
                            if trace
         | 
| 879 | 
            +
                              query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
         | 
| 880 | 
            +
                                schema.sync_lazy(lazy_obj)
         | 
| 892 881 | 
             
                              end
         | 
| 882 | 
            +
                            else
         | 
| 883 | 
            +
                              schema.sync_lazy(lazy_obj)
         | 
| 893 884 | 
             
                            end
         | 
| 894 | 
            -
                          rescue GraphQL::ExecutionError => ex_err
         | 
| 885 | 
            +
                          rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
         | 
| 895 886 | 
             
                            ex_err
         | 
| 887 | 
            +
                          rescue StandardError => err
         | 
| 888 | 
            +
                            begin
         | 
| 889 | 
            +
                              query.handle_or_reraise(err)
         | 
| 890 | 
            +
                            rescue GraphQL::ExecutionError => ex_err
         | 
| 891 | 
            +
                              ex_err
         | 
| 892 | 
            +
                            end
         | 
| 896 893 | 
             
                          end
         | 
| 897 894 | 
             
                          yield(inner_obj)
         | 
| 898 895 | 
             
                        end
         | 
| @@ -86,8 +86,83 @@ module GraphQL | |
| 86 86 | 
             
                    @errors = []
         | 
| 87 87 | 
             
                    @path = []
         | 
| 88 88 | 
             
                    @value = nil
         | 
| 89 | 
            -
                    @context = self # for SharedMethods
         | 
| 90 | 
            -
                    @scoped_context =  | 
| 89 | 
            +
                    @context = self # for SharedMethods TODO delete sharedmethods
         | 
| 90 | 
            +
                    @scoped_context = ScopedContext.new(self)
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  class ScopedContext
         | 
| 94 | 
            +
                    def initialize(query_context)
         | 
| 95 | 
            +
                      @query_context = query_context
         | 
| 96 | 
            +
                      @path_contexts = {}
         | 
| 97 | 
            +
                      @no_path = [].freeze
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    def merged_context
         | 
| 101 | 
            +
                      merged_ctx = {}
         | 
| 102 | 
            +
                      each_present_path_ctx do |path_ctx|
         | 
| 103 | 
            +
                        merged_ctx = path_ctx.merge(merged_ctx)
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                      merged_ctx
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    def merge!(hash)
         | 
| 109 | 
            +
                      current_ctx = @path_contexts[current_path] ||= {}
         | 
| 110 | 
            +
                      current_ctx.merge!(hash)
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    def current_path
         | 
| 114 | 
            +
                      @query_context.namespace(:interpreter)[:current_path] || @no_path
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    def key?(key)
         | 
| 118 | 
            +
                      each_present_path_ctx do |path_ctx|
         | 
| 119 | 
            +
                        if path_ctx.key?(key)
         | 
| 120 | 
            +
                          return true
         | 
| 121 | 
            +
                        end
         | 
| 122 | 
            +
                      end
         | 
| 123 | 
            +
                      false
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                    def [](key)
         | 
| 127 | 
            +
                      each_present_path_ctx do |path_ctx|
         | 
| 128 | 
            +
                        if path_ctx.key?(key)
         | 
| 129 | 
            +
                          return path_ctx[key]
         | 
| 130 | 
            +
                        end
         | 
| 131 | 
            +
                      end
         | 
| 132 | 
            +
                      nil
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    def dig(key, *other_keys)
         | 
| 136 | 
            +
                      each_present_path_ctx do |path_ctx|
         | 
| 137 | 
            +
                        if path_ctx.key?(key)
         | 
| 138 | 
            +
                          found_value = path_ctx[key]
         | 
| 139 | 
            +
                          if other_keys.any?
         | 
| 140 | 
            +
                            return found_value.dig(*other_keys)
         | 
| 141 | 
            +
                          else
         | 
| 142 | 
            +
                            return found_value
         | 
| 143 | 
            +
                          end
         | 
| 144 | 
            +
                        end
         | 
| 145 | 
            +
                      end
         | 
| 146 | 
            +
                      nil
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    private
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    # Start at the current location,
         | 
| 152 | 
            +
                    # but look up the tree for previously-assigned scoped values
         | 
| 153 | 
            +
                    def each_present_path_ctx
         | 
| 154 | 
            +
                      search_path = current_path.dup
         | 
| 155 | 
            +
                      if (current_path_ctx = @path_contexts[search_path])
         | 
| 156 | 
            +
                        yield(current_path_ctx)
         | 
| 157 | 
            +
                      end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                      while search_path.size > 0
         | 
| 160 | 
            +
                        search_path.pop # look one level higher
         | 
| 161 | 
            +
                        if (search_path_ctx = @path_contexts[search_path])
         | 
| 162 | 
            +
                          yield(search_path_ctx)
         | 
| 163 | 
            +
                        end
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
                    end
         | 
| 91 166 | 
             
                  end
         | 
| 92 167 |  | 
| 93 168 | 
             
                  # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
         | 
| @@ -106,7 +181,7 @@ module GraphQL | |
| 106 181 | 
             
                  attr_writer :value
         | 
| 107 182 |  | 
| 108 183 | 
             
                  # @api private
         | 
| 109 | 
            -
                   | 
| 184 | 
            +
                  attr_reader :scoped_context
         | 
| 110 185 |  | 
| 111 186 | 
             
                  def []=(key, value)
         | 
| 112 187 | 
             
                    @provided_values[key] = value
         | 
| @@ -119,8 +194,11 @@ module GraphQL | |
| 119 194 |  | 
| 120 195 | 
             
                  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
         | 
| 121 196 | 
             
                  def [](key)
         | 
| 122 | 
            -
                     | 
| 123 | 
            -
             | 
| 197 | 
            +
                    if @scoped_context.key?(key)
         | 
| 198 | 
            +
                      @scoped_context[key]
         | 
| 199 | 
            +
                    else
         | 
| 200 | 
            +
                      @provided_values[key]
         | 
| 201 | 
            +
                    end
         | 
| 124 202 | 
             
                  end
         | 
| 125 203 |  | 
| 126 204 | 
             
                  def delete(key)
         | 
| @@ -135,7 +213,7 @@ module GraphQL | |
| 135 213 |  | 
| 136 214 | 
             
                  def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
         | 
| 137 215 | 
             
                    if @scoped_context.key?(key)
         | 
| 138 | 
            -
                       | 
| 216 | 
            +
                      scoped_context[key]
         | 
| 139 217 | 
             
                    elsif @provided_values.key?(key)
         | 
| 140 218 | 
             
                      @provided_values[key]
         | 
| 141 219 | 
             
                    elsif default != UNSPECIFIED_FETCH_DEFAULT
         | 
| @@ -148,12 +226,21 @@ module GraphQL | |
| 148 226 | 
             
                  end
         | 
| 149 227 |  | 
| 150 228 | 
             
                  def dig(key, *other_keys)
         | 
| 151 | 
            -
                    @scoped_context.key?(key) | 
| 229 | 
            +
                    if @scoped_context.key?(key)
         | 
| 230 | 
            +
                      @scoped_context.dig(key, *other_keys)
         | 
| 231 | 
            +
                    else
         | 
| 232 | 
            +
                      @provided_values.dig(key, *other_keys)
         | 
| 233 | 
            +
                    end
         | 
| 152 234 | 
             
                  end
         | 
| 153 235 |  | 
| 154 236 | 
             
                  def to_h
         | 
| 155 | 
            -
                     | 
| 237 | 
            +
                    if (current_scoped_context = @scoped_context.merged_context)
         | 
| 238 | 
            +
                      @provided_values.merge(current_scoped_context)
         | 
| 239 | 
            +
                    else
         | 
| 240 | 
            +
                      @provided_values
         | 
| 241 | 
            +
                    end
         | 
| 156 242 | 
             
                  end
         | 
| 243 | 
            +
             | 
| 157 244 | 
             
                  alias :to_hash :to_h
         | 
| 158 245 |  | 
| 159 246 | 
             
                  def key?(key)
         | 
| @@ -185,7 +272,7 @@ module GraphQL | |
| 185 272 | 
             
                  end
         | 
| 186 273 |  | 
| 187 274 | 
             
                  def scoped_merge!(hash)
         | 
| 188 | 
            -
                    @scoped_context | 
| 275 | 
            +
                    @scoped_context.merge!(hash)
         | 
| 189 276 | 
             
                  end
         | 
| 190 277 |  | 
| 191 278 | 
             
                  def scoped_set!(key, value)
         | 
| @@ -4,6 +4,12 @@ module GraphQL | |
| 4 4 | 
             
                class InputValidationResult
         | 
| 5 5 | 
             
                  attr_accessor :problems
         | 
| 6 6 |  | 
| 7 | 
            +
                  def self.from_problem(explanation, path = nil, extensions: nil, message: nil)
         | 
| 8 | 
            +
                    result = self.new
         | 
| 9 | 
            +
                    result.add_problem(explanation, path, extensions: extensions, message: message)
         | 
| 10 | 
            +
                    result
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 7 13 | 
             
                  def initialize(valid: true, problems: nil)
         | 
| 8 14 | 
             
                    @valid = valid
         | 
| 9 15 | 
             
                    @problems = problems
         | 
| @@ -27,7 +33,7 @@ module GraphQL | |
| 27 33 | 
             
                  end
         | 
| 28 34 |  | 
| 29 35 | 
             
                  def merge_result!(path, inner_result)
         | 
| 30 | 
            -
                    return if inner_result.valid?
         | 
| 36 | 
            +
                    return if inner_result.nil? || inner_result.valid?
         | 
| 31 37 |  | 
| 32 38 | 
             
                    if inner_result.problems
         | 
| 33 39 | 
             
                      inner_result.problems.each do |p|
         | 
| @@ -38,6 +44,9 @@ module GraphQL | |
| 38 44 | 
             
                    # It could have been explicitly set on inner_result (if it had no problems)
         | 
| 39 45 | 
             
                    @valid = false
         | 
| 40 46 | 
             
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  VALID = self.new
         | 
| 49 | 
            +
                  VALID.freeze
         | 
| 41 50 | 
             
                end
         | 
| 42 51 | 
             
              end
         | 
| 43 52 | 
             
            end
         | 
    
        data/lib/graphql/query.rb
    CHANGED
    
    | @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 | 
             
            require "graphql/query/context"
         | 
| 3 3 | 
             
            require "graphql/query/fingerprint"
         | 
| 4 | 
            -
            require "graphql/query/literal_input"
         | 
| 5 4 | 
             
            require "graphql/query/null_context"
         | 
| 6 5 | 
             
            require "graphql/query/result"
         | 
| 7 6 | 
             
            require "graphql/query/variables"
         | 
| @@ -332,10 +331,8 @@ module GraphQL | |
| 332 331 | 
             
                end
         | 
| 333 332 |  | 
| 334 333 | 
             
                # @api private
         | 
| 335 | 
            -
                def  | 
| 336 | 
            -
                  schema. | 
| 337 | 
            -
                    yield
         | 
| 338 | 
            -
                  end
         | 
| 334 | 
            +
                def handle_or_reraise(err)
         | 
| 335 | 
            +
                  schema.handle_or_reraise(context, err)
         | 
| 339 336 | 
             
                end
         | 
| 340 337 |  | 
| 341 338 | 
             
                private
         | 
| @@ -31,25 +31,18 @@ module GraphQL | |
| 31 31 |  | 
| 32 32 | 
             
                  # @param collection [Object] The list of items to wrap in a connection
         | 
| 33 33 | 
             
                  # @param item [Object] The newly-added item (will be wrapped in `edge_class`)
         | 
| 34 | 
            +
                  # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection
         | 
| 34 35 | 
             
                  # @param parent [Object] The owner of `collection`, will be passed to the connection if provided
         | 
| 35 | 
            -
                  # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
         | 
| 36 36 | 
             
                  # @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class)
         | 
| 37 | 
            -
                  def initialize(collection:, item:, parent: nil,  | 
| 38 | 
            -
                     | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
                      @ | 
| 44 | 
            -
                        @connection.range_add_edge(item)
         | 
| 45 | 
            -
                      else
         | 
| 46 | 
            -
                        @connection.edge_class.new(item, @connection)
         | 
| 47 | 
            -
                      end
         | 
| 37 | 
            +
                  def initialize(collection:, item:, context:, parent: nil, edge_class: nil)
         | 
| 38 | 
            +
                    conn_class = context.schema.connections.wrapper_for(collection)
         | 
| 39 | 
            +
                    # The rest will be added by ConnectionExtension
         | 
| 40 | 
            +
                    @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
         | 
| 41 | 
            +
                    # Check if this connection supports it, to support old versions of GraphQL-Pro
         | 
| 42 | 
            +
                    @edge = if @connection.respond_to?(:range_add_edge)
         | 
| 43 | 
            +
                      @connection.range_add_edge(item)
         | 
| 48 44 | 
             
                    else
         | 
| 49 | 
            -
                       | 
| 50 | 
            -
                      @connection = connection_class.new(collection, {}, parent: parent, context: context)
         | 
| 51 | 
            -
                      edge_class ||= Relay::Edge
         | 
| 52 | 
            -
                      @edge = edge_class.new(item, @connection)
         | 
| 45 | 
            +
                      @connection.edge_class.new(item, @connection)
         | 
| 53 46 | 
             
                    end
         | 
| 54 47 |  | 
| 55 48 | 
             
                    @parent = parent
         | 
| @@ -107,6 +107,11 @@ module GraphQL | |
| 107 107 | 
             
                          if !pt.include?(owner) && owner.is_a?(Class)
         | 
| 108 108 | 
             
                            pt << owner
         | 
| 109 109 | 
             
                          end
         | 
| 110 | 
            +
                          int.interfaces.each do |indirect_int|
         | 
| 111 | 
            +
                            if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name))
         | 
| 112 | 
            +
                              update_type_owner(owner, indirect_int_type)
         | 
| 113 | 
            +
                            end
         | 
| 114 | 
            +
                          end
         | 
| 110 115 | 
             
                        end
         | 
| 111 116 | 
             
                      end
         | 
| 112 117 | 
             
                    when nil
         |