graphql 2.2.5 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/schema.erb +3 -0
- data/lib/graphql/analysis/ast/field_usage.rb +36 -9
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime.rb +9 -0
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +2 -3
- data/lib/graphql/language/lexer.rb +48 -30
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +25 -11
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +60 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +3 -3
- data/lib/graphql/schema/argument.rb +1 -0
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/input_object.rb +1 -2
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/resolver.rb +19 -10
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +119 -28
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +32 -6
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +2 -2
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -2
- metadata +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 566d6d5c49b331b3f38e2f4d338c635bae02a86b14a004ff55f1eccccb973181
         | 
| 4 | 
            +
              data.tar.gz: 0a8a048f8644e933252ca30459386e2049ac8c3093731709d344bf898f884731
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 12136553e963ed98012887215d1f21efd31c2a3bacd40ada6836ca3884680cb4ac6a8c7a1fabaa7fc8699c0ba6db9f20418ae068a74694daef8db1d13e506933
         | 
| 7 | 
            +
              data.tar.gz: 1faab9e89cf122660fb277df551ba026685cefeaa2fba9d83b590a5a18d4c4dc1202712ecf404c2998eb12c27a1219bd548944ef5f65daaad79371163ca66b43
         | 
| @@ -26,6 +26,9 @@ class <%= schema_name %> < GraphQL::Schema | |
| 26 26 | 
             
                raise(GraphQL::RequiredImplementationMissingError)
         | 
| 27 27 | 
             
              end
         | 
| 28 28 |  | 
| 29 | 
            +
              # Limit the size of incoming queries:
         | 
| 30 | 
            +
              max_query_string_tokens(5000)
         | 
| 31 | 
            +
             | 
| 29 32 | 
             
              # Stop validating when it encounters this many errors:
         | 
| 30 33 | 
             
              validate_max_errors(100)
         | 
| 31 34 | 
             
            end
         | 
| @@ -8,6 +8,7 @@ module GraphQL | |
| 8 8 | 
             
                      @used_fields = Set.new
         | 
| 9 9 | 
             
                      @used_deprecated_fields = Set.new
         | 
| 10 10 | 
             
                      @used_deprecated_arguments = Set.new
         | 
| 11 | 
            +
                      @used_deprecated_enum_values = Set.new
         | 
| 11 12 | 
             
                    end
         | 
| 12 13 |  | 
| 13 14 | 
             
                    def on_leave_field(node, parent, visitor)
         | 
| @@ -15,7 +16,7 @@ module GraphQL | |
| 15 16 | 
             
                      field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
         | 
| 16 17 | 
             
                      @used_fields << field
         | 
| 17 18 | 
             
                      @used_deprecated_fields << field if field_defn.deprecation_reason
         | 
| 18 | 
            -
                      arguments = visitor.query.arguments_for(node,  | 
| 19 | 
            +
                      arguments = visitor.query.arguments_for(node, field_defn)
         | 
| 19 20 | 
             
                      # If there was an error when preparing this argument object,
         | 
| 20 21 | 
             
                      # then this might be an error or something:
         | 
| 21 22 | 
             
                      if arguments.respond_to?(:argument_values)
         | 
| @@ -28,6 +29,7 @@ module GraphQL | |
| 28 29 | 
             
                        used_fields: @used_fields.to_a,
         | 
| 29 30 | 
             
                        used_deprecated_fields: @used_deprecated_fields.to_a,
         | 
| 30 31 | 
             
                        used_deprecated_arguments: @used_deprecated_arguments.to_a,
         | 
| 32 | 
            +
                        used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
         | 
| 31 33 | 
             
                      }
         | 
| 32 34 | 
             
                    end
         | 
| 33 35 |  | 
| @@ -39,18 +41,43 @@ module GraphQL | |
| 39 41 | 
             
                          @used_deprecated_arguments << argument.definition.path
         | 
| 40 42 | 
             
                        end
         | 
| 41 43 |  | 
| 42 | 
            -
                         | 
| 44 | 
            +
                        arg_val = argument.value
         | 
| 43 45 |  | 
| 44 | 
            -
                        if  | 
| 45 | 
            -
             | 
| 46 | 
            -
                         | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 46 | 
            +
                        next if arg_val.nil?
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                        argument_type = argument.definition.type
         | 
| 49 | 
            +
                        if argument_type.non_null?
         | 
| 50 | 
            +
                          argument_type = argument_type.of_type
         | 
| 51 | 
            +
                        end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                        if argument_type.kind.input_object?
         | 
| 54 | 
            +
                          extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
         | 
| 55 | 
            +
                        elsif argument_type.kind.enum?
         | 
| 56 | 
            +
                          extract_deprecated_enum_value(argument_type, arg_val)
         | 
| 57 | 
            +
                        elsif argument_type.list?
         | 
| 58 | 
            +
                          inner_type = argument_type.unwrap
         | 
| 59 | 
            +
                          case inner_type.kind
         | 
| 60 | 
            +
                          when TypeKinds::INPUT_OBJECT
         | 
| 61 | 
            +
                            argument.original_value.each do |value|
         | 
| 62 | 
            +
                              extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
         | 
| 63 | 
            +
                            end
         | 
| 64 | 
            +
                          when TypeKinds::ENUM
         | 
| 65 | 
            +
                            arg_val.each do |value|
         | 
| 66 | 
            +
                              extract_deprecated_enum_value(inner_type, value)
         | 
| 67 | 
            +
                            end
         | 
| 68 | 
            +
                          else
         | 
| 69 | 
            +
                            # Not a kind of input that we track
         | 
| 70 | 
            +
                          end
         | 
| 51 71 | 
             
                        end
         | 
| 52 72 | 
             
                      end
         | 
| 53 73 | 
             
                    end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    def extract_deprecated_enum_value(enum_type, value)
         | 
| 76 | 
            +
                      enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
         | 
| 77 | 
            +
                      if enum_value&.deprecation_reason
         | 
| 78 | 
            +
                        @used_deprecated_enum_values << enum_value.path
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 54 81 | 
             
                  end
         | 
| 55 82 | 
             
                end
         | 
| 56 83 | 
             
              end
         | 
| @@ -118,8 +118,12 @@ module GraphQL | |
| 118 118 | 
             
                    def on_inline_fragment(node, parent)
         | 
| 119 119 | 
             
                      on_fragment_with_type(node) do
         | 
| 120 120 | 
             
                        @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
         | 
| 121 | 
            +
                        @skipping = @skip_stack.last || skip?(node)
         | 
| 122 | 
            +
                        @skip_stack << @skipping
         | 
| 123 | 
            +
             | 
| 121 124 | 
             
                        call_on_enter_inline_fragment(node, parent)
         | 
| 122 125 | 
             
                        super
         | 
| 126 | 
            +
                        @skipping = @skip_stack.pop
         | 
| 123 127 | 
             
                        call_on_leave_inline_fragment(node, parent)
         | 
| 124 128 | 
             
                      end
         | 
| 125 129 | 
             
                    end
         | 
| @@ -187,9 +191,13 @@ module GraphQL | |
| 187 191 |  | 
| 188 192 | 
             
                    def on_fragment_spread(node, parent)
         | 
| 189 193 | 
             
                      @path.push("... #{node.name}")
         | 
| 194 | 
            +
                      @skipping = @skip_stack.last || skip?(node)
         | 
| 195 | 
            +
                      @skip_stack << @skipping
         | 
| 196 | 
            +
             | 
| 190 197 | 
             
                      call_on_enter_fragment_spread(node, parent)
         | 
| 191 198 | 
             
                      enter_fragment_spread_inline(node)
         | 
| 192 199 | 
             
                      super
         | 
| 200 | 
            +
                      @skipping = @skip_stack.pop
         | 
| 193 201 | 
             
                      leave_fragment_spread_inline(node)
         | 
| 194 202 | 
             
                      call_on_leave_fragment_spread(node, parent)
         | 
| 195 203 | 
             
                      @path.pop
         | 
    
        data/lib/graphql/analysis/ast.rb
    CHANGED
    
    | @@ -6,6 +6,7 @@ require "graphql/analysis/ast/query_complexity" | |
| 6 6 | 
             
            require "graphql/analysis/ast/max_query_complexity"
         | 
| 7 7 | 
             
            require "graphql/analysis/ast/query_depth"
         | 
| 8 8 | 
             
            require "graphql/analysis/ast/max_query_depth"
         | 
| 9 | 
            +
            require "timeout"
         | 
| 9 10 |  | 
| 10 11 | 
             
            module GraphQL
         | 
| 11 12 | 
             
              module Analysis
         | 
| @@ -63,7 +64,10 @@ module GraphQL | |
| 63 64 | 
             
                            analyzers: analyzers_to_run
         | 
| 64 65 | 
             
                          )
         | 
| 65 66 |  | 
| 66 | 
            -
                           | 
| 67 | 
            +
                          # `nil` or `0` causes no timeout
         | 
| 68 | 
            +
                          Timeout::timeout(query.validate_timeout_remaining) do
         | 
| 69 | 
            +
                            visitor.visit
         | 
| 70 | 
            +
                          end
         | 
| 67 71 |  | 
| 68 72 | 
             
                          if visitor.rescued_errors.any?
         | 
| 69 73 | 
             
                            return visitor.rescued_errors
         | 
| @@ -75,6 +79,11 @@ module GraphQL | |
| 75 79 | 
             
                        []
         | 
| 76 80 | 
             
                      end
         | 
| 77 81 | 
             
                    end
         | 
| 82 | 
            +
                  rescue Timeout::Error
         | 
| 83 | 
            +
                    [GraphQL::AnalysisError.new("Timeout on validation of query")]
         | 
| 84 | 
            +
                  rescue GraphQL::UnauthorizedError
         | 
| 85 | 
            +
                    # This error was raised during analysis and will be returned the client before execution
         | 
| 86 | 
            +
                    []
         | 
| 78 87 | 
             
                  end
         | 
| 79 88 |  | 
| 80 89 | 
             
                  def analysis_errors(results)
         | 
| @@ -16,12 +16,6 @@ module GraphQL | |
| 16 16 | 
             
                      "[" +
         | 
| 17 17 | 
             
                        obj.map { |v| inspect_truncated(v) }.join(", ") +
         | 
| 18 18 | 
             
                        "]"
         | 
| 19 | 
            -
                    when Query::Context::SharedMethods
         | 
| 20 | 
            -
                      if obj.invalid_null?
         | 
| 21 | 
            -
                        "nil"
         | 
| 22 | 
            -
                      else
         | 
| 23 | 
            -
                        inspect_truncated(obj.value)
         | 
| 24 | 
            -
                      end
         | 
| 25 19 | 
             
                    else
         | 
| 26 20 | 
             
                      inspect_truncated(obj)
         | 
| 27 21 | 
             
                    end
         | 
| @@ -33,12 +27,6 @@ module GraphQL | |
| 33 27 | 
             
                      "{...}"
         | 
| 34 28 | 
             
                    when Array
         | 
| 35 29 | 
             
                      "[...]"
         | 
| 36 | 
            -
                    when Query::Context::SharedMethods
         | 
| 37 | 
            -
                      if obj.invalid_null?
         | 
| 38 | 
            -
                        "nil"
         | 
| 39 | 
            -
                      else
         | 
| 40 | 
            -
                        inspect_truncated(obj.value)
         | 
| 41 | 
            -
                      end
         | 
| 42 30 | 
             
                    when GraphQL::Execution::Lazy
         | 
| 43 31 | 
             
                      "(unresolved)"
         | 
| 44 32 | 
             
                    else
         | 
| @@ -1,13 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 | 
             
            module GraphQL
         | 
| 3 | 
            -
              class CoercionError < GraphQL:: | 
| 4 | 
            -
                # @return [Hash] Optional custom data for error objects which will be added
         | 
| 5 | 
            -
                # under the `extensions` key.
         | 
| 6 | 
            -
                attr_accessor :extensions
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                def initialize(message, extensions: nil)
         | 
| 9 | 
            -
                  @extensions = extensions
         | 
| 10 | 
            -
                  super(message)
         | 
| 11 | 
            -
                end
         | 
| 3 | 
            +
              class CoercionError < GraphQL::ExecutionError
         | 
| 12 4 | 
             
              end
         | 
| 13 5 | 
             
            end
         | 
| @@ -14,6 +14,11 @@ module GraphQL | |
| 14 14 | 
             
                  def load
         | 
| 15 15 | 
             
                    @source.load(@key)
         | 
| 16 16 | 
             
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def load_with_deprecation_warning
         | 
| 19 | 
            +
                    warn("Returning `.request(...)` from GraphQL::Dataloader is deprecated, use `.load(...)` instead. (See usage of #{@source} with #{@key.inspect}).")
         | 
| 20 | 
            +
                    load
         | 
| 21 | 
            +
                  end
         | 
| 17 22 | 
             
                end
         | 
| 18 23 | 
             
              end
         | 
| 19 24 | 
             
            end
         | 
| @@ -6,15 +6,19 @@ module GraphQL | |
| 6 6 | 
             
                  # A container for metadata regarding arguments present in a GraphQL query.
         | 
| 7 7 | 
             
                  # @see Interpreter::Arguments#argument_values for a hash of these objects.
         | 
| 8 8 | 
             
                  class ArgumentValue
         | 
| 9 | 
            -
                    def initialize(definition:, value:, default_used:)
         | 
| 9 | 
            +
                    def initialize(definition:, value:, original_value:, default_used:)
         | 
| 10 10 | 
             
                      @definition = definition
         | 
| 11 11 | 
             
                      @value = value
         | 
| 12 | 
            +
                      @original_value = original_value
         | 
| 12 13 | 
             
                      @default_used = default_used
         | 
| 13 14 | 
             
                    end
         | 
| 14 15 |  | 
| 15 16 | 
             
                    # @return [Object] The Ruby-ready value for this Argument
         | 
| 16 17 | 
             
                    attr_reader :value
         | 
| 17 18 |  | 
| 19 | 
            +
                    # @return [Object] The value of this argument _before_ `prepare` is applied.
         | 
| 20 | 
            +
                    attr_reader :original_value
         | 
| 21 | 
            +
             | 
| 18 22 | 
             
                    # @return [GraphQL::Schema::Argument] The definition instance for this argument
         | 
| 19 23 | 
             
                    attr_reader :definition
         | 
| 20 24 |  | 
| @@ -352,6 +352,15 @@ module GraphQL | |
| 352 352 | 
             
                      end
         | 
| 353 353 |  | 
| 354 354 | 
             
                      field_result = call_method_on_directives(:resolve, object, directives) do
         | 
| 355 | 
            +
                        if directives.any?
         | 
| 356 | 
            +
                          # This might be executed in a different context; reset this info
         | 
| 357 | 
            +
                          runtime_state = get_current_runtime_state
         | 
| 358 | 
            +
                          runtime_state.current_field = field_defn
         | 
| 359 | 
            +
                          runtime_state.current_object = object
         | 
| 360 | 
            +
                          runtime_state.current_arguments = resolved_arguments
         | 
| 361 | 
            +
                          runtime_state.current_result_name = result_name
         | 
| 362 | 
            +
                          runtime_state.current_result = selection_result
         | 
| 363 | 
            +
                        end
         | 
| 355 364 | 
             
                        # Actually call the field resolver and capture the result
         | 
| 356 365 | 
             
                        app_result = begin
         | 
| 357 366 | 
             
                          @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
         | 
| @@ -37,173 +37,113 @@ module GraphQL | |
| 37 37 | 
             
                      multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
         | 
| 38 38 | 
             
                        schema = multiplex.schema
         | 
| 39 39 | 
             
                        queries = multiplex.queries
         | 
| 40 | 
            -
                        query_instrumenters = schema.instrumenters[:query]
         | 
| 41 | 
            -
                        multiplex_instrumenters = schema.instrumenters[:multiplex]
         | 
| 42 40 | 
             
                        lazies_at_depth = Hash.new { |h, k| h[k] = [] }
         | 
| 41 | 
            +
                        multiplex_analyzers = schema.multiplex_analyzers
         | 
| 42 | 
            +
                        if multiplex.max_complexity
         | 
| 43 | 
            +
                          multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
         | 
| 44 | 
            +
                        end
         | 
| 43 45 |  | 
| 44 | 
            -
                         | 
| 45 | 
            -
                         | 
| 46 | 
            -
                           | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 46 | 
            +
                        schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
         | 
| 47 | 
            +
                        begin
         | 
| 48 | 
            +
                          # Since this is basically the batching context,
         | 
| 49 | 
            +
                          # share it for a whole multiplex
         | 
| 50 | 
            +
                          multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy(deprecation_warning: false).new
         | 
| 51 | 
            +
                          # Do as much eager evaluation of the query as possible
         | 
| 52 | 
            +
                          results = []
         | 
| 53 | 
            +
                          queries.each_with_index do |query, idx|
         | 
| 54 | 
            +
                            if query.subscription? && !query.subscription_update?
         | 
| 55 | 
            +
                              query.context.namespace(:subscriptions)[:events] = []
         | 
| 52 56 | 
             
                            end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
                               | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                                   | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
                                    begin
         | 
| 68 | 
            -
                                      # Although queries in a multiplex _share_ an Interpreter instance,
         | 
| 69 | 
            -
                                      # they also have another item of state, which is private to that query
         | 
| 70 | 
            -
                                      # in particular, assign it here:
         | 
| 71 | 
            -
                                      runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
         | 
| 72 | 
            -
                                      query.context.namespace(:interpreter_runtime)[:runtime] = runtime
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                                      query.current_trace.execute_query(query: query) do
         | 
| 75 | 
            -
                                        runtime.run_eager
         | 
| 76 | 
            -
                                      end
         | 
| 77 | 
            -
                                    rescue GraphQL::ExecutionError => err
         | 
| 78 | 
            -
                                      query.context.errors << err
         | 
| 79 | 
            -
                                      NO_OPERATION
         | 
| 80 | 
            -
                                    end
         | 
| 57 | 
            +
                            multiplex.dataloader.append_job {
         | 
| 58 | 
            +
                              operation = query.selected_operation
         | 
| 59 | 
            +
                              result = if operation.nil? || !query.valid? || query.context.errors.any?
         | 
| 60 | 
            +
                                NO_OPERATION
         | 
| 61 | 
            +
                              else
         | 
| 62 | 
            +
                                begin
         | 
| 63 | 
            +
                                  # Although queries in a multiplex _share_ an Interpreter instance,
         | 
| 64 | 
            +
                                  # they also have another item of state, which is private to that query
         | 
| 65 | 
            +
                                  # in particular, assign it here:
         | 
| 66 | 
            +
                                  runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
         | 
| 67 | 
            +
                                  query.context.namespace(:interpreter_runtime)[:runtime] = runtime
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                                  query.current_trace.execute_query(query: query) do
         | 
| 70 | 
            +
                                    runtime.run_eager
         | 
| 81 71 | 
             
                                  end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 72 | 
            +
                                rescue GraphQL::ExecutionError => err
         | 
| 73 | 
            +
                                  query.context.errors << err
         | 
| 74 | 
            +
                                  NO_OPERATION
         | 
| 75 | 
            +
                                end
         | 
| 84 76 | 
             
                              end
         | 
| 77 | 
            +
                              results[idx] = result
         | 
| 78 | 
            +
                            }
         | 
| 79 | 
            +
                          end
         | 
| 85 80 |  | 
| 86 | 
            -
             | 
| 81 | 
            +
                          multiplex.dataloader.run
         | 
| 87 82 |  | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                              # Then, find all errors and assign the result to the query object
         | 
| 105 | 
            -
                              results.each_with_index do |data_result, idx|
         | 
| 106 | 
            -
                                query = queries[idx]
         | 
| 107 | 
            -
                                # Assign the result so that it can be accessed in instrumentation
         | 
| 108 | 
            -
                                query.result_values = if data_result.equal?(NO_OPERATION)
         | 
| 109 | 
            -
                                  if !query.valid? || query.context.errors.any?
         | 
| 110 | 
            -
                                    # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
         | 
| 111 | 
            -
                                    { "errors" => query.static_errors.map(&:to_h) }
         | 
| 112 | 
            -
                                  else
         | 
| 113 | 
            -
                                    data_result
         | 
| 114 | 
            -
                                  end
         | 
| 115 | 
            -
                                else
         | 
| 116 | 
            -
                                  result = {
         | 
| 117 | 
            -
                                    "data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
         | 
| 118 | 
            -
                                  }
         | 
| 83 | 
            +
                          # Then, work through lazy results in a breadth-first way
         | 
| 84 | 
            +
                          multiplex.dataloader.append_job {
         | 
| 85 | 
            +
                            query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
         | 
| 86 | 
            +
                            queries = multiplex ? multiplex.queries : [query]
         | 
| 87 | 
            +
                            final_values = queries.map do |query|
         | 
| 88 | 
            +
                              runtime = query.context.namespace(:interpreter_runtime)[:runtime]
         | 
| 89 | 
            +
                              # it might not be present if the query has an error
         | 
| 90 | 
            +
                              runtime ? runtime.final_result : nil
         | 
| 91 | 
            +
                            end
         | 
| 92 | 
            +
                            final_values.compact!
         | 
| 93 | 
            +
                            multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
         | 
| 94 | 
            +
                              Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
         | 
| 95 | 
            +
                            end
         | 
| 96 | 
            +
                          }
         | 
| 97 | 
            +
                          multiplex.dataloader.run
         | 
| 119 98 |  | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 99 | 
            +
                          # Then, find all errors and assign the result to the query object
         | 
| 100 | 
            +
                          results.each_with_index do |data_result, idx|
         | 
| 101 | 
            +
                            query = queries[idx]
         | 
| 102 | 
            +
                            if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
         | 
| 103 | 
            +
                              schema.subscriptions.write_subscription(query, events)
         | 
| 104 | 
            +
                            end
         | 
| 105 | 
            +
                            # Assign the result so that it can be accessed in instrumentation
         | 
| 106 | 
            +
                            query.result_values = if data_result.equal?(NO_OPERATION)
         | 
| 107 | 
            +
                              if !query.valid? || query.context.errors.any?
         | 
| 108 | 
            +
                                # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
         | 
| 109 | 
            +
                                { "errors" => query.static_errors.map(&:to_h) }
         | 
| 110 | 
            +
                              else
         | 
| 111 | 
            +
                                data_result
         | 
| 112 | 
            +
                              end
         | 
| 113 | 
            +
                            else
         | 
| 114 | 
            +
                              result = {}
         | 
| 124 115 |  | 
| 125 | 
            -
             | 
| 126 | 
            -
                                 | 
| 127 | 
            -
                                 | 
| 128 | 
            -
                                  query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
         | 
| 129 | 
            -
                                end
         | 
| 130 | 
            -
                                # Get the Query::Result, not the Hash
         | 
| 131 | 
            -
                                results[idx] = query.result
         | 
| 116 | 
            +
                              if query.context.errors.any?
         | 
| 117 | 
            +
                                error_result = query.context.errors.map(&:to_h)
         | 
| 118 | 
            +
                                result["errors"] = error_result
         | 
| 132 119 | 
             
                              end
         | 
| 133 120 |  | 
| 134 | 
            -
                               | 
| 135 | 
            -
             | 
| 136 | 
            -
                               | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
                               | 
| 140 | 
            -
                            ensure
         | 
| 141 | 
            -
                              queries.map { |query|
         | 
| 142 | 
            -
                                runtime = query.context.namespace(:interpreter_runtime)[:runtime]
         | 
| 143 | 
            -
                                if runtime
         | 
| 144 | 
            -
                                  runtime.delete_all_interpreter_context
         | 
| 145 | 
            -
                                end
         | 
| 146 | 
            -
                              }
         | 
| 121 | 
            +
                              result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                              result
         | 
| 124 | 
            +
                            end
         | 
| 125 | 
            +
                            if query.context.namespace?(:__query_result_extensions__)
         | 
| 126 | 
            +
                              query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
         | 
| 147 127 | 
             
                            end
         | 
| 128 | 
            +
                            # Get the Query::Result, not the Hash
         | 
| 129 | 
            +
                            results[idx] = query.result
         | 
| 148 130 | 
             
                          end
         | 
| 149 | 
            -
                        end
         | 
| 150 | 
            -
                      end
         | 
| 151 | 
            -
                    end
         | 
| 152 131 |  | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
                            yield
         | 
| 132 | 
            +
                          results
         | 
| 133 | 
            +
                        rescue Exception
         | 
| 134 | 
            +
                          # TODO rescue at a higher level so it will catch errors in analysis, too
         | 
| 135 | 
            +
                          # Assign values here so that the query's `@executed` becomes true
         | 
| 136 | 
            +
                          queries.map { |q| q.result_values ||= {} }
         | 
| 137 | 
            +
                          raise
         | 
| 138 | 
            +
                        ensure
         | 
| 139 | 
            +
                          queries.map { |query|
         | 
| 140 | 
            +
                            runtime = query.context.namespace(:interpreter_runtime)[:runtime]
         | 
| 141 | 
            +
                            if runtime
         | 
| 142 | 
            +
                              runtime.delete_all_interpreter_context
         | 
| 143 | 
            +
                            end
         | 
| 166 144 | 
             
                          }
         | 
| 167 | 
            -
                        }
         | 
| 168 | 
            -
                      end
         | 
| 169 | 
            -
                    end
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                    # Call each before hook, and if they all succeed, yield.
         | 
| 172 | 
            -
                    # If they don't all succeed, call after_ for each one that succeeded.
         | 
| 173 | 
            -
                    def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
         | 
| 174 | 
            -
                      begin
         | 
| 175 | 
            -
                        successful = []
         | 
| 176 | 
            -
                        instrumenters.each do |instrumenter|
         | 
| 177 | 
            -
                          instrumenter.public_send(before_hook_name, object)
         | 
| 178 | 
            -
                          successful << instrumenter
         | 
| 179 | 
            -
                        end
         | 
| 180 | 
            -
             | 
| 181 | 
            -
                        # if any before hooks raise an exception, quit calling before hooks,
         | 
| 182 | 
            -
                        # but call the after hooks on anything that succeeded but also
         | 
| 183 | 
            -
                        # raise the exception that came from the before hook.
         | 
| 184 | 
            -
                      rescue GraphQL::ExecutionError => err
         | 
| 185 | 
            -
                        object.context.errors << err
         | 
| 186 | 
            -
                      rescue => e
         | 
| 187 | 
            -
                        raise call_after_hooks(successful, object, after_hook_name, e)
         | 
| 188 | 
            -
                      end
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                      begin
         | 
| 191 | 
            -
                        yield # Call the user code
         | 
| 192 | 
            -
                      ensure
         | 
| 193 | 
            -
                        ex = call_after_hooks(successful, object, after_hook_name, nil)
         | 
| 194 | 
            -
                        raise ex if ex
         | 
| 195 | 
            -
                      end
         | 
| 196 | 
            -
                    end
         | 
| 197 | 
            -
             | 
| 198 | 
            -
                    def call_after_hooks(instrumenters, object, after_hook_name, ex)
         | 
| 199 | 
            -
                      instrumenters.reverse_each do |instrumenter|
         | 
| 200 | 
            -
                        begin
         | 
| 201 | 
            -
                          instrumenter.public_send(after_hook_name, object)
         | 
| 202 | 
            -
                        rescue => e
         | 
| 203 | 
            -
                          ex = e
         | 
| 204 145 | 
             
                        end
         | 
| 205 146 | 
             
                      end
         | 
| 206 | 
            -
                      ex
         | 
| 207 147 | 
             
                    end
         | 
| 208 148 | 
             
                  end
         | 
| 209 149 |  | 
| @@ -9,13 +9,19 @@ module GraphQL | |
| 9 9 |  | 
| 10 10 | 
             
                  def __schema
         | 
| 11 11 | 
             
                    # Apply wrapping manually since this field isn't wrapped by instrumentation
         | 
| 12 | 
            -
                    schema =  | 
| 12 | 
            +
                    schema = context.schema
         | 
| 13 13 | 
             
                    schema_type = schema.introspection_system.types["__Schema"]
         | 
| 14 | 
            -
                    schema_type.wrap(schema,  | 
| 14 | 
            +
                    schema_type.wrap(schema, context)
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def __type(name:)
         | 
| 18 | 
            -
                    context.warden.reachable_type?(name) | 
| 18 | 
            +
                    if context.warden.reachable_type?(name)
         | 
| 19 | 
            +
                      context.warden.get_type(name)
         | 
| 20 | 
            +
                    elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
         | 
| 21 | 
            +
                      type
         | 
| 22 | 
            +
                    else
         | 
| 23 | 
            +
                      nil
         | 
| 24 | 
            +
                    end
         | 
| 19 25 | 
             
                  end
         | 
| 20 26 | 
             
                end
         | 
| 21 27 | 
             
              end
         | 
| @@ -24,7 +24,7 @@ module GraphQL | |
| 24 24 | 
             
                    @include_built_in_directives = include_built_in_directives
         | 
| 25 25 | 
             
                    @include_one_of = false
         | 
| 26 26 |  | 
| 27 | 
            -
                    schema_context = schema.context_class.new(query: nil,  | 
| 27 | 
            +
                    schema_context = schema.context_class.new(query: nil, schema: schema, values: context)
         | 
| 28 28 |  | 
| 29 29 |  | 
| 30 30 | 
             
                    @warden = @schema.warden_class.new(
         | 
| @@ -266,8 +266,7 @@ module GraphQL | |
| 266 266 | 
             
                    end
         | 
| 267 267 | 
             
                    definitions = build_directive_nodes(dirs_to_build)
         | 
| 268 268 |  | 
| 269 | 
            -
                    type_nodes = build_type_definition_nodes(warden.reachable_types)
         | 
| 270 | 
            -
             | 
| 269 | 
            +
                    type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
         | 
| 271 270 | 
             
                    if @include_one_of
         | 
| 272 271 | 
             
                      # This may have been set to true when iterating over all types
         | 
| 273 272 | 
             
                      definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
         |