graphql 2.0.0 → 2.0.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/graphql/dataloader/null_dataloader.rb +3 -1
- data/lib/graphql/execution/interpreter/runtime.rb +15 -22
- data/lib/graphql/query/context.rb +96 -9
- data/lib/graphql/query/input_validation_result.rb +10 -1
- data/lib/graphql/query.rb +0 -1
- data/lib/graphql/schema/enum.rb +3 -5
- data/lib/graphql/schema/field.rb +67 -48
- 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/validates_input.rb +2 -2
- data/lib/graphql/schema/relay_classic_mutation.rb +32 -14
- data/lib/graphql/schema/resolver/has_payload_type.rb +1 -1
- data/lib/graphql/schema/resolver.rb +23 -45
- data/lib/graphql/schema/scalar.rb +4 -4
- data/lib/graphql/schema/subscription.rb +0 -7
- data/lib/graphql/schema/warden.rb +1 -1
- data/lib/graphql/subscriptions.rb +10 -3
- data/lib/graphql/version.rb +1 -1
- metadata +2 -3
- 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: f37881627f4e4ddcdb6f2f83f395982d624396949b7e26c1c7fa395f34b6c37a
         | 
| 4 | 
            +
              data.tar.gz: c4840087ffff471824df26453876279b2e44014cdf99f9778b35481c917e18fa
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 34999652dad1730d8cf51a46b213dc62d34a00e429b734da857aadbe5d5497c1a70c5d6ebc9d0a316db65a2e8e40075ab56d8bb59eb1cdc45b88c9549149ff92
         | 
| 7 | 
            +
              data.tar.gz: 8ff253848a6fcdaaaa831c87377b4b9d4ca494704bba0844336dc3c500b125df5f888f076f77cdf4fae61c03b2d8cb9383e18406bbf8b6ab2d7bd399dd54bf7d
         | 
| @@ -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
         | 
| @@ -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
         | 
| @@ -510,7 +506,7 @@ module GraphQL | |
| 510 506 | 
             
                          rescue GraphQL::ExecutionError => err
         | 
| 511 507 | 
             
                            err
         | 
| 512 508 | 
             
                          end
         | 
| 513 | 
            -
                          after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node,  | 
| 509 | 
            +
                          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 510 | 
             
                            continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
         | 
| 515 511 | 
             
                            if HALT != continue_value
         | 
| 516 512 | 
             
                              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 +684,7 @@ module GraphQL | |
| 688 684 | 
             
                        resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
         | 
| 689 685 | 
             
                        resolved_value ||= value
         | 
| 690 686 |  | 
| 691 | 
            -
                        after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node,  | 
| 687 | 
            +
                        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 688 | 
             
                          possible_types = query.possible_types(current_type)
         | 
| 693 689 |  | 
| 694 690 | 
             
                          if !possible_types.include?(resolved_type)
         | 
| @@ -708,7 +704,7 @@ module GraphQL | |
| 708 704 | 
             
                        rescue GraphQL::ExecutionError => err
         | 
| 709 705 | 
             
                          err
         | 
| 710 706 | 
             
                        end
         | 
| 711 | 
            -
                        after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node,  | 
| 707 | 
            +
                        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 708 | 
             
                          continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
         | 
| 713 709 | 
             
                          if HALT != continue_value
         | 
| 714 710 | 
             
                            response_hash = GraphQLResultHash.new(result_name, selection_result)
         | 
| @@ -734,7 +730,6 @@ module GraphQL | |
| 734 730 | 
             
                              call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
         | 
| 735 731 | 
             
                                evaluate_selections(
         | 
| 736 732 | 
             
                                  path,
         | 
| 737 | 
            -
                                  context.scoped_context,
         | 
| 738 733 | 
             
                                  continue_value,
         | 
| 739 734 | 
             
                                  current_type,
         | 
| 740 735 | 
             
                                  false,
         | 
| @@ -757,7 +752,6 @@ module GraphQL | |
| 757 752 | 
             
                        set_result(selection_result, result_name, response_list)
         | 
| 758 753 |  | 
| 759 754 | 
             
                        idx = 0
         | 
| 760 | 
            -
                        scoped_context = context.scoped_context
         | 
| 761 755 | 
             
                        begin
         | 
| 762 756 | 
             
                          value.each do |inner_value|
         | 
| 763 757 | 
             
                            break if dead_result?(response_list)
         | 
| @@ -768,10 +762,10 @@ module GraphQL | |
| 768 762 | 
             
                            idx += 1
         | 
| 769 763 | 
             
                            if use_dataloader_job
         | 
| 770 764 | 
             
                              @dataloader.append_job do
         | 
| 771 | 
            -
                                resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 765 | 
            +
                                resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
         | 
| 772 766 | 
             
                              end
         | 
| 773 767 | 
             
                            else
         | 
| 774 | 
            -
                              resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 768 | 
            +
                              resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
         | 
| 775 769 | 
             
                            end
         | 
| 776 770 | 
             
                          end
         | 
| 777 771 | 
             
                        rescue NoMethodError => err
         | 
| @@ -791,11 +785,11 @@ module GraphQL | |
| 791 785 | 
             
                      end
         | 
| 792 786 | 
             
                    end
         | 
| 793 787 |  | 
| 794 | 
            -
                    def resolve_list_item(inner_value, inner_type, next_path, ast_node,  | 
| 788 | 
            +
                    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 789 | 
             
                      set_all_interpreter_context(nil, nil, nil, next_path)
         | 
| 796 790 | 
             
                      call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
         | 
| 797 791 | 
             
                        # This will update `response_list` with the lazy
         | 
| 798 | 
            -
                        after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node,  | 
| 792 | 
            +
                        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 793 | 
             
                          continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
         | 
| 800 794 | 
             
                          if HALT != continue_value
         | 
| 801 795 | 
             
                            continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
         | 
| @@ -870,11 +864,10 @@ module GraphQL | |
| 870 864 | 
             
                    # @param eager [Boolean] Set to `true` for mutation root fields only
         | 
| 871 865 | 
             
                    # @param trace [Boolean] If `false`, don't wrap this with field tracing
         | 
| 872 866 | 
             
                    # @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:,  | 
| 867 | 
            +
                    def after_lazy(lazy_obj, owner:, field:, path:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
         | 
| 874 868 | 
             
                      if lazy?(lazy_obj)
         | 
| 875 869 | 
             
                        lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
         | 
| 876 870 | 
             
                          set_all_interpreter_context(owner_object, field, arguments, path)
         | 
| 877 | 
            -
                          context.scoped_context = scoped_context
         | 
| 878 871 | 
             
                          # Wrap the execution of _this_ method with tracing,
         | 
| 879 872 | 
             
                          # but don't wrap the continuation below
         | 
| 880 873 | 
             
                          inner_obj = begin
         | 
| @@ -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
    
    
    
        data/lib/graphql/schema/enum.rb
    CHANGED
    
    | @@ -123,16 +123,14 @@ module GraphQL | |
| 123 123 | 
             
                    end
         | 
| 124 124 |  | 
| 125 125 | 
             
                    def validate_non_null_input(value_name, ctx)
         | 
| 126 | 
            -
                      result = GraphQL::Query::InputValidationResult.new
         | 
| 127 | 
            -
             | 
| 128 126 | 
             
                      allowed_values = ctx.warden.enum_values(self)
         | 
| 129 127 | 
             
                      matching_value = allowed_values.find { |v| v.graphql_name == value_name }
         | 
| 130 128 |  | 
| 131 129 | 
             
                      if matching_value.nil?
         | 
| 132 | 
            -
                         | 
| 130 | 
            +
                        GraphQL::Query::InputValidationResult.from_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}")
         | 
| 131 | 
            +
                      else
         | 
| 132 | 
            +
                        nil
         | 
| 133 133 | 
             
                      end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                      result
         | 
| 136 134 | 
             
                    end
         | 
| 137 135 |  | 
| 138 136 | 
             
                    def coerce_result(value, ctx)
         | 
    
        data/lib/graphql/schema/field.rb
    CHANGED
    
    | @@ -29,7 +29,13 @@ module GraphQL | |
| 29 29 | 
             
                  attr_reader :method_str
         | 
| 30 30 |  | 
| 31 31 | 
             
                  # @return [Symbol] The method on the type to look up
         | 
| 32 | 
            -
                   | 
| 32 | 
            +
                  def resolver_method
         | 
| 33 | 
            +
                    if @resolver_class
         | 
| 34 | 
            +
                      @resolver_class.resolver_method
         | 
| 35 | 
            +
                    else
         | 
| 36 | 
            +
                      @resolver_method
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 33 39 |  | 
| 34 40 | 
             
                  # @return [Class] The thing this field was defined on (type, mutation, resolver)
         | 
| 35 41 | 
             
                  attr_accessor :owner
         | 
| @@ -68,7 +74,10 @@ module GraphQL | |
| 68 74 | 
             
                  attr_reader :trace
         | 
| 69 75 |  | 
| 70 76 | 
             
                  # @return [String, nil]
         | 
| 71 | 
            -
                   | 
| 77 | 
            +
                  def subscription_scope
         | 
| 78 | 
            +
                    @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil)
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
                  attr_writer :subscription_scope
         | 
| 72 81 |  | 
| 73 82 | 
             
                  # Create a field instance from a list of arguments, keyword arguments, and a block.
         | 
| 74 83 | 
             
                  #
         | 
| @@ -82,11 +91,9 @@ module GraphQL | |
| 82 91 | 
             
                  # @return [GraphQL::Schema:Field] an instance of `self
         | 
| 83 92 | 
             
                  # @see {.initialize} for other options
         | 
| 84 93 | 
             
                  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
         | 
| 85 | 
            -
                    if ( | 
| 86 | 
            -
                      # Get the parent config, merge in local overrides
         | 
| 87 | 
            -
                      kwargs = parent_config.field_options.merge(kwargs)
         | 
| 94 | 
            +
                    if (resolver_class = resolver || mutation || subscription)
         | 
| 88 95 | 
             
                      # Add a reference to that parent class
         | 
| 89 | 
            -
                      kwargs[:resolver_class] =  | 
| 96 | 
            +
                      kwargs[:resolver_class] = resolver_class
         | 
| 90 97 | 
             
                    end
         | 
| 91 98 |  | 
| 92 99 | 
             
                    if name
         | 
| @@ -101,8 +108,8 @@ module GraphQL | |
| 101 108 |  | 
| 102 109 | 
             
                        kwargs[:description] = desc
         | 
| 103 110 | 
             
                        kwargs[:type] = type
         | 
| 104 | 
            -
                      elsif ( | 
| 105 | 
            -
                        # The return type should be copied from  | 
| 111 | 
            +
                      elsif (resolver || mutation) && type.is_a?(String)
         | 
| 112 | 
            +
                        # The return type should be copied from the resolver, and the second positional argument is the description
         | 
| 106 113 | 
             
                        kwargs[:description] = type
         | 
| 107 114 | 
             
                      else
         | 
| 108 115 | 
             
                        kwargs[:type] = type
         | 
| @@ -119,8 +126,8 @@ module GraphQL | |
| 119 126 | 
             
                  def connection?
         | 
| 120 127 | 
             
                    if @connection.nil?
         | 
| 121 128 | 
             
                      # Provide default based on type name
         | 
| 122 | 
            -
                      return_type_name = if  | 
| 123 | 
            -
                        Member::BuildType.to_type_name( | 
| 129 | 
            +
                      return_type_name = if @resolver_class && @resolver_class.type
         | 
| 130 | 
            +
                        Member::BuildType.to_type_name(@resolver_class.type)
         | 
| 124 131 | 
             
                      elsif @return_type_expr
         | 
| 125 132 | 
             
                        Member::BuildType.to_type_name(@return_type_expr)
         | 
| 126 133 | 
             
                      else
         | 
| @@ -181,9 +188,6 @@ module GraphQL | |
| 181 188 | 
             
                  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
         | 
| 182 189 | 
             
                  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
         | 
| 183 190 | 
             
                  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
         | 
| 184 | 
            -
                  # @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
         | 
| 185 | 
            -
                  # @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
         | 
| 186 | 
            -
                  # @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
         | 
| 187 191 | 
             
                  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
         | 
| 188 192 | 
             
                  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
         | 
| 189 193 | 
             
                  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
         | 
| @@ -197,30 +201,20 @@ module GraphQL | |
| 197 201 | 
             
                  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
         | 
| 198 202 | 
             
                  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
         | 
| 199 203 | 
             
                  # @param validates [Array<Hash>] Configurations for validating this field
         | 
| 200 | 
            -
                  def initialize(type: nil, name: nil, owner: nil, null: true,  | 
| 204 | 
            +
                  def initialize(type: nil, name: nil, owner: nil, null: true, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, &definition_block)
         | 
| 201 205 | 
             
                    if name.nil?
         | 
| 202 206 | 
             
                      raise ArgumentError, "missing first `name` argument or keyword `name:`"
         | 
| 203 207 | 
             
                    end
         | 
| 204 | 
            -
                    if !( | 
| 208 | 
            +
                    if !(resolver_class)
         | 
| 205 209 | 
             
                      if type.nil?
         | 
| 206 210 | 
             
                        raise ArgumentError, "missing second `type` argument or keyword `type:`"
         | 
| 207 211 | 
             
                      end
         | 
| 208 212 | 
             
                    end
         | 
| 209 | 
            -
                    if (field || function || resolve) && extras.any?
         | 
| 210 | 
            -
                      raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
         | 
| 211 | 
            -
                    end
         | 
| 212 213 | 
             
                    @original_name = name
         | 
| 213 214 | 
             
                    name_s = -name.to_s
         | 
| 214 215 | 
             
                    @underscored_name = -Member::BuildType.underscore(name_s)
         | 
| 215 216 | 
             
                    @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
         | 
| 216 217 | 
             
                    @description = description
         | 
| 217 | 
            -
                    if field.is_a?(GraphQL::Schema::Field)
         | 
| 218 | 
            -
                      raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
         | 
| 219 | 
            -
                    else
         | 
| 220 | 
            -
                      @field = field
         | 
| 221 | 
            -
                    end
         | 
| 222 | 
            -
                    @function = function
         | 
| 223 | 
            -
                    @resolve = resolve
         | 
| 224 218 | 
             
                    self.deprecation_reason = deprecation_reason
         | 
| 225 219 |  | 
| 226 220 | 
             
                    if method && hash_key && dig
         | 
| @@ -253,7 +247,9 @@ module GraphQL | |
| 253 247 | 
             
                    @max_page_size = max_page_size == :not_given ? nil : max_page_size
         | 
| 254 248 | 
             
                    @introspection = introspection
         | 
| 255 249 | 
             
                    @extras = extras
         | 
| 256 | 
            -
                     | 
| 250 | 
            +
                    if !broadcastable.nil?
         | 
| 251 | 
            +
                      @broadcastable = broadcastable
         | 
| 252 | 
            +
                    end
         | 
| 257 253 | 
             
                    @resolver_class = resolver_class
         | 
| 258 254 | 
             
                    @scope = scope
         | 
| 259 255 | 
             
                    @trace = trace
         | 
| @@ -297,6 +293,10 @@ module GraphQL | |
| 297 293 | 
             
                      self.extensions(extensions)
         | 
| 298 294 | 
             
                    end
         | 
| 299 295 |  | 
| 296 | 
            +
                    if resolver_class && resolver_class.extensions.any?
         | 
| 297 | 
            +
                      self.extensions(resolver_class.extensions)
         | 
| 298 | 
            +
                    end
         | 
| 299 | 
            +
             | 
| 300 300 | 
             
                    if directives.any?
         | 
| 301 301 | 
             
                      directives.each do |(dir_class, options)|
         | 
| 302 302 | 
             
                        self.directive(dir_class, **options)
         | 
| @@ -321,7 +321,13 @@ module GraphQL | |
| 321 321 | 
             
                  # @return [Boolean, nil]
         | 
| 322 322 | 
             
                  # @see GraphQL::Subscriptions::BroadcastAnalyzer
         | 
| 323 323 | 
             
                  def broadcastable?
         | 
| 324 | 
            -
                    @broadcastable
         | 
| 324 | 
            +
                    if defined?(@broadcastable)
         | 
| 325 | 
            +
                      @broadcastable
         | 
| 326 | 
            +
                    elsif @resolver_class
         | 
| 327 | 
            +
                      @resolver_class.broadcastable?
         | 
| 328 | 
            +
                    else
         | 
| 329 | 
            +
                      nil
         | 
| 330 | 
            +
                    end
         | 
| 325 331 | 
             
                  end
         | 
| 326 332 |  | 
| 327 333 | 
             
                  # @param text [String]
         | 
| @@ -329,6 +335,8 @@ module GraphQL | |
| 329 335 | 
             
                  def description(text = nil)
         | 
| 330 336 | 
             
                    if text
         | 
| 331 337 | 
             
                      @description = text
         | 
| 338 | 
            +
                    elsif @resolver_class
         | 
| 339 | 
            +
                      @description || @resolver_class.description
         | 
| 332 340 | 
             
                    else
         | 
| 333 341 | 
             
                      @description
         | 
| 334 342 | 
             
                    end
         | 
| @@ -394,7 +402,12 @@ module GraphQL | |
| 394 402 | 
             
                  def extras(new_extras = nil)
         | 
| 395 403 | 
             
                    if new_extras.nil?
         | 
| 396 404 | 
             
                      # Read the value
         | 
| 397 | 
            -
                      @extras
         | 
| 405 | 
            +
                      field_extras = @extras
         | 
| 406 | 
            +
                      if @resolver_class && @resolver_class.extras.any?
         | 
| 407 | 
            +
                        field_extras + @resolver_class.extras
         | 
| 408 | 
            +
                      else
         | 
| 409 | 
            +
                        field_extras
         | 
| 410 | 
            +
                      end
         | 
| 398 411 | 
             
                    else
         | 
| 399 412 | 
             
                      if @extras.frozen?
         | 
| 400 413 | 
             
                        @extras = @extras.dup
         | 
| @@ -477,7 +490,11 @@ module GraphQL | |
| 477 490 | 
             
                    when Numeric
         | 
| 478 491 | 
             
                      @complexity = new_complexity
         | 
| 479 492 | 
             
                    when nil
         | 
| 480 | 
            -
                      @ | 
| 493 | 
            +
                      if @resolver_class
         | 
| 494 | 
            +
                        @complexity || @resolver_class.complexity || 1
         | 
| 495 | 
            +
                      else
         | 
| 496 | 
            +
                        @complexity || 1
         | 
| 497 | 
            +
                      end
         | 
| 481 498 | 
             
                    else
         | 
| 482 499 | 
             
                      raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
         | 
| 483 500 | 
             
                    end
         | 
| @@ -485,29 +502,31 @@ module GraphQL | |
| 485 502 |  | 
| 486 503 | 
             
                  # @return [Boolean] True if this field's {#max_page_size} should override the schema default.
         | 
| 487 504 | 
             
                  def has_max_page_size?
         | 
| 488 | 
            -
                    @has_max_page_size
         | 
| 505 | 
            +
                    @has_max_page_size || (@resolver_class && @resolver_class.has_max_page_size?)
         | 
| 489 506 | 
             
                  end
         | 
| 490 507 |  | 
| 491 508 | 
             
                  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
         | 
| 492 | 
            -
                   | 
| 509 | 
            +
                  def max_page_size
         | 
| 510 | 
            +
                    @max_page_size || (@resolver_class && @resolver_class.max_page_size)
         | 
| 511 | 
            +
                  end
         | 
| 493 512 |  | 
| 494 513 | 
             
                  class MissingReturnTypeError < GraphQL::Error; end
         | 
| 495 514 | 
             
                  attr_writer :type
         | 
| 496 515 |  | 
| 497 516 | 
             
                  def type
         | 
| 498 | 
            -
                    @ | 
| 499 | 
            -
                       | 
| 500 | 
            -
                    elsif @field
         | 
| 501 | 
            -
                      Member::BuildType.parse_type(@field.type, null: false)
         | 
| 502 | 
            -
                    elsif @return_type_expr.nil?
         | 
| 503 | 
            -
                      # Not enough info to determine type
         | 
| 504 | 
            -
                      message = "Can't determine the return type for #{self.path}"
         | 
| 505 | 
            -
                      if @resolver_class
         | 
| 506 | 
            -
                        message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
         | 
| 507 | 
            -
                      end
         | 
| 508 | 
            -
                      raise MissingReturnTypeError, message
         | 
| 517 | 
            +
                    if @resolver_class && (t = @resolver_class.type)
         | 
| 518 | 
            +
                      t
         | 
| 509 519 | 
             
                    else
         | 
| 510 | 
            -
                       | 
| 520 | 
            +
                      @type ||= if @return_type_expr.nil?
         | 
| 521 | 
            +
                        # Not enough info to determine type
         | 
| 522 | 
            +
                        message = "Can't determine the return type for #{self.path}"
         | 
| 523 | 
            +
                        if @resolver_class
         | 
| 524 | 
            +
                          message += " (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
         | 
| 525 | 
            +
                        end
         | 
| 526 | 
            +
                        raise MissingReturnTypeError, message
         | 
| 527 | 
            +
                      else
         | 
| 528 | 
            +
                        Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
         | 
| 529 | 
            +
                      end
         | 
| 511 530 | 
             
                    end
         | 
| 512 531 | 
             
                  rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
         | 
| 513 532 | 
             
                    # Let this propagate up
         | 
| @@ -637,14 +656,14 @@ module GraphQL | |
| 637 656 | 
             
                        # - A method on the wrapped object;
         | 
| 638 657 | 
             
                        # - Or, raise not implemented.
         | 
| 639 658 | 
             
                        #
         | 
| 640 | 
            -
                        if obj.respond_to?( | 
| 641 | 
            -
                          method_to_call =  | 
| 659 | 
            +
                        if obj.respond_to?(resolver_method)
         | 
| 660 | 
            +
                          method_to_call = resolver_method
         | 
| 642 661 | 
             
                          method_receiver = obj
         | 
| 643 662 | 
             
                          # Call the method with kwargs, if there are any
         | 
| 644 663 | 
             
                          if ruby_kwargs.any?
         | 
| 645 | 
            -
                            obj.public_send( | 
| 664 | 
            +
                            obj.public_send(resolver_method, **ruby_kwargs)
         | 
| 646 665 | 
             
                          else
         | 
| 647 | 
            -
                            obj.public_send( | 
| 666 | 
            +
                            obj.public_send(resolver_method)
         | 
| 648 667 | 
             
                          end
         | 
| 649 668 | 
             
                        elsif obj.object.is_a?(Hash)
         | 
| 650 669 | 
             
                          inner_object = obj.object
         | 
| @@ -667,7 +686,7 @@ module GraphQL | |
| 667 686 | 
             
                          raise <<-ERR
         | 
| 668 687 | 
             
                        Failed to implement #{@owner.graphql_name}.#{@name}, tried:
         | 
| 669 688 |  | 
| 670 | 
            -
                        - `#{obj.class}##{ | 
| 689 | 
            +
                        - `#{obj.class}##{resolver_method}`, which did not exist
         | 
| 671 690 | 
             
                        - `#{obj.object.class}##{@method_sym}`, which did not exist
         | 
| 672 691 | 
             
                        - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
         | 
| 673 692 |  | 
| @@ -127,19 +127,15 @@ module GraphQL | |
| 127 127 | 
             
                    INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
         | 
| 128 128 |  | 
| 129 129 | 
             
                    def validate_non_null_input(input, ctx)
         | 
| 130 | 
            -
                      result = GraphQL::Query::InputValidationResult.new
         | 
| 131 | 
            -
             | 
| 132 130 | 
             
                      warden = ctx.warden
         | 
| 133 131 |  | 
| 134 132 | 
             
                      if input.is_a?(Array)
         | 
| 135 | 
            -
                         | 
| 136 | 
            -
                        return result
         | 
| 133 | 
            +
                        return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
         | 
| 137 134 | 
             
                      end
         | 
| 138 135 |  | 
| 139 136 | 
             
                      if !(input.respond_to?(:to_h) || input.respond_to?(:to_unsafe_h))
         | 
| 140 137 | 
             
                        # We're not sure it'll act like a hash, so reject it:
         | 
| 141 | 
            -
                         | 
| 142 | 
            -
                        return result
         | 
| 138 | 
            +
                        return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
         | 
| 143 139 | 
             
                      end
         | 
| 144 140 |  | 
| 145 141 | 
             
                      # Inject missing required arguments
         | 
| @@ -151,18 +147,22 @@ module GraphQL | |
| 151 147 | 
             
                        m
         | 
| 152 148 | 
             
                      end
         | 
| 153 149 |  | 
| 154 | 
            -
             | 
| 150 | 
            +
                      result = nil
         | 
| 155 151 | 
             
                      [input, missing_required_inputs].each do |args_to_validate|
         | 
| 156 152 | 
             
                        args_to_validate.each do |argument_name, value|
         | 
| 157 153 | 
             
                          argument = warden.get_argument(self, argument_name)
         | 
| 158 154 | 
             
                          # Items in the input that are unexpected
         | 
| 159 | 
            -
                           | 
| 155 | 
            +
                          if argument.nil?
         | 
| 156 | 
            +
                            result ||= Query::InputValidationResult.new
         | 
| 160 157 | 
             
                            result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
         | 
| 161 | 
            -
             | 
| 158 | 
            +
                          else
         | 
| 159 | 
            +
                            # Items in the input that are expected, but have invalid values
         | 
| 160 | 
            +
                            argument_result = argument.type.validate_input(value, ctx)
         | 
| 161 | 
            +
                            result ||= Query::InputValidationResult.new
         | 
| 162 | 
            +
                            if !argument_result.valid?
         | 
| 163 | 
            +
                              result.merge_result!(argument_name, argument_result)
         | 
| 164 | 
            +
                            end
         | 
| 162 165 | 
             
                          end
         | 
| 163 | 
            -
                          # Items in the input that are expected, but have invalid values
         | 
| 164 | 
            -
                          argument_result = argument.type.validate_input(value, ctx)
         | 
| 165 | 
            -
                          result.merge_result!(argument_name, argument_result) unless argument_result.valid?
         | 
| 166 166 | 
             
                        end
         | 
| 167 167 | 
             
                      end
         | 
| 168 168 |  | 
    
        data/lib/graphql/schema/list.rb
    CHANGED
    
    | @@ -46,10 +46,11 @@ module GraphQL | |
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 48 | 
             
                  def validate_non_null_input(value, ctx)
         | 
| 49 | 
            -
                    result =  | 
| 49 | 
            +
                    result = nil
         | 
| 50 50 | 
             
                    ensure_array(value).each_with_index do |item, index|
         | 
| 51 51 | 
             
                      item_result = of_type.validate_input(item, ctx)
         | 
| 52 52 | 
             
                      if !item_result.valid?
         | 
| 53 | 
            +
                        result ||= GraphQL::Query::InputValidationResult.new
         | 
| 53 54 | 
             
                        result.merge_result!(index, item_result)
         | 
| 54 55 | 
             
                      end
         | 
| 55 56 | 
             
                    end
         | 
| @@ -78,23 +78,44 @@ module GraphQL | |
| 78 78 | 
             
                    # @return [GraphQL::Schema::Argument]
         | 
| 79 79 | 
             
                    def add_argument(arg_defn)
         | 
| 80 80 | 
             
                      @own_arguments ||= {}
         | 
| 81 | 
            -
                      prev_defn = own_arguments[arg_defn.name]
         | 
| 81 | 
            +
                      prev_defn = @own_arguments[arg_defn.name]
         | 
| 82 82 | 
             
                      case prev_defn
         | 
| 83 83 | 
             
                      when nil
         | 
| 84 | 
            -
                        own_arguments[arg_defn.name] = arg_defn
         | 
| 84 | 
            +
                        @own_arguments[arg_defn.name] = arg_defn
         | 
| 85 85 | 
             
                      when Array
         | 
| 86 86 | 
             
                        prev_defn << arg_defn
         | 
| 87 87 | 
             
                      when GraphQL::Schema::Argument
         | 
| 88 | 
            -
                        own_arguments[arg_defn.name] = [prev_defn, arg_defn]
         | 
| 88 | 
            +
                        @own_arguments[arg_defn.name] = [prev_defn, arg_defn]
         | 
| 89 89 | 
             
                      else
         | 
| 90 90 | 
             
                        raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
         | 
| 91 91 | 
             
                      end
         | 
| 92 92 | 
             
                      arg_defn
         | 
| 93 93 | 
             
                    end
         | 
| 94 94 |  | 
| 95 | 
            +
                    def remove_argument(arg_defn)
         | 
| 96 | 
            +
                      prev_defn = @own_arguments[arg_defn.name]
         | 
| 97 | 
            +
                      case prev_defn
         | 
| 98 | 
            +
                      when nil
         | 
| 99 | 
            +
                        # done
         | 
| 100 | 
            +
                      when Array
         | 
| 101 | 
            +
                        prev_defn.delete(arg_defn)
         | 
| 102 | 
            +
                      when GraphQL::Schema::Argument
         | 
| 103 | 
            +
                        @own_arguments.delete(arg_defn.name)
         | 
| 104 | 
            +
                      else
         | 
| 105 | 
            +
                        raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                      nil
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
             | 
| 95 110 | 
             
                    # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
         | 
| 96 111 | 
             
                    def arguments(context = GraphQL::Query::NullContext)
         | 
| 97 | 
            -
                      inherited_arguments =  | 
| 112 | 
            +
                      inherited_arguments = if self.is_a?(Class) && superclass.respond_to?(:arguments)
         | 
| 113 | 
            +
                        superclass.arguments(context)
         | 
| 114 | 
            +
                      elsif defined?(@resolver_class) && @resolver_class
         | 
| 115 | 
            +
                        @resolver_class.field_arguments(context)
         | 
| 116 | 
            +
                      else
         | 
| 117 | 
            +
                        nil
         | 
| 118 | 
            +
                      end
         | 
| 98 119 | 
             
                      # Local definitions override inherited ones
         | 
| 99 120 | 
             
                      if own_arguments.any?
         | 
| 100 121 | 
             
                        own_arguments_that_apply = {}
         | 
| @@ -125,6 +146,10 @@ module GraphQL | |
| 125 146 | 
             
                            all_defns.merge!(ancestor.own_arguments)
         | 
| 126 147 | 
             
                          end
         | 
| 127 148 | 
             
                        end
         | 
| 149 | 
            +
                      elsif defined?(@resolver_class) && @resolver_class
         | 
| 150 | 
            +
                        all_defns = {}
         | 
| 151 | 
            +
                        all_defns.merge!(@resolver_class.own_field_arguments)
         | 
| 152 | 
            +
                        all_defns.merge!(own_arguments)
         | 
| 128 153 | 
             
                      else
         | 
| 129 154 | 
             
                        all_defns = own_arguments
         | 
| 130 155 | 
             
                      end
         | 
| @@ -137,8 +162,13 @@ module GraphQL | |
| 137 162 | 
             
                    def get_argument(argument_name, context = GraphQL::Query::NullContext)
         | 
| 138 163 | 
             
                      warden = Warden.from_context(context)
         | 
| 139 164 | 
             
                      if !self.is_a?(Class)
         | 
| 140 | 
            -
                         | 
| 141 | 
            -
             | 
| 165 | 
            +
                        if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
         | 
| 166 | 
            +
                          visible_arg
         | 
| 167 | 
            +
                        elsif defined?(@resolver_class) && @resolver_class
         | 
| 168 | 
            +
                          @resolver_class.get_field_argument(argument_name, context)
         | 
| 169 | 
            +
                        else
         | 
| 170 | 
            +
                          nil
         | 
| 171 | 
            +
                        end
         | 
| 142 172 | 
             
                      else
         | 
| 143 173 | 
             
                        for ancestor in ancestors
         | 
| 144 174 | 
             
                          if ancestor.respond_to?(:own_arguments) &&
         | 
| @@ -10,9 +10,9 @@ module GraphQL | |
| 10 10 |  | 
| 11 11 | 
             
                    def validate_input(val, ctx)
         | 
| 12 12 | 
             
                      if val.nil?
         | 
| 13 | 
            -
                         | 
| 13 | 
            +
                        Query::InputValidationResult::VALID
         | 
| 14 14 | 
             
                      else
         | 
| 15 | 
            -
                        validate_non_null_input(val, ctx)
         | 
| 15 | 
            +
                        validate_non_null_input(val, ctx) || Query::InputValidationResult::VALID
         | 
| 16 16 | 
             
                      end
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| @@ -70,11 +70,32 @@ module GraphQL | |
| 70 70 | 
             
                  end
         | 
| 71 71 |  | 
| 72 72 | 
             
                  class << self
         | 
| 73 | 
            +
                    def dummy
         | 
| 74 | 
            +
                      @dummy ||= begin
         | 
| 75 | 
            +
                        d = Class.new(GraphQL::Schema::Resolver)
         | 
| 76 | 
            +
                        d.argument_class(self.argument_class)
         | 
| 77 | 
            +
                        # TODO make this lazier?
         | 
| 78 | 
            +
                        d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
         | 
| 79 | 
            +
                        d
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    def field_arguments(context = GraphQL::Query::NullContext)
         | 
| 84 | 
            +
                      dummy.arguments(context)
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    def get_field_argument(name, context = GraphQL::Query::NullContext)
         | 
| 88 | 
            +
                      dummy.get_argument(name, context)
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    def own_field_arguments
         | 
| 92 | 
            +
                      dummy.own_arguments
         | 
| 93 | 
            +
                    end
         | 
| 73 94 |  | 
| 74 95 | 
             
                    # Also apply this argument to the input type:
         | 
| 75 | 
            -
                    def argument(*args, **kwargs, &block)
         | 
| 96 | 
            +
                    def argument(*args, own_argument: false, **kwargs, &block)
         | 
| 76 97 | 
             
                      it = input_type # make sure any inherited arguments are already added to it
         | 
| 77 | 
            -
                      arg = super
         | 
| 98 | 
            +
                      arg = super(*args, **kwargs, &block)
         | 
| 78 99 |  | 
| 79 100 | 
             
                      # This definition might be overriding something inherited;
         | 
| 80 101 | 
             
                      # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
         | 
| @@ -114,15 +135,6 @@ module GraphQL | |
| 114 135 | 
             
                      @input_type ||= generate_input_type
         | 
| 115 136 | 
             
                    end
         | 
| 116 137 |  | 
| 117 | 
            -
                    # Extend {Schema::Mutation.field_options} to add the `input` argument
         | 
| 118 | 
            -
                    def field_options
         | 
| 119 | 
            -
                      sig = super
         | 
| 120 | 
            -
                      # Arguments were added at the root, but they should be nested
         | 
| 121 | 
            -
                      sig[:arguments].clear
         | 
| 122 | 
            -
                      sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
         | 
| 123 | 
            -
                      sig
         | 
| 124 | 
            -
                    end
         | 
| 125 | 
            -
             | 
| 126 138 | 
             
                    private
         | 
| 127 139 |  | 
| 128 140 | 
             
                    # Generate the input type for the `input:` argument
         | 
| @@ -130,11 +142,17 @@ module GraphQL | |
| 130 142 | 
             
                    # @return [Class] a subclass of {.input_object_class}
         | 
| 131 143 | 
             
                    def generate_input_type
         | 
| 132 144 | 
             
                      mutation_args = all_argument_definitions
         | 
| 133 | 
            -
                      mutation_name = graphql_name
         | 
| 134 145 | 
             
                      mutation_class = self
         | 
| 135 146 | 
             
                      Class.new(input_object_class) do
         | 
| 136 | 
            -
                         | 
| 137 | 
            -
             | 
| 147 | 
            +
                        class << self
         | 
| 148 | 
            +
                          def default_graphql_name
         | 
| 149 | 
            +
                            "#{self.mutation.graphql_name}Input"
         | 
| 150 | 
            +
                          end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                          def description(new_desc = nil)
         | 
| 153 | 
            +
                            super || "Autogenerated input type of #{self.mutation.graphql_name}"
         | 
| 154 | 
            +
                          end
         | 
| 155 | 
            +
                        end
         | 
| 138 156 | 
             
                        mutation(mutation_class)
         | 
| 139 157 | 
             
                        # these might be inherited:
         | 
| 140 158 | 
             
                        mutation_args.each do |arg|
         | 
| @@ -15,8 +15,6 @@ module GraphQL | |
| 15 15 | 
             
                #
         | 
| 16 16 | 
             
                # A resolver's configuration may be overridden with other keywords in the `field(...)` call.
         | 
| 17 17 | 
             
                #
         | 
| 18 | 
            -
                # See the {.field_options} to see how a Resolver becomes a set of field configuration options.
         | 
| 19 | 
            -
                #
         | 
| 20 18 | 
             
                # @see {GraphQL::Schema::Mutation} for a concrete subclass of `Resolver`.
         | 
| 21 19 | 
             
                # @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function`
         | 
| 22 20 | 
             
                class Resolver
         | 
| @@ -210,6 +208,18 @@ module GraphQL | |
| 210 208 | 
             
                  end
         | 
| 211 209 |  | 
| 212 210 | 
             
                  class << self
         | 
| 211 | 
            +
                    def field_arguments(context = GraphQL::Query::NullContext)
         | 
| 212 | 
            +
                      arguments(context)
         | 
| 213 | 
            +
                    end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    def get_field_argument(name, context = GraphQL::Query::NullContext)
         | 
| 216 | 
            +
                      get_argument(name, context)
         | 
| 217 | 
            +
                    end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                    def own_field_arguments
         | 
| 220 | 
            +
                      own_arguments
         | 
| 221 | 
            +
                    end
         | 
| 222 | 
            +
             | 
| 213 223 | 
             
                    # Default `:resolve` set below.
         | 
| 214 224 | 
             
                    # @return [Symbol] The method to call on instances of this object to resolve the field
         | 
| 215 225 | 
             
                    def resolve_method(new_method = nil)
         | 
| @@ -242,6 +252,14 @@ module GraphQL | |
| 242 252 | 
             
                      @null.nil? ? (superclass.respond_to?(:null) ? superclass.null : true) : @null
         | 
| 243 253 | 
             
                    end
         | 
| 244 254 |  | 
| 255 | 
            +
                    def resolver_method(new_method_name = nil)
         | 
| 256 | 
            +
                      if new_method_name
         | 
| 257 | 
            +
                        @resolver_method = new_method_name
         | 
| 258 | 
            +
                      else
         | 
| 259 | 
            +
                        @resolver_method || :resolve_with_support
         | 
| 260 | 
            +
                      end
         | 
| 261 | 
            +
                    end
         | 
| 262 | 
            +
             | 
| 245 263 | 
             
                    # Call this method to get the return type of the field,
         | 
| 246 264 | 
             
                    # or use it as a configuration method to assign a return type
         | 
| 247 265 | 
             
                    # instead of generating one.
         | 
| @@ -257,8 +275,8 @@ module GraphQL | |
| 257 275 | 
             
                        @type_expr = new_type
         | 
| 258 276 | 
             
                        @null = null
         | 
| 259 277 | 
             
                      else
         | 
| 260 | 
            -
                        if  | 
| 261 | 
            -
                          GraphQL::Schema::Member::BuildType.parse_type( | 
| 278 | 
            +
                        if type_expr
         | 
| 279 | 
            +
                          GraphQL::Schema::Member::BuildType.parse_type(type_expr, null: self.null)
         | 
| 262 280 | 
             
                        elsif superclass.respond_to?(:type)
         | 
| 263 281 | 
             
                          superclass.type
         | 
| 264 282 | 
             
                        else
         | 
| @@ -307,47 +325,7 @@ module GraphQL | |
| 307 325 |  | 
| 308 326 | 
             
                    # @return [Boolean] `true` if this resolver or a superclass has an assigned `max_page_size`
         | 
| 309 327 | 
             
                    def has_max_page_size?
         | 
| 310 | 
            -
                      defined?(@max_page_size) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
         | 
| 311 | 
            -
                    end
         | 
| 312 | 
            -
             | 
| 313 | 
            -
                    def field_options
         | 
| 314 | 
            -
             | 
| 315 | 
            -
                      all_args = {}
         | 
| 316 | 
            -
                      all_argument_definitions.each do |arg|
         | 
| 317 | 
            -
                        if (prev_entry = all_args[arg.graphql_name])
         | 
| 318 | 
            -
                          if prev_entry.is_a?(Array)
         | 
| 319 | 
            -
                            prev_entry << arg
         | 
| 320 | 
            -
                          else
         | 
| 321 | 
            -
                            all_args[arg.graphql_name] = [prev_entry, arg]
         | 
| 322 | 
            -
                          end
         | 
| 323 | 
            -
                        else
         | 
| 324 | 
            -
                          all_args[arg.graphql_name] = arg
         | 
| 325 | 
            -
                        end
         | 
| 326 | 
            -
                      end
         | 
| 327 | 
            -
             | 
| 328 | 
            -
                      field_opts = {
         | 
| 329 | 
            -
                        type: type_expr,
         | 
| 330 | 
            -
                        description: description,
         | 
| 331 | 
            -
                        extras: extras,
         | 
| 332 | 
            -
                        resolver_method: :resolve_with_support,
         | 
| 333 | 
            -
                        resolver_class: self,
         | 
| 334 | 
            -
                        arguments: all_args,
         | 
| 335 | 
            -
                        null: null,
         | 
| 336 | 
            -
                        complexity: complexity,
         | 
| 337 | 
            -
                        broadcastable: broadcastable?,
         | 
| 338 | 
            -
                      }
         | 
| 339 | 
            -
             | 
| 340 | 
            -
                      # If there aren't any, then the returned array is `[].freeze`,
         | 
| 341 | 
            -
                      # but passing that along breaks some user code.
         | 
| 342 | 
            -
                      if (exts = extensions).any?
         | 
| 343 | 
            -
                        field_opts[:extensions] = exts
         | 
| 344 | 
            -
                      end
         | 
| 345 | 
            -
             | 
| 346 | 
            -
                      if has_max_page_size?
         | 
| 347 | 
            -
                        field_opts[:max_page_size] = max_page_size
         | 
| 348 | 
            -
                      end
         | 
| 349 | 
            -
             | 
| 350 | 
            -
                      field_opts
         | 
| 328 | 
            +
                      (!!defined?(@max_page_size)) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
         | 
| 351 329 | 
             
                    end
         | 
| 352 330 |  | 
| 353 331 | 
             
                    # A non-normalized type configuration, without `null` applied
         | 
| @@ -41,7 +41,6 @@ module GraphQL | |
| 41 41 | 
             
                    end
         | 
| 42 42 |  | 
| 43 43 | 
             
                    def validate_non_null_input(value, ctx)
         | 
| 44 | 
            -
                      result = Query::InputValidationResult.new
         | 
| 45 44 | 
             
                      coerced_result = begin
         | 
| 46 45 | 
             
                        ctx.query.with_error_handling do
         | 
| 47 46 | 
             
                          coerce_input(value, ctx)
         | 
| @@ -56,11 +55,12 @@ module GraphQL | |
| 56 55 | 
             
                        else
         | 
| 57 56 | 
             
                          " #{GraphQL::Language.serialize(value)}"
         | 
| 58 57 | 
             
                        end
         | 
| 59 | 
            -
                         | 
| 58 | 
            +
                        Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
         | 
| 60 59 | 
             
                      elsif coerced_result.is_a?(GraphQL::CoercionError)
         | 
| 61 | 
            -
                         | 
| 60 | 
            +
                        Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
         | 
| 61 | 
            +
                      else
         | 
| 62 | 
            +
                        nil
         | 
| 62 63 | 
             
                      end
         | 
| 63 | 
            -
                      result
         | 
| 64 64 | 
             
                    end
         | 
| 65 65 | 
             
                  end
         | 
| 66 66 | 
             
                end
         | 
| @@ -143,13 +143,6 @@ module GraphQL | |
| 143 143 | 
             
                  def self.topic_for(arguments:, field:, scope:)
         | 
| 144 144 | 
             
                    Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
         | 
| 145 145 | 
             
                  end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  # Overriding Resolver#field_options to include subscription_scope
         | 
| 148 | 
            -
                  def self.field_options
         | 
| 149 | 
            -
                    super.merge(
         | 
| 150 | 
            -
                      subscription_scope: subscription_scope
         | 
| 151 | 
            -
                    )
         | 
| 152 | 
            -
                  end
         | 
| 153 146 | 
             
                end
         | 
| 154 147 | 
             
              end
         | 
| 155 148 | 
             
            end
         | 
| @@ -10,7 +10,7 @@ module GraphQL | |
| 10 10 | 
             
                # should go through a warden. If you access the schema directly,
         | 
| 11 11 | 
             
                # you may show a client something that it shouldn't be allowed to see.
         | 
| 12 12 | 
             
                #
         | 
| 13 | 
            -
                # @example  | 
| 13 | 
            +
                # @example Hiding private fields
         | 
| 14 14 | 
             
                #   private_members = -> (member, ctx) { member.metadata[:private] }
         | 
| 15 15 | 
             
                #   result = Schema.execute(query_string, except: private_members)
         | 
| 16 16 | 
             
                #
         | 
| @@ -56,17 +56,24 @@ module GraphQL | |
| 56 56 | 
             
                # @param args [Hash<String, Symbol => Object]
         | 
| 57 57 | 
             
                # @param object [Object]
         | 
| 58 58 | 
             
                # @param scope [Symbol, String]
         | 
| 59 | 
            +
                # @param context [Hash]
         | 
| 59 60 | 
             
                # @return [void]
         | 
| 60 | 
            -
                def trigger(event_name, args, object, scope: nil)
         | 
| 61 | 
            +
                def trigger(event_name, args, object, scope: nil, context: {})
         | 
| 62 | 
            +
                  # Make something as context-like as possible, even though there isn't a current query:
         | 
| 63 | 
            +
                  context = @schema.context_class.new(
         | 
| 64 | 
            +
                    query: GraphQL::Query.new(@schema, "", validate: false),
         | 
| 65 | 
            +
                    object: nil,
         | 
| 66 | 
            +
                    values: context
         | 
| 67 | 
            +
                  )
         | 
| 61 68 | 
             
                  event_name = event_name.to_s
         | 
| 62 69 |  | 
| 63 70 | 
             
                  # Try with the verbatim input first:
         | 
| 64 | 
            -
                  field = @schema.get_field(@schema.subscription, event_name)
         | 
| 71 | 
            +
                  field = @schema.get_field(@schema.subscription, event_name, context)
         | 
| 65 72 |  | 
| 66 73 | 
             
                  if field.nil?
         | 
| 67 74 | 
             
                    # And if it wasn't found, normalize it:
         | 
| 68 75 | 
             
                    normalized_event_name = normalize_name(event_name)
         | 
| 69 | 
            -
                    field = @schema.get_field(@schema.subscription, normalized_event_name)
         | 
| 76 | 
            +
                    field = @schema.get_field(@schema.subscription, normalized_event_name, context)
         | 
| 70 77 | 
             
                    if field.nil?
         | 
| 71 78 | 
             
                      raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
         | 
| 72 79 | 
             
                    end
         | 
    
        data/lib/graphql/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: graphql
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0. | 
| 4 | 
            +
              version: 2.0.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Robert Mosolgo
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022-02- | 
| 11 | 
            +
            date: 2022-02-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: benchmark-ips
         | 
| @@ -366,7 +366,6 @@ files: | |
| 366 366 | 
             
            - lib/graphql/query/context.rb
         | 
| 367 367 | 
             
            - lib/graphql/query/fingerprint.rb
         | 
| 368 368 | 
             
            - lib/graphql/query/input_validation_result.rb
         | 
| 369 | 
            -
            - lib/graphql/query/literal_input.rb
         | 
| 370 369 | 
             
            - lib/graphql/query/null_context.rb
         | 
| 371 370 | 
             
            - lib/graphql/query/result.rb
         | 
| 372 371 | 
             
            - lib/graphql/query/validation_pipeline.rb
         | 
| @@ -1,131 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            module GraphQL
         | 
| 3 | 
            -
              class Query
         | 
| 4 | 
            -
                # Turn query string values into something useful for query execution
         | 
| 5 | 
            -
                class LiteralInput
         | 
| 6 | 
            -
                  def self.coerce(type, ast_node, variables)
         | 
| 7 | 
            -
                    case ast_node
         | 
| 8 | 
            -
                    when nil
         | 
| 9 | 
            -
                      nil
         | 
| 10 | 
            -
                    when Language::Nodes::NullValue
         | 
| 11 | 
            -
                      nil
         | 
| 12 | 
            -
                    when Language::Nodes::VariableIdentifier
         | 
| 13 | 
            -
                      variables[ast_node.name]
         | 
| 14 | 
            -
                    else
         | 
| 15 | 
            -
                      case type.kind.name
         | 
| 16 | 
            -
                      when "SCALAR"
         | 
| 17 | 
            -
                        # TODO smell
         | 
| 18 | 
            -
                        # This gets used for plain values during subscriber.trigger
         | 
| 19 | 
            -
                        if variables
         | 
| 20 | 
            -
                          type.coerce_input(ast_node, variables.context)
         | 
| 21 | 
            -
                        else
         | 
| 22 | 
            -
                          type.coerce_isolated_input(ast_node)
         | 
| 23 | 
            -
                        end
         | 
| 24 | 
            -
                      when "ENUM"
         | 
| 25 | 
            -
                        # TODO smell
         | 
| 26 | 
            -
                        # This gets used for plain values sometimes
         | 
| 27 | 
            -
                        v = ast_node.is_a?(GraphQL::Language::Nodes::Enum) ? ast_node.name : ast_node
         | 
| 28 | 
            -
                        if variables
         | 
| 29 | 
            -
                          type.coerce_input(v, variables.context)
         | 
| 30 | 
            -
                        else
         | 
| 31 | 
            -
                          type.coerce_isolated_input(v)
         | 
| 32 | 
            -
                        end
         | 
| 33 | 
            -
                      when "NON_NULL"
         | 
| 34 | 
            -
                        LiteralInput.coerce(type.of_type, ast_node, variables)
         | 
| 35 | 
            -
                      when "LIST"
         | 
| 36 | 
            -
                        if ast_node.is_a?(Array)
         | 
| 37 | 
            -
                          ast_node.map { |element_ast| LiteralInput.coerce(type.of_type, element_ast, variables) }
         | 
| 38 | 
            -
                        else
         | 
| 39 | 
            -
                          [LiteralInput.coerce(type.of_type, ast_node, variables)]
         | 
| 40 | 
            -
                        end
         | 
| 41 | 
            -
                      when "INPUT_OBJECT"
         | 
| 42 | 
            -
                        # TODO smell: handling AST vs handling plain Ruby
         | 
| 43 | 
            -
                        next_args = ast_node.is_a?(Hash) ? ast_node : ast_node.arguments
         | 
| 44 | 
            -
                        from_arguments(next_args, type, variables)
         | 
| 45 | 
            -
                      else
         | 
| 46 | 
            -
                        raise "Invariant: unexpected type to coerce to: #{type}"
         | 
| 47 | 
            -
                      end
         | 
| 48 | 
            -
                    end
         | 
| 49 | 
            -
                  end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                  def self.from_arguments(ast_arguments, argument_owner, variables)
         | 
| 52 | 
            -
                    context = variables ? variables.context : nil
         | 
| 53 | 
            -
                    values_hash = {}
         | 
| 54 | 
            -
                    defaults_used = Set.new
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                    indexed_arguments = case ast_arguments
         | 
| 57 | 
            -
                    when Hash
         | 
| 58 | 
            -
                      ast_arguments
         | 
| 59 | 
            -
                    when Array
         | 
| 60 | 
            -
                      ast_arguments.each_with_object({}) { |a, memo| memo[a.name] = a }
         | 
| 61 | 
            -
                    else
         | 
| 62 | 
            -
                      raise ArgumentError, "Unexpected ast_arguments: #{ast_arguments}"
         | 
| 63 | 
            -
                    end
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                    argument_defns = argument_owner.arguments(context || GraphQL::Query::NullContext)
         | 
| 66 | 
            -
                    argument_defns.each do |arg_name, arg_defn|
         | 
| 67 | 
            -
                      ast_arg = indexed_arguments[arg_name]
         | 
| 68 | 
            -
                      # First, check the argument in the AST.
         | 
| 69 | 
            -
                      # If the value is a variable,
         | 
| 70 | 
            -
                      # only add a value if the variable is actually present.
         | 
| 71 | 
            -
                      # Otherwise, coerce the value in the AST, prepare the value and add it.
         | 
| 72 | 
            -
                      #
         | 
| 73 | 
            -
                      # TODO: since indexed_arguments can come from a plain Ruby hash,
         | 
| 74 | 
            -
                      # have to check for `false` or `nil` as hash values. This is getting smelly :S
         | 
| 75 | 
            -
                      if indexed_arguments.key?(arg_name)
         | 
| 76 | 
            -
                        arg_value = ast_arg.is_a?(GraphQL::Language::Nodes::Argument) ? ast_arg.value : ast_arg
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                        value_is_a_variable = arg_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                        if (!value_is_a_variable || (value_is_a_variable && variables.key?(arg_value.name)))
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                          value = coerce(arg_defn.type, arg_value, variables)
         | 
| 83 | 
            -
                          # Legacy `prepare` application
         | 
| 84 | 
            -
                          if arg_defn.is_a?(GraphQL::Argument)
         | 
| 85 | 
            -
                            value = arg_defn.prepare(value, context)
         | 
| 86 | 
            -
                          end
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                          if value.is_a?(GraphQL::ExecutionError)
         | 
| 89 | 
            -
                            value.ast_node = ast_arg
         | 
| 90 | 
            -
                            raise value
         | 
| 91 | 
            -
                          end
         | 
| 92 | 
            -
             | 
| 93 | 
            -
                          values_hash[arg_name] = value
         | 
| 94 | 
            -
                        end
         | 
| 95 | 
            -
                      end
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                      # Then, the definition for a default value.
         | 
| 98 | 
            -
                      # If the definition has a default value and
         | 
| 99 | 
            -
                      # a value wasn't provided from the AST,
         | 
| 100 | 
            -
                      # then add the default value.
         | 
| 101 | 
            -
                      if arg_defn.default_value? && !values_hash.key?(arg_name)
         | 
| 102 | 
            -
                        value = arg_defn.default_value
         | 
| 103 | 
            -
                        defaults_used << arg_name
         | 
| 104 | 
            -
                        # `context` isn't present when pre-calculating defaults
         | 
| 105 | 
            -
                        if context
         | 
| 106 | 
            -
                          if arg_defn.is_a?(GraphQL::Argument)
         | 
| 107 | 
            -
                            value = arg_defn.prepare(value, context)
         | 
| 108 | 
            -
                          end
         | 
| 109 | 
            -
                          if value.is_a?(GraphQL::ExecutionError)
         | 
| 110 | 
            -
                            value.ast_node = ast_arg
         | 
| 111 | 
            -
                            raise value
         | 
| 112 | 
            -
                          end
         | 
| 113 | 
            -
                        end
         | 
| 114 | 
            -
                        values_hash[arg_name] = value
         | 
| 115 | 
            -
                      end
         | 
| 116 | 
            -
                    end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                    # A Schema::InputObject, Schema::GraphQL::Field, Schema::Directive, logic from Interpreter::Arguments
         | 
| 119 | 
            -
                    ruby_kwargs = {}
         | 
| 120 | 
            -
                    values_hash.each do |key, value|
         | 
| 121 | 
            -
                      ruby_kwargs[Schema::Member::BuildType.underscore(key).to_sym] = value
         | 
| 122 | 
            -
                    end
         | 
| 123 | 
            -
                    if argument_owner.is_a?(Class) && argument_owner < GraphQL::Schema::InputObject
         | 
| 124 | 
            -
                      argument_owner.new(ruby_kwargs: ruby_kwargs, context: context, defaults_used: defaults_used)
         | 
| 125 | 
            -
                    else
         | 
| 126 | 
            -
                      ruby_kwargs
         | 
| 127 | 
            -
                    end
         | 
| 128 | 
            -
                  end
         | 
| 129 | 
            -
                end
         | 
| 130 | 
            -
              end
         | 
| 131 | 
            -
            end
         |