graphql 2.0.13 → 2.0.14
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/dataloader/source.rb +9 -0
 - data/lib/graphql/execution/interpreter.rb +185 -59
 - data/lib/graphql/execution/lookahead.rb +26 -26
 - data/lib/graphql/execution/multiplex.rb +1 -116
 - data/lib/graphql/execution.rb +0 -1
 - data/lib/graphql/introspection/type_type.rb +7 -0
 - data/lib/graphql/introspection.rb +2 -1
 - data/lib/graphql/query.rb +1 -1
 - data/lib/graphql/schema/build_from_definition.rb +1 -2
 - data/lib/graphql/schema/directive/one_of.rb +12 -0
 - data/lib/graphql/schema/input_object.rb +35 -0
 - data/lib/graphql/schema/late_bound_type.rb +4 -0
 - data/lib/graphql/schema.rb +3 -1
 - data/lib/graphql/static_validation/all_rules.rb +1 -0
 - data/lib/graphql/static_validation/literal_validator.rb +4 -0
 - data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
 - data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
 - data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
 - data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
 - data/lib/graphql/version.rb +1 -1
 - metadata +6 -4
 - data/lib/graphql/execution/instrumentation.rb +0 -92
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: a3bc6610f88b6689ee6991a6ad3543580f9bdf1b3cb9f2f7e3f78862d76072f7
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: ce52505d6c43e330aa8e5def38719f62bdfe067274a86530c889350c64994bd0
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8f3aac93aa1013c257c84521b54285719d6edb8c212a83140123902a06f3cffabc1005d2a176b723cff5677614eab7ae1617b71ce7eb403e721fcbafbe07c1e1
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a84bf37728b5c0ff8795070fd682ac7af83e8888d47cadff5103a76ee333788c30bf78f505528fee4f868288b99dfc11d82f7c604710e13b4a7d9690eb2efe36
         
     | 
| 
         @@ -23,5 +23,8 @@ class <%= schema_name %> < GraphQL::Schema 
     | 
|
| 
       23 
23 
     | 
    
         
             
                # to return the correct GraphQL object type for `obj`
         
     | 
| 
       24 
24 
     | 
    
         
             
                raise(GraphQL::RequiredImplementationMissingError)
         
     | 
| 
       25 
25 
     | 
    
         
             
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              # Stop validating when it encounters this many errors:
         
     | 
| 
      
 28 
     | 
    
         
            +
              validate_max_errors(100)
         
     | 
| 
       26 
29 
     | 
    
         
             
            end
         
     | 
| 
       27 
30 
     | 
    
         
             
            <% end -%>
         
     | 
| 
         @@ -86,6 +86,15 @@ module GraphQL 
     | 
|
| 
       86 
86 
     | 
    
         
             
                    !@pending_keys.empty?
         
     | 
| 
       87 
87 
     | 
    
         
             
                  end
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
      
 89 
     | 
    
         
            +
                  # Add these key-value pairs to this source's cache
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # (future loads will use these merged values).
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # @param results [Hash<Object => Object>] key-value pairs to cache in this source
         
     | 
| 
      
 92 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 93 
     | 
    
         
            +
                  def merge(results)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @results.merge!(results)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
       89 
98 
     | 
    
         
             
                  # Called by {GraphQL::Dataloader} to resolve and pending requests to this source.
         
     | 
| 
       90 
99 
     | 
    
         
             
                  # @api private
         
     | 
| 
       91 
100 
     | 
    
         
             
                  # @return [void]
         
     | 
| 
         @@ -11,76 +11,202 @@ require "graphql/execution/interpreter/handles_raw_value" 
     | 
|
| 
       11 
11 
     | 
    
         
             
            module GraphQL
         
     | 
| 
       12 
12 
     | 
    
         
             
              module Execution
         
     | 
| 
       13 
13 
     | 
    
         
             
                class Interpreter
         
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
       15 
     | 
    
         
            -
                    #  
     | 
| 
       16 
     | 
    
         
            -
                    #  
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # Used internally to signal that the query shouldn't be executed
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 17 
     | 
    
         
            +
                    NO_OPERATION = {}.freeze
         
     | 
| 
       19 
18 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                    #  
     | 
| 
       22 
     | 
    
         
            -
                    #  
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
                    # @param schema [GraphQL::Schema]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    # @param queries [Array<GraphQL::Query, Hash>]
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # @param context [Hash]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # @param max_complexity [Integer, nil]
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # @return [Array<Hash>] One result per query
         
     | 
| 
      
 24 
     | 
    
         
            +
                    def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      queries = query_options.map do |opts|
         
     | 
| 
      
 26 
     | 
    
         
            +
                        case opts
         
     | 
| 
      
 27 
     | 
    
         
            +
                        when Hash
         
     | 
| 
      
 28 
     | 
    
         
            +
                          GraphQL::Query.new(schema, nil, **opts)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        when GraphQL::Query
         
     | 
| 
      
 30 
     | 
    
         
            +
                          opts
         
     | 
| 
      
 31 
     | 
    
         
            +
                        else
         
     | 
| 
      
 32 
     | 
    
         
            +
                          raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
         
     | 
| 
      
 33 
     | 
    
         
            +
                        end
         
     | 
| 
      
 34 
     | 
    
         
            +
                      end
         
     | 
| 
       29 
35 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
                      multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      multiplex.trace("execute_multiplex", { multiplex: multiplex }) do
         
     | 
| 
      
 38 
     | 
    
         
            +
                        schema = multiplex.schema
         
     | 
| 
      
 39 
     | 
    
         
            +
                        queries = multiplex.queries
         
     | 
| 
      
 40 
     | 
    
         
            +
                        query_instrumenters = schema.instrumenters[:query]
         
     | 
| 
      
 41 
     | 
    
         
            +
                        multiplex_instrumenters = schema.instrumenters[:multiplex]
         
     | 
| 
       34 
42 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
                        # First, run multiplex instrumentation, then query instrumentation for each query
         
     | 
| 
      
 44 
     | 
    
         
            +
                        call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
         
     | 
| 
      
 45 
     | 
    
         
            +
                          each_query_call_hooks(query_instrumenters, queries) do
         
     | 
| 
      
 46 
     | 
    
         
            +
                            schema = multiplex.schema
         
     | 
| 
      
 47 
     | 
    
         
            +
                            multiplex_analyzers = schema.multiplex_analyzers
         
     | 
| 
      
 48 
     | 
    
         
            +
                            queries = multiplex.queries
         
     | 
| 
      
 49 
     | 
    
         
            +
                            if multiplex.max_complexity
         
     | 
| 
      
 50 
     | 
    
         
            +
                              multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
         
     | 
| 
      
 51 
     | 
    
         
            +
                            end
         
     | 
| 
       40 
52 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 53 
     | 
    
         
            +
                            schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
         
     | 
| 
      
 54 
     | 
    
         
            +
                            begin
         
     | 
| 
      
 55 
     | 
    
         
            +
                              # Since this is basically the batching context,
         
     | 
| 
      
 56 
     | 
    
         
            +
                              # share it for a whole multiplex
         
     | 
| 
      
 57 
     | 
    
         
            +
                              multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy.new
         
     | 
| 
      
 58 
     | 
    
         
            +
                              # Do as much eager evaluation of the query as possible
         
     | 
| 
      
 59 
     | 
    
         
            +
                              results = []
         
     | 
| 
      
 60 
     | 
    
         
            +
                              queries.each_with_index do |query, idx|
         
     | 
| 
      
 61 
     | 
    
         
            +
                                multiplex.dataloader.append_job {
         
     | 
| 
      
 62 
     | 
    
         
            +
                                  operation = query.selected_operation
         
     | 
| 
      
 63 
     | 
    
         
            +
                                  result = if operation.nil? || !query.valid? || query.context.errors.any?
         
     | 
| 
      
 64 
     | 
    
         
            +
                                    NO_OPERATION
         
     | 
| 
      
 65 
     | 
    
         
            +
                                  else
         
     | 
| 
      
 66 
     | 
    
         
            +
                                    begin
         
     | 
| 
      
 67 
     | 
    
         
            +
                                      # Although queries in a multiplex _share_ an Interpreter instance,
         
     | 
| 
      
 68 
     | 
    
         
            +
                                      # they also have another item of state, which is private to that query
         
     | 
| 
      
 69 
     | 
    
         
            +
                                      # in particular, assign it here:
         
     | 
| 
      
 70 
     | 
    
         
            +
                                      runtime = Runtime.new(query: query)
         
     | 
| 
      
 71 
     | 
    
         
            +
                                      query.context.namespace(:interpreter)[:runtime] = runtime
         
     | 
| 
       53 
72 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
      
 73 
     | 
    
         
            +
                                      query.trace("execute_query", {query: query}) do
         
     | 
| 
      
 74 
     | 
    
         
            +
                                        runtime.run_eager
         
     | 
| 
      
 75 
     | 
    
         
            +
                                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
                                    rescue GraphQL::ExecutionError => err
         
     | 
| 
      
 77 
     | 
    
         
            +
                                      query.context.errors << err
         
     | 
| 
      
 78 
     | 
    
         
            +
                                      NO_OPERATION
         
     | 
| 
      
 79 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                                  results[idx] = result
         
     | 
| 
      
 82 
     | 
    
         
            +
                                }
         
     | 
| 
      
 83 
     | 
    
         
            +
                              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                              multiplex.dataloader.run
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                              # Then, work through lazy results in a breadth-first way
         
     | 
| 
      
 88 
     | 
    
         
            +
                              multiplex.dataloader.append_job {
         
     | 
| 
      
 89 
     | 
    
         
            +
                                tracer = multiplex
         
     | 
| 
      
 90 
     | 
    
         
            +
                                query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                                queries = multiplex ? multiplex.queries : [query]
         
     | 
| 
      
 92 
     | 
    
         
            +
                                final_values = queries.map do |query|
         
     | 
| 
      
 93 
     | 
    
         
            +
                                  runtime = query.context.namespace(:interpreter)[:runtime]
         
     | 
| 
      
 94 
     | 
    
         
            +
                                  # it might not be present if the query has an error
         
     | 
| 
      
 95 
     | 
    
         
            +
                                  runtime ? runtime.final_result : nil
         
     | 
| 
      
 96 
     | 
    
         
            +
                                end
         
     | 
| 
      
 97 
     | 
    
         
            +
                                final_values.compact!
         
     | 
| 
      
 98 
     | 
    
         
            +
                                tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
         
     | 
| 
      
 99 
     | 
    
         
            +
                                  Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
         
     | 
| 
      
 100 
     | 
    
         
            +
                                end
         
     | 
| 
      
 101 
     | 
    
         
            +
                                queries.each do |query|
         
     | 
| 
      
 102 
     | 
    
         
            +
                                  runtime = query.context.namespace(:interpreter)[:runtime]
         
     | 
| 
      
 103 
     | 
    
         
            +
                                  if runtime
         
     | 
| 
      
 104 
     | 
    
         
            +
                                    runtime.delete_interpreter_context(:current_path)
         
     | 
| 
      
 105 
     | 
    
         
            +
                                    runtime.delete_interpreter_context(:current_field)
         
     | 
| 
      
 106 
     | 
    
         
            +
                                    runtime.delete_interpreter_context(:current_object)
         
     | 
| 
      
 107 
     | 
    
         
            +
                                    runtime.delete_interpreter_context(:current_arguments)
         
     | 
| 
      
 108 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                                end
         
     | 
| 
      
 110 
     | 
    
         
            +
                              }
         
     | 
| 
      
 111 
     | 
    
         
            +
                              multiplex.dataloader.run
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                              # Then, find all errors and assign the result to the query object
         
     | 
| 
      
 114 
     | 
    
         
            +
                              results.each_with_index do |data_result, idx|
         
     | 
| 
      
 115 
     | 
    
         
            +
                                query = queries[idx]
         
     | 
| 
      
 116 
     | 
    
         
            +
                                # Assign the result so that it can be accessed in instrumentation
         
     | 
| 
      
 117 
     | 
    
         
            +
                                query.result_values = if data_result.equal?(NO_OPERATION)
         
     | 
| 
      
 118 
     | 
    
         
            +
                                  if !query.valid? || query.context.errors.any?
         
     | 
| 
      
 119 
     | 
    
         
            +
                                    # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
         
     | 
| 
      
 120 
     | 
    
         
            +
                                    { "errors" => query.static_errors.map(&:to_h) }
         
     | 
| 
      
 121 
     | 
    
         
            +
                                  else
         
     | 
| 
      
 122 
     | 
    
         
            +
                                    data_result
         
     | 
| 
      
 123 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
                                else
         
     | 
| 
      
 125 
     | 
    
         
            +
                                  result = {
         
     | 
| 
      
 126 
     | 
    
         
            +
                                    "data" => query.context.namespace(:interpreter)[:runtime].final_result
         
     | 
| 
      
 127 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                                  if query.context.errors.any?
         
     | 
| 
      
 130 
     | 
    
         
            +
                                    error_result = query.context.errors.map(&:to_h)
         
     | 
| 
      
 131 
     | 
    
         
            +
                                    result["errors"] = error_result
         
     | 
| 
      
 132 
     | 
    
         
            +
                                  end
         
     | 
| 
       56 
133 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
      
 134 
     | 
    
         
            +
                                  result
         
     | 
| 
      
 135 
     | 
    
         
            +
                                end
         
     | 
| 
      
 136 
     | 
    
         
            +
                                if query.context.namespace?(:__query_result_extensions__)
         
     | 
| 
      
 137 
     | 
    
         
            +
                                  query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
         
     | 
| 
      
 138 
     | 
    
         
            +
                                end
         
     | 
| 
      
 139 
     | 
    
         
            +
                                # Get the Query::Result, not the Hash
         
     | 
| 
      
 140 
     | 
    
         
            +
                                results[idx] = query.result
         
     | 
| 
      
 141 
     | 
    
         
            +
                              end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                              results
         
     | 
| 
      
 144 
     | 
    
         
            +
                            rescue Exception
         
     | 
| 
      
 145 
     | 
    
         
            +
                              # TODO rescue at a higher level so it will catch errors in analysis, too
         
     | 
| 
      
 146 
     | 
    
         
            +
                              # Assign values here so that the query's `@executed` becomes true
         
     | 
| 
      
 147 
     | 
    
         
            +
                              queries.map { |q| q.result_values ||= {} }
         
     | 
| 
      
 148 
     | 
    
         
            +
                              raise
         
     | 
| 
      
 149 
     | 
    
         
            +
                            end
         
     | 
| 
      
 150 
     | 
    
         
            +
                          end
         
     | 
| 
      
 151 
     | 
    
         
            +
                        end
         
     | 
| 
      
 152 
     | 
    
         
            +
                      end
         
     | 
| 
       63 
153 
     | 
    
         
             
                    end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                     
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    private
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                    # Call the before_ hooks of each query,
         
     | 
| 
      
 158 
     | 
    
         
            +
                    # Then yield if no errors.
         
     | 
| 
      
 159 
     | 
    
         
            +
                    # `call_hooks` takes care of appropriate cleanup.
         
     | 
| 
      
 160 
     | 
    
         
            +
                    def each_query_call_hooks(instrumenters, queries, i = 0)
         
     | 
| 
      
 161 
     | 
    
         
            +
                      if i >= queries.length
         
     | 
| 
      
 162 
     | 
    
         
            +
                        yield
         
     | 
| 
      
 163 
     | 
    
         
            +
                      else
         
     | 
| 
      
 164 
     | 
    
         
            +
                        query = queries[i]
         
     | 
| 
      
 165 
     | 
    
         
            +
                        call_hooks(instrumenters, query, :before_query, :after_query) {
         
     | 
| 
      
 166 
     | 
    
         
            +
                          each_query_call_hooks(instrumenters, queries, i + 1) {
         
     | 
| 
      
 167 
     | 
    
         
            +
                            yield
         
     | 
| 
      
 168 
     | 
    
         
            +
                          }
         
     | 
| 
      
 169 
     | 
    
         
            +
                        }
         
     | 
| 
      
 170 
     | 
    
         
            +
                      end
         
     | 
| 
       69 
171 
     | 
    
         
             
                    end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                     
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                    # Call each before hook, and if they all succeed, yield.
         
     | 
| 
      
 174 
     | 
    
         
            +
                    # If they don't all succeed, call after_ for each one that succeeded.
         
     | 
| 
      
 175 
     | 
    
         
            +
                    def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 177 
     | 
    
         
            +
                        successful = []
         
     | 
| 
      
 178 
     | 
    
         
            +
                        instrumenters.each do |instrumenter|
         
     | 
| 
      
 179 
     | 
    
         
            +
                          instrumenter.public_send(before_hook_name, object)
         
     | 
| 
      
 180 
     | 
    
         
            +
                          successful << instrumenter
         
     | 
| 
      
 181 
     | 
    
         
            +
                        end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                        # if any before hooks raise an exception, quit calling before hooks,
         
     | 
| 
      
 184 
     | 
    
         
            +
                        # but call the after hooks on anything that succeeded but also
         
     | 
| 
      
 185 
     | 
    
         
            +
                        # raise the exception that came from the before hook.
         
     | 
| 
      
 186 
     | 
    
         
            +
                      rescue GraphQL::ExecutionError => err
         
     | 
| 
      
 187 
     | 
    
         
            +
                        object.context.errors << err
         
     | 
| 
      
 188 
     | 
    
         
            +
                      rescue => e
         
     | 
| 
      
 189 
     | 
    
         
            +
                        raise call_after_hooks(successful, object, after_hook_name, e)
         
     | 
| 
      
 190 
     | 
    
         
            +
                      end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 193 
     | 
    
         
            +
                        yield # Call the user code
         
     | 
| 
      
 194 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 195 
     | 
    
         
            +
                        ex = call_after_hooks(successful, object, after_hook_name, nil)
         
     | 
| 
      
 196 
     | 
    
         
            +
                        raise ex if ex
         
     | 
| 
      
 197 
     | 
    
         
            +
                      end
         
     | 
| 
       73 
198 
     | 
    
         
             
                    end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                       
     | 
| 
       77 
     | 
    
         
            -
                         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                    def call_after_hooks(instrumenters, object, after_hook_name, ex)
         
     | 
| 
      
 201 
     | 
    
         
            +
                      instrumenters.reverse_each do |instrumenter|
         
     | 
| 
      
 202 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 203 
     | 
    
         
            +
                          instrumenter.public_send(after_hook_name, object)
         
     | 
| 
      
 204 
     | 
    
         
            +
                        rescue => e
         
     | 
| 
      
 205 
     | 
    
         
            +
                          ex = e
         
     | 
| 
      
 206 
     | 
    
         
            +
                        end
         
     | 
| 
       81 
207 
     | 
    
         
             
                      end
         
     | 
| 
      
 208 
     | 
    
         
            +
                      ex
         
     | 
| 
       82 
209 
     | 
    
         
             
                    end
         
     | 
| 
       83 
     | 
    
         
            -
                    nil
         
     | 
| 
       84 
210 
     | 
    
         
             
                  end
         
     | 
| 
       85 
211 
     | 
    
         | 
| 
       86 
212 
     | 
    
         
             
                  class ListResultFailedError < GraphQL::Error
         
     | 
| 
         @@ -87,16 +87,28 @@ module GraphQL 
     | 
|
| 
       87 
87 
     | 
    
         | 
| 
       88 
88 
     | 
    
         
             
                  # Like {#selects?}, but can be used for chaining.
         
     | 
| 
       89 
89 
     | 
    
         
             
                  # It returns a null object (check with {#selected?})
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # @param field_name [String, Symbol]
         
     | 
| 
       90 
91 
     | 
    
         
             
                  # @return [GraphQL::Execution::Lookahead]
         
     | 
| 
       91 
92 
     | 
    
         
             
                  def selection(field_name, selected_type: @selected_type, arguments: nil)
         
     | 
| 
       92 
     | 
    
         
            -
                     
     | 
| 
      
 93 
     | 
    
         
            +
                    next_field_defn = case field_name
         
     | 
| 
      
 94 
     | 
    
         
            +
                    when String
         
     | 
| 
      
 95 
     | 
    
         
            +
                      @query.get_field(selected_type, field_name)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    when Symbol
         
     | 
| 
      
 97 
     | 
    
         
            +
                      # Try to avoid the `.to_s` below, if possible
         
     | 
| 
      
 98 
     | 
    
         
            +
                      all_fields = @query.warden.fields(selected_type)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
         
     | 
| 
      
 100 
     | 
    
         
            +
                        match_by_orig_name
         
     | 
| 
      
 101 
     | 
    
         
            +
                      else
         
     | 
| 
      
 102 
     | 
    
         
            +
                        guessed_name = Schema::Member::BuildType.camelize(field_name.to_s)
         
     | 
| 
      
 103 
     | 
    
         
            +
                        @query.get_field(selected_type, guessed_name)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      end
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
       93 
106 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                    next_field_defn = @query.get_field(selected_type, next_field_name)
         
     | 
| 
       95 
107 
     | 
    
         
             
                    if next_field_defn
         
     | 
| 
       96 
108 
     | 
    
         
             
                      next_nodes = []
         
     | 
| 
       97 
109 
     | 
    
         
             
                      @ast_nodes.each do |ast_node|
         
     | 
| 
       98 
110 
     | 
    
         
             
                        ast_node.selections.each do |selection|
         
     | 
| 
       99 
     | 
    
         
            -
                          find_selected_nodes(selection,  
     | 
| 
      
 111 
     | 
    
         
            +
                          find_selected_nodes(selection, next_field_defn, arguments: arguments, matches: next_nodes)
         
     | 
| 
       100 
112 
     | 
    
         
             
                        end
         
     | 
| 
       101 
113 
     | 
    
         
             
                      end
         
     | 
| 
       102 
114 
     | 
    
         | 
| 
         @@ -196,23 +208,6 @@ module GraphQL 
     | 
|
| 
       196 
208 
     | 
    
         | 
| 
       197 
209 
     | 
    
         
             
                  private
         
     | 
| 
       198 
210 
     | 
    
         | 
| 
       199 
     | 
    
         
            -
                  # If it's a symbol, stringify and camelize it
         
     | 
| 
       200 
     | 
    
         
            -
                  def normalize_name(name)
         
     | 
| 
       201 
     | 
    
         
            -
                    if name.is_a?(Symbol)
         
     | 
| 
       202 
     | 
    
         
            -
                      Schema::Member::BuildType.camelize(name.to_s)
         
     | 
| 
       203 
     | 
    
         
            -
                    else
         
     | 
| 
       204 
     | 
    
         
            -
                      name
         
     | 
| 
       205 
     | 
    
         
            -
                    end
         
     | 
| 
       206 
     | 
    
         
            -
                  end
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
                  def normalize_keyword(keyword)
         
     | 
| 
       209 
     | 
    
         
            -
                    if keyword.is_a?(String)
         
     | 
| 
       210 
     | 
    
         
            -
                      Schema::Member::BuildType.underscore(keyword).to_sym
         
     | 
| 
       211 
     | 
    
         
            -
                    else
         
     | 
| 
       212 
     | 
    
         
            -
                      keyword
         
     | 
| 
       213 
     | 
    
         
            -
                    end
         
     | 
| 
       214 
     | 
    
         
            -
                  end
         
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
211 
     | 
    
         
             
                  def skipped_by_directive?(ast_selection)
         
     | 
| 
       217 
212 
     | 
    
         
             
                    ast_selection.directives.each do |directive|
         
     | 
| 
       218 
213 
     | 
    
         
             
                      dir_defn = @query.schema.directives.fetch(directive.name)
         
     | 
| 
         @@ -265,11 +260,11 @@ module GraphQL 
     | 
|
| 
       265 
260 
     | 
    
         | 
| 
       266 
261 
     | 
    
         
             
                  # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
         
     | 
| 
       267 
262 
     | 
    
         
             
                  # and matches the `arguments:` constraints, then add that node to `matches`
         
     | 
| 
       268 
     | 
    
         
            -
                  def find_selected_nodes(node,  
     | 
| 
      
 263 
     | 
    
         
            +
                  def find_selected_nodes(node, field_defn, arguments:, matches:)
         
     | 
| 
       269 
264 
     | 
    
         
             
                    return if skipped_by_directive?(node)
         
     | 
| 
       270 
265 
     | 
    
         
             
                    case node
         
     | 
| 
       271 
266 
     | 
    
         
             
                    when GraphQL::Language::Nodes::Field
         
     | 
| 
       272 
     | 
    
         
            -
                      if node.name ==  
     | 
| 
      
 267 
     | 
    
         
            +
                      if node.name == field_defn.graphql_name
         
     | 
| 
       273 
268 
     | 
    
         
             
                        if arguments.nil? || arguments.empty?
         
     | 
| 
       274 
269 
     | 
    
         
             
                          # No constraint applied
         
     | 
| 
       275 
270 
     | 
    
         
             
                          matches << node
         
     | 
| 
         @@ -278,10 +273,10 @@ module GraphQL 
     | 
|
| 
       278 
273 
     | 
    
         
             
                        end
         
     | 
| 
       279 
274 
     | 
    
         
             
                      end
         
     | 
| 
       280 
275 
     | 
    
         
             
                    when GraphQL::Language::Nodes::InlineFragment
         
     | 
| 
       281 
     | 
    
         
            -
                      node.selections.each { |s| find_selected_nodes(s,  
     | 
| 
      
 276 
     | 
    
         
            +
                      node.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
         
     | 
| 
       282 
277 
     | 
    
         
             
                    when GraphQL::Language::Nodes::FragmentSpread
         
     | 
| 
       283 
278 
     | 
    
         
             
                      frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
         
     | 
| 
       284 
     | 
    
         
            -
                      frag_defn.selections.each { |s| find_selected_nodes(s,  
     | 
| 
      
 279 
     | 
    
         
            +
                      frag_defn.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
         
     | 
| 
       285 
280 
     | 
    
         
             
                    else
         
     | 
| 
       286 
281 
     | 
    
         
             
                      raise "Unexpected selection comparison on #{node.class.name} (#{node})"
         
     | 
| 
       287 
282 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -290,9 +285,14 @@ module GraphQL 
     | 
|
| 
       290 
285 
     | 
    
         
             
                  def arguments_match?(arguments, field_defn, field_node)
         
     | 
| 
       291 
286 
     | 
    
         
             
                    query_kwargs = @query.arguments_for(field_node, field_defn)
         
     | 
| 
       292 
287 
     | 
    
         
             
                    arguments.all? do |arg_name, arg_value|
         
     | 
| 
       293 
     | 
    
         
            -
                       
     | 
| 
      
 288 
     | 
    
         
            +
                      arg_name_sym = if arg_name.is_a?(String)
         
     | 
| 
      
 289 
     | 
    
         
            +
                        Schema::Member::BuildType.underscore(arg_name).to_sym
         
     | 
| 
      
 290 
     | 
    
         
            +
                      else
         
     | 
| 
      
 291 
     | 
    
         
            +
                        arg_name
         
     | 
| 
      
 292 
     | 
    
         
            +
                      end
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
       294 
294 
     | 
    
         
             
                      # Make sure the constraint is present with a matching value
         
     | 
| 
       295 
     | 
    
         
            -
                      query_kwargs.key?( 
     | 
| 
      
 295 
     | 
    
         
            +
                      query_kwargs.key?(arg_name_sym) && query_kwargs[arg_name_sym] == arg_value
         
     | 
| 
       296 
296 
     | 
    
         
             
                    end
         
     | 
| 
       297 
297 
     | 
    
         
             
                  end
         
     | 
| 
       298 
298 
     | 
    
         
             
                end
         
     | 
| 
         @@ -23,13 +23,10 @@ module GraphQL 
     | 
|
| 
       23 
23 
     | 
    
         
             
                # @see {Schema#multiplex} for public API
         
     | 
| 
       24 
24 
     | 
    
         
             
                # @api private
         
     | 
| 
       25 
25 
     | 
    
         
             
                class Multiplex
         
     | 
| 
       26 
     | 
    
         
            -
                  # Used internally to signal that the query shouldn't be executed
         
     | 
| 
       27 
     | 
    
         
            -
                  # @api private
         
     | 
| 
       28 
     | 
    
         
            -
                  NO_OPERATION = {}.freeze
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
26 
     | 
    
         
             
                  include Tracing::Traceable
         
     | 
| 
       31 
27 
     | 
    
         | 
| 
       32 
28 
     | 
    
         
             
                  attr_reader :context, :queries, :schema, :max_complexity, :dataloader
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       33 
30 
     | 
    
         
             
                  def initialize(schema:, queries:, context:, max_complexity:)
         
     | 
| 
       34 
31 
     | 
    
         
             
                    @schema = schema
         
     | 
| 
       35 
32 
     | 
    
         
             
                    @queries = queries
         
     | 
| 
         @@ -43,118 +40,6 @@ module GraphQL 
     | 
|
| 
       43 
40 
     | 
    
         
             
                    end
         
     | 
| 
       44 
41 
     | 
    
         
             
                    @max_complexity = max_complexity
         
     | 
| 
       45 
42 
     | 
    
         
             
                  end
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                  class << self
         
     | 
| 
       48 
     | 
    
         
            -
                    # @param schema [GraphQL::Schema]
         
     | 
| 
       49 
     | 
    
         
            -
                    # @param queries [Array<GraphQL::Query, Hash>]
         
     | 
| 
       50 
     | 
    
         
            -
                    # @param context [Hash]
         
     | 
| 
       51 
     | 
    
         
            -
                    # @param max_complexity [Integer, nil]
         
     | 
| 
       52 
     | 
    
         
            -
                    # @return [Array<Hash>] One result per query
         
     | 
| 
       53 
     | 
    
         
            -
                    def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
         
     | 
| 
       54 
     | 
    
         
            -
                      queries = query_options.map do |opts|
         
     | 
| 
       55 
     | 
    
         
            -
                        case opts
         
     | 
| 
       56 
     | 
    
         
            -
                        when Hash
         
     | 
| 
       57 
     | 
    
         
            -
                          GraphQL::Query.new(schema, nil, **opts)
         
     | 
| 
       58 
     | 
    
         
            -
                        when GraphQL::Query
         
     | 
| 
       59 
     | 
    
         
            -
                          opts
         
     | 
| 
       60 
     | 
    
         
            -
                        else
         
     | 
| 
       61 
     | 
    
         
            -
                          raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
         
     | 
| 
       62 
     | 
    
         
            -
                        end
         
     | 
| 
       63 
     | 
    
         
            -
                      end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                      multiplex = self.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
         
     | 
| 
       66 
     | 
    
         
            -
                      multiplex.trace("execute_multiplex", { multiplex: multiplex }) do
         
     | 
| 
       67 
     | 
    
         
            -
                        GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
         
     | 
| 
       68 
     | 
    
         
            -
                          schema = multiplex.schema
         
     | 
| 
       69 
     | 
    
         
            -
                          multiplex_analyzers = schema.multiplex_analyzers
         
     | 
| 
       70 
     | 
    
         
            -
                          if multiplex.max_complexity
         
     | 
| 
       71 
     | 
    
         
            -
                            multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
         
     | 
| 
       72 
     | 
    
         
            -
                          end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                          schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                          begin
         
     | 
| 
       77 
     | 
    
         
            -
                            multiplex.schema.query_execution_strategy.begin_multiplex(multiplex)
         
     | 
| 
       78 
     | 
    
         
            -
                            # Do as much eager evaluation of the query as possible
         
     | 
| 
       79 
     | 
    
         
            -
                            results = []
         
     | 
| 
       80 
     | 
    
         
            -
                            queries.each_with_index do |query, idx|
         
     | 
| 
       81 
     | 
    
         
            -
                              multiplex.dataloader.append_job { begin_query(results, idx, query, multiplex) }
         
     | 
| 
       82 
     | 
    
         
            -
                            end
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                            multiplex.dataloader.run
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                            # Then, work through lazy results in a breadth-first way
         
     | 
| 
       87 
     | 
    
         
            -
                            multiplex.dataloader.append_job {
         
     | 
| 
       88 
     | 
    
         
            -
                              multiplex.schema.query_execution_strategy.finish_multiplex(results, multiplex)
         
     | 
| 
       89 
     | 
    
         
            -
                            }
         
     | 
| 
       90 
     | 
    
         
            -
                            multiplex.dataloader.run
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                            # Then, find all errors and assign the result to the query object
         
     | 
| 
       93 
     | 
    
         
            -
                            results.each_with_index do |data_result, idx|
         
     | 
| 
       94 
     | 
    
         
            -
                              query = queries[idx]
         
     | 
| 
       95 
     | 
    
         
            -
                              finish_query(data_result, query, multiplex)
         
     | 
| 
       96 
     | 
    
         
            -
                              # Get the Query::Result, not the Hash
         
     | 
| 
       97 
     | 
    
         
            -
                              results[idx] = query.result
         
     | 
| 
       98 
     | 
    
         
            -
                            end
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                            results
         
     | 
| 
       101 
     | 
    
         
            -
                          rescue Exception
         
     | 
| 
       102 
     | 
    
         
            -
                            # TODO rescue at a higher level so it will catch errors in analysis, too
         
     | 
| 
       103 
     | 
    
         
            -
                            # Assign values here so that the query's `@executed` becomes true
         
     | 
| 
       104 
     | 
    
         
            -
                            queries.map { |q| q.result_values ||= {} }
         
     | 
| 
       105 
     | 
    
         
            -
                            raise
         
     | 
| 
       106 
     | 
    
         
            -
                          end
         
     | 
| 
       107 
     | 
    
         
            -
                        end
         
     | 
| 
       108 
     | 
    
         
            -
                      end
         
     | 
| 
       109 
     | 
    
         
            -
                    end
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                    # @param query [GraphQL::Query]
         
     | 
| 
       112 
     | 
    
         
            -
                    def begin_query(results, idx, query, multiplex)
         
     | 
| 
       113 
     | 
    
         
            -
                      operation = query.selected_operation
         
     | 
| 
       114 
     | 
    
         
            -
                      result = if operation.nil? || !query.valid? || query.context.errors.any?
         
     | 
| 
       115 
     | 
    
         
            -
                        NO_OPERATION
         
     | 
| 
       116 
     | 
    
         
            -
                      else
         
     | 
| 
       117 
     | 
    
         
            -
                        begin
         
     | 
| 
       118 
     | 
    
         
            -
                          query.schema.query_execution_strategy.begin_query(query, multiplex)
         
     | 
| 
       119 
     | 
    
         
            -
                        rescue GraphQL::ExecutionError => err
         
     | 
| 
       120 
     | 
    
         
            -
                          query.context.errors << err
         
     | 
| 
       121 
     | 
    
         
            -
                          NO_OPERATION
         
     | 
| 
       122 
     | 
    
         
            -
                        end
         
     | 
| 
       123 
     | 
    
         
            -
                      end
         
     | 
| 
       124 
     | 
    
         
            -
                      results[idx] = result
         
     | 
| 
       125 
     | 
    
         
            -
                      nil
         
     | 
| 
       126 
     | 
    
         
            -
                    end
         
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
                    private
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
                    # @param data_result [Hash] The result for the "data" key, if any
         
     | 
| 
       131 
     | 
    
         
            -
                    # @param query [GraphQL::Query] The query which was run
         
     | 
| 
       132 
     | 
    
         
            -
                    # @return [Hash] final result of this query, including all values and errors
         
     | 
| 
       133 
     | 
    
         
            -
                    def finish_query(data_result, query, multiplex)
         
     | 
| 
       134 
     | 
    
         
            -
                      # Assign the result so that it can be accessed in instrumentation
         
     | 
| 
       135 
     | 
    
         
            -
                      query.result_values = if data_result.equal?(NO_OPERATION)
         
     | 
| 
       136 
     | 
    
         
            -
                        if !query.valid? || query.context.errors.any?
         
     | 
| 
       137 
     | 
    
         
            -
                          # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
         
     | 
| 
       138 
     | 
    
         
            -
                          { "errors" => query.static_errors.map(&:to_h) }
         
     | 
| 
       139 
     | 
    
         
            -
                        else
         
     | 
| 
       140 
     | 
    
         
            -
                          data_result
         
     | 
| 
       141 
     | 
    
         
            -
                        end
         
     | 
| 
       142 
     | 
    
         
            -
                      else
         
     | 
| 
       143 
     | 
    
         
            -
                        # Use `context.value` which was assigned during execution
         
     | 
| 
       144 
     | 
    
         
            -
                        result = query.schema.query_execution_strategy.finish_query(query, multiplex)
         
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
                        if query.context.errors.any?
         
     | 
| 
       147 
     | 
    
         
            -
                          error_result = query.context.errors.map(&:to_h)
         
     | 
| 
       148 
     | 
    
         
            -
                          result["errors"] = error_result
         
     | 
| 
       149 
     | 
    
         
            -
                        end
         
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
                        result
         
     | 
| 
       152 
     | 
    
         
            -
                      end
         
     | 
| 
       153 
     | 
    
         
            -
                      if query.context.namespace?(:__query_result_extensions__)
         
     | 
| 
       154 
     | 
    
         
            -
                        query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
         
     | 
| 
       155 
     | 
    
         
            -
                      end
         
     | 
| 
       156 
     | 
    
         
            -
                    end
         
     | 
| 
       157 
     | 
    
         
            -
                  end
         
     | 
| 
       158 
43 
     | 
    
         
             
                end
         
     | 
| 
       159 
44 
     | 
    
         
             
              end
         
     | 
| 
       160 
45 
     | 
    
         
             
            end
         
     | 
    
        data/lib/graphql/execution.rb
    CHANGED
    
    
| 
         @@ -29,6 +29,13 @@ module GraphQL 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                  field :specifiedByURL, String, resolver_method: :specified_by_url
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                  field :is_one_of, Boolean, null: false
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def is_one_of
         
     | 
| 
      
 35 
     | 
    
         
            +
                    object.kind.input_object? &&
         
     | 
| 
      
 36 
     | 
    
         
            +
                      object.directives.any? { |d| d.graphql_name == "oneOf" }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       32 
39 
     | 
    
         
             
                  def specified_by_url
         
     | 
| 
       33 
40 
     | 
    
         
             
                    if object.kind.scalar?
         
     | 
| 
       34 
41 
     | 
    
         
             
                      object.specified_by_url
         
     | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            module GraphQL
         
     | 
| 
       3 
3 
     | 
    
         
             
              module Introspection
         
     | 
| 
       4 
     | 
    
         
            -
                def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false)
         
     | 
| 
      
 4 
     | 
    
         
            +
                def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  # The introspection query to end all introspection queries, copied from
         
     | 
| 
       6 
6 
     | 
    
         
             
                  # https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
         
     | 
| 
       7 
7 
     | 
    
         
             
                  <<-QUERY
         
     | 
| 
         @@ -30,6 +30,7 @@ fragment FullType on __Type { 
     | 
|
| 
       30 
30 
     | 
    
         
             
              name
         
     | 
| 
       31 
31 
     | 
    
         
             
              description
         
     | 
| 
       32 
32 
     | 
    
         
             
              #{include_specified_by_url ? "specifiedByURL" : ""}
         
     | 
| 
      
 33 
     | 
    
         
            +
              #{include_is_one_of ? "isOneOf" : ""}
         
     | 
| 
       33 
34 
     | 
    
         
             
              fields(includeDeprecated: true) {
         
     | 
| 
       34 
35 
     | 
    
         
             
                name
         
     | 
| 
       35 
36 
     | 
    
         
             
                description
         
     | 
    
        data/lib/graphql/query.rb
    CHANGED
    
    | 
         @@ -196,7 +196,7 @@ module GraphQL 
     | 
|
| 
       196 
196 
     | 
    
         
             
                # @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
         
     | 
| 
       197 
197 
     | 
    
         
             
                def result
         
     | 
| 
       198 
198 
     | 
    
         
             
                  if !@executed
         
     | 
| 
       199 
     | 
    
         
            -
                    Execution:: 
     | 
| 
      
 199 
     | 
    
         
            +
                    Execution::Interpreter.run_all(@schema, [self], context: @context)
         
     | 
| 
       200 
200 
     | 
    
         
             
                  end
         
     | 
| 
       201 
201 
     | 
    
         
             
                  @result ||= Query::Result.new(query: self, values: @result_values)
         
     | 
| 
       202 
202 
     | 
    
         
             
                end
         
     | 
| 
         @@ -55,14 +55,13 @@ module GraphQL 
     | 
|
| 
       55 
55 
     | 
    
         
             
                        end
         
     | 
| 
       56 
56 
     | 
    
         
             
                      })
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
      
 58 
     | 
    
         
            +
                      directives.merge!(GraphQL::Schema.default_directives)
         
     | 
| 
       58 
59 
     | 
    
         
             
                      document.definitions.each do |definition|
         
     | 
| 
       59 
60 
     | 
    
         
             
                        if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
         
     | 
| 
       60 
61 
     | 
    
         
             
                          directives[definition.name] = build_directive(definition, directive_type_resolver)
         
     | 
| 
       61 
62 
     | 
    
         
             
                        end
         
     | 
| 
       62 
63 
     | 
    
         
             
                      end
         
     | 
| 
       63 
64 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                      directives = GraphQL::Schema.default_directives.merge(directives)
         
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
65 
     | 
    
         
             
                      # In case any directives referenced built-in types for their arguments:
         
     | 
| 
       67 
66 
     | 
    
         
             
                      replace_late_bound_types_with_built_in(types)
         
     | 
| 
       68 
67 
     | 
    
         | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module GraphQL
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Schema
         
     | 
| 
      
 4 
     | 
    
         
            +
                class Directive < GraphQL::Schema::Member
         
     | 
| 
      
 5 
     | 
    
         
            +
                  class OneOf < GraphQL::Schema::Directive
         
     | 
| 
      
 6 
     | 
    
         
            +
                    description "Requires that exactly one field must be supplied and that field must not be `null`."
         
     | 
| 
      
 7 
     | 
    
         
            +
                    locations(GraphQL::Schema::Directive::INPUT_OBJECT)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    default_directive true
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -69,6 +69,19 @@ module GraphQL 
     | 
|
| 
       69 
69 
     | 
    
         
             
                    true
         
     | 
| 
       70 
70 
     | 
    
         
             
                  end
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
      
 72 
     | 
    
         
            +
                  def self.one_of
         
     | 
| 
      
 73 
     | 
    
         
            +
                    if !one_of?
         
     | 
| 
      
 74 
     | 
    
         
            +
                      if all_argument_definitions.any? { |arg| arg.type.non_null? }
         
     | 
| 
      
 75 
     | 
    
         
            +
                        raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                      directive(GraphQL::Schema::Directive::OneOf)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def self.one_of?
         
     | 
| 
      
 82 
     | 
    
         
            +
                    directives.any? { |d| d.is_a?(GraphQL::Schema::Directive::OneOf) }
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
       72 
85 
     | 
    
         
             
                  def unwrap_value(value)
         
     | 
| 
       73 
86 
     | 
    
         
             
                    case value
         
     | 
| 
       74 
87 
     | 
    
         
             
                    when Array
         
     | 
| 
         @@ -109,6 +122,14 @@ module GraphQL 
     | 
|
| 
       109 
122 
     | 
    
         
             
                  class << self
         
     | 
| 
       110 
123 
     | 
    
         
             
                    def argument(*args, **kwargs, &block)
         
     | 
| 
       111 
124 
     | 
    
         
             
                      argument_defn = super(*args, **kwargs, &block)
         
     | 
| 
      
 125 
     | 
    
         
            +
                      if one_of?
         
     | 
| 
      
 126 
     | 
    
         
            +
                        if argument_defn.type.non_null?
         
     | 
| 
      
 127 
     | 
    
         
            +
                          raise ArgumentError, "Argument '#{argument_defn.path}' must be nullable because it is part of a OneOf type, add `required: false`."
         
     | 
| 
      
 128 
     | 
    
         
            +
                        end
         
     | 
| 
      
 129 
     | 
    
         
            +
                        if argument_defn.default_value?
         
     | 
| 
      
 130 
     | 
    
         
            +
                          raise ArgumentError, "Argument '#{argument_defn.path}' cannot have a default value because it is part of a OneOf type, remove `default_value: ...`."
         
     | 
| 
      
 131 
     | 
    
         
            +
                        end
         
     | 
| 
      
 132 
     | 
    
         
            +
                      end
         
     | 
| 
       112 
133 
     | 
    
         
             
                      # Add a method access
         
     | 
| 
       113 
134 
     | 
    
         
             
                      method_name = argument_defn.keyword
         
     | 
| 
       114 
135 
     | 
    
         
             
                      class_eval <<-RUBY, __FILE__, __LINE__
         
     | 
| 
         @@ -166,6 +187,20 @@ module GraphQL 
     | 
|
| 
       166 
187 
     | 
    
         
             
                        end
         
     | 
| 
       167 
188 
     | 
    
         
             
                      end
         
     | 
| 
       168 
189 
     | 
    
         | 
| 
      
 190 
     | 
    
         
            +
                      if one_of?
         
     | 
| 
      
 191 
     | 
    
         
            +
                        if input.size == 1
         
     | 
| 
      
 192 
     | 
    
         
            +
                          input.each do |name, value|
         
     | 
| 
      
 193 
     | 
    
         
            +
                            if value.nil?
         
     | 
| 
      
 194 
     | 
    
         
            +
                              result ||= Query::InputValidationResult.new
         
     | 
| 
      
 195 
     | 
    
         
            +
                              result.add_problem("'#{graphql_name}' requires exactly one argument, but '#{name}' was `null`.")
         
     | 
| 
      
 196 
     | 
    
         
            +
                            end
         
     | 
| 
      
 197 
     | 
    
         
            +
                          end
         
     | 
| 
      
 198 
     | 
    
         
            +
                        else
         
     | 
| 
      
 199 
     | 
    
         
            +
                          result ||= Query::InputValidationResult.new
         
     | 
| 
      
 200 
     | 
    
         
            +
                          result.add_problem("'#{graphql_name}' requires exactly one argument, but #{input.size} were provided.")
         
     | 
| 
      
 201 
     | 
    
         
            +
                        end
         
     | 
| 
      
 202 
     | 
    
         
            +
                      end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
       169 
204 
     | 
    
         
             
                      result
         
     | 
| 
       170 
205 
     | 
    
         
             
                    end
         
     | 
| 
       171 
206 
     | 
    
         | 
    
        data/lib/graphql/schema.rb
    CHANGED
    
    | 
         @@ -31,6 +31,7 @@ require "graphql/schema/union" 
     | 
|
| 
       31 
31 
     | 
    
         
             
            require "graphql/schema/directive"
         
     | 
| 
       32 
32 
     | 
    
         
             
            require "graphql/schema/directive/deprecated"
         
     | 
| 
       33 
33 
     | 
    
         
             
            require "graphql/schema/directive/include"
         
     | 
| 
      
 34 
     | 
    
         
            +
            require "graphql/schema/directive/one_of"
         
     | 
| 
       34 
35 
     | 
    
         
             
            require "graphql/schema/directive/skip"
         
     | 
| 
       35 
36 
     | 
    
         
             
            require "graphql/schema/directive/feature"
         
     | 
| 
       36 
37 
     | 
    
         
             
            require "graphql/schema/directive/flagged"
         
     | 
| 
         @@ -913,6 +914,7 @@ module GraphQL 
     | 
|
| 
       913 
914 
     | 
    
         
             
                      "include" => GraphQL::Schema::Directive::Include,
         
     | 
| 
       914 
915 
     | 
    
         
             
                      "skip" => GraphQL::Schema::Directive::Skip,
         
     | 
| 
       915 
916 
     | 
    
         
             
                      "deprecated" => GraphQL::Schema::Directive::Deprecated,
         
     | 
| 
      
 917 
     | 
    
         
            +
                      "oneOf" => GraphQL::Schema::Directive::OneOf,
         
     | 
| 
       916 
918 
     | 
    
         
             
                    }.freeze
         
     | 
| 
       917 
919 
     | 
    
         
             
                  end
         
     | 
| 
       918 
920 
     | 
    
         | 
| 
         @@ -990,7 +992,7 @@ module GraphQL 
     | 
|
| 
       990 
992 
     | 
    
         
             
                  # @param context [Hash] Multiplex-level context
         
     | 
| 
       991 
993 
     | 
    
         
             
                  # @return [Array<Hash>] One result for each query in the input
         
     | 
| 
       992 
994 
     | 
    
         
             
                  def multiplex(queries, **kwargs)
         
     | 
| 
       993 
     | 
    
         
            -
                    GraphQL::Execution:: 
     | 
| 
      
 995 
     | 
    
         
            +
                    GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
         
     | 
| 
       994 
996 
     | 
    
         
             
                  end
         
     | 
| 
       995 
997 
     | 
    
         | 
| 
       996 
998 
     | 
    
         
             
                  def instrumenters
         
     | 
| 
         @@ -108,6 +108,10 @@ module GraphQL 
     | 
|
| 
       108 
108 
     | 
    
         
             
                        arg_type = @warden.get_argument(type, name).type
         
     | 
| 
       109 
109 
     | 
    
         
             
                        recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
         
     | 
| 
       110 
110 
     | 
    
         
             
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                      if type.one_of? && ast_node.arguments.size != 1
         
     | 
| 
      
 113 
     | 
    
         
            +
                        results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
         
     | 
| 
      
 114 
     | 
    
         
            +
                      end
         
     | 
| 
       111 
115 
     | 
    
         
             
                      merge_results(results)
         
     | 
| 
       112 
116 
     | 
    
         
             
                    end
         
     | 
| 
       113 
117 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module GraphQL
         
     | 
| 
      
 3 
     | 
    
         
            +
              module StaticValidation
         
     | 
| 
      
 4 
     | 
    
         
            +
                module OneOfInputObjectsAreValid
         
     | 
| 
      
 5 
     | 
    
         
            +
                  def on_input_object(node, parent)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    return super unless parent.is_a?(GraphQL::Language::Nodes::Argument)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                    parent_type = get_parent_type(context, parent)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    return super unless parent_type && parent_type.kind.input_object? && parent_type.one_of?
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    validate_one_of_input_object(node, context, parent_type)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    super
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  private
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def validate_one_of_input_object(ast_node, context, parent_type)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    present_fields = ast_node.arguments.map(&:name)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    input_object_type = parent_type.to_type_signature
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    if present_fields.count != 1
         
     | 
| 
      
 22 
     | 
    
         
            +
                      add_error(
         
     | 
| 
      
 23 
     | 
    
         
            +
                        OneOfInputObjectsAreValidError.new(
         
     | 
| 
      
 24 
     | 
    
         
            +
                          "OneOf Input Object '#{input_object_type}' must specify exactly one key.",
         
     | 
| 
      
 25 
     | 
    
         
            +
                          path: context.path,
         
     | 
| 
      
 26 
     | 
    
         
            +
                          nodes: ast_node,
         
     | 
| 
      
 27 
     | 
    
         
            +
                          input_object_type: input_object_type
         
     | 
| 
      
 28 
     | 
    
         
            +
                        )
         
     | 
| 
      
 29 
     | 
    
         
            +
                      )
         
     | 
| 
      
 30 
     | 
    
         
            +
                      return
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    field = present_fields.first
         
     | 
| 
      
 34 
     | 
    
         
            +
                    value = ast_node.arguments.first.value
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    if value.is_a?(GraphQL::Language::Nodes::NullValue)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      add_error(
         
     | 
| 
      
 38 
     | 
    
         
            +
                        OneOfInputObjectsAreValidError.new(
         
     | 
| 
      
 39 
     | 
    
         
            +
                          "Argument '#{input_object_type}.#{field}' must be non-null.",
         
     | 
| 
      
 40 
     | 
    
         
            +
                          path: [*context.path, field],
         
     | 
| 
      
 41 
     | 
    
         
            +
                          nodes: ast_node.arguments.first,
         
     | 
| 
      
 42 
     | 
    
         
            +
                          input_object_type: input_object_type
         
     | 
| 
      
 43 
     | 
    
         
            +
                        )
         
     | 
| 
      
 44 
     | 
    
         
            +
                      )
         
     | 
| 
      
 45 
     | 
    
         
            +
                      return
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      variable_name = value.name
         
     | 
| 
      
 50 
     | 
    
         
            +
                      variable_type = @declared_variables[variable_name].type
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                      unless variable_type.is_a?(GraphQL::Language::Nodes::NonNullType)
         
     | 
| 
      
 53 
     | 
    
         
            +
                        add_error(
         
     | 
| 
      
 54 
     | 
    
         
            +
                          OneOfInputObjectsAreValidError.new(
         
     | 
| 
      
 55 
     | 
    
         
            +
                            "Variable '#{variable_name}' must be non-nullable to be used for OneOf Input Object '#{input_object_type}'.",
         
     | 
| 
      
 56 
     | 
    
         
            +
                            path: [*context.path, field],
         
     | 
| 
      
 57 
     | 
    
         
            +
                            nodes: ast_node,
         
     | 
| 
      
 58 
     | 
    
         
            +
                            input_object_type: input_object_type
         
     | 
| 
      
 59 
     | 
    
         
            +
                          )
         
     | 
| 
      
 60 
     | 
    
         
            +
                        )
         
     | 
| 
      
 61 
     | 
    
         
            +
                      end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module GraphQL
         
     | 
| 
      
 3 
     | 
    
         
            +
              module StaticValidation
         
     | 
| 
      
 4 
     | 
    
         
            +
                class OneOfInputObjectsAreValidError < StaticValidation::Error
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :input_object_type
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(message, path:, nodes:, input_object_type:)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    super(message, path: path, nodes: nodes)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @input_object_type = input_object_type
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  # A hash representation of this Message
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def to_h
         
     | 
| 
      
 14 
     | 
    
         
            +
                    extensions = {
         
     | 
| 
      
 15 
     | 
    
         
            +
                      "code" => code,
         
     | 
| 
      
 16 
     | 
    
         
            +
                      "inputObjectType" => input_object_type
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    super.merge({
         
     | 
| 
      
 20 
     | 
    
         
            +
                      "extensions" => extensions
         
     | 
| 
      
 21 
     | 
    
         
            +
                    })
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def code
         
     | 
| 
      
 25 
     | 
    
         
            +
                    "invalidOneOfInputObject"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -23,7 +23,7 @@ module GraphQL 
     | 
|
| 
       23 
23 
     | 
    
         
             
                            problems = validation_result.problems
         
     | 
| 
       24 
24 
     | 
    
         
             
                            first_problem = problems && problems.first
         
     | 
| 
       25 
25 
     | 
    
         
             
                            if first_problem
         
     | 
| 
       26 
     | 
    
         
            -
                              error_message = first_problem[" 
     | 
| 
      
 26 
     | 
    
         
            +
                              error_message = first_problem["explanation"]
         
     | 
| 
       27 
27 
     | 
    
         
             
                            end
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
                            error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
         
     | 
| 
         @@ -75,10 +75,12 @@ module GraphQL 
     | 
|
| 
       75 
75 
     | 
    
         
             
                  end
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
                  def analytics_enabled?
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # [Deprecated] options[:analytics_enabled] will be removed in the future
         
     | 
| 
       78 
79 
     | 
    
         
             
                    analytics_available? && Datadog::Contrib::Analytics.enabled?(options.fetch(:analytics_enabled, false))
         
     | 
| 
       79 
80 
     | 
    
         
             
                  end
         
     | 
| 
       80 
81 
     | 
    
         | 
| 
       81 
82 
     | 
    
         
             
                  def analytics_sample_rate
         
     | 
| 
      
 83 
     | 
    
         
            +
                    # [Deprecated] options[:analytics_sample_rate] will be removed in the future
         
     | 
| 
       82 
84 
     | 
    
         
             
                    options.fetch(:analytics_sample_rate, 1.0)
         
     | 
| 
       83 
85 
     | 
    
         
             
                  end
         
     | 
| 
       84 
86 
     | 
    
         | 
    
        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.14
         
     | 
| 
       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-08 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-09-08 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: benchmark-ips
         
     | 
| 
         @@ -303,7 +303,6 @@ files: 
     | 
|
| 
       303 
303 
     | 
    
         
             
            - lib/graphql/execution.rb
         
     | 
| 
       304 
304 
     | 
    
         
             
            - lib/graphql/execution/directive_checks.rb
         
     | 
| 
       305 
305 
     | 
    
         
             
            - lib/graphql/execution/errors.rb
         
     | 
| 
       306 
     | 
    
         
            -
            - lib/graphql/execution/instrumentation.rb
         
     | 
| 
       307 
306 
     | 
    
         
             
            - lib/graphql/execution/interpreter.rb
         
     | 
| 
       308 
307 
     | 
    
         
             
            - lib/graphql/execution/interpreter/argument_value.rb
         
     | 
| 
       309 
308 
     | 
    
         
             
            - lib/graphql/execution/interpreter/arguments.rb
         
     | 
| 
         @@ -394,6 +393,7 @@ files: 
     | 
|
| 
       394 
393 
     | 
    
         
             
            - lib/graphql/schema/directive/feature.rb
         
     | 
| 
       395 
394 
     | 
    
         
             
            - lib/graphql/schema/directive/flagged.rb
         
     | 
| 
       396 
395 
     | 
    
         
             
            - lib/graphql/schema/directive/include.rb
         
     | 
| 
      
 396 
     | 
    
         
            +
            - lib/graphql/schema/directive/one_of.rb
         
     | 
| 
       397 
397 
     | 
    
         
             
            - lib/graphql/schema/directive/skip.rb
         
     | 
| 
       398 
398 
     | 
    
         
             
            - lib/graphql/schema/directive/transform.rb
         
     | 
| 
       399 
399 
     | 
    
         
             
            - lib/graphql/schema/enum.rb
         
     | 
| 
         @@ -497,6 +497,8 @@ files: 
     | 
|
| 
       497 
497 
     | 
    
         
             
            - lib/graphql/static_validation/rules/mutation_root_exists_error.rb
         
     | 
| 
       498 
498 
     | 
    
         
             
            - lib/graphql/static_validation/rules/no_definitions_are_present.rb
         
     | 
| 
       499 
499 
     | 
    
         
             
            - lib/graphql/static_validation/rules/no_definitions_are_present_error.rb
         
     | 
| 
      
 500 
     | 
    
         
            +
            - lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb
         
     | 
| 
      
 501 
     | 
    
         
            +
            - lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb
         
     | 
| 
       500 
502 
     | 
    
         
             
            - lib/graphql/static_validation/rules/operation_names_are_valid.rb
         
     | 
| 
       501 
503 
     | 
    
         
             
            - lib/graphql/static_validation/rules/operation_names_are_valid_error.rb
         
     | 
| 
       502 
504 
     | 
    
         
             
            - lib/graphql/static_validation/rules/query_root_exists.rb
         
     | 
| 
         @@ -595,7 +597,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       595 
597 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       596 
598 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       597 
599 
     | 
    
         
             
            requirements: []
         
     | 
| 
       598 
     | 
    
         
            -
            rubygems_version: 3.2. 
     | 
| 
      
 600 
     | 
    
         
            +
            rubygems_version: 3.2.33
         
     | 
| 
       599 
601 
     | 
    
         
             
            signing_key:
         
     | 
| 
       600 
602 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       601 
603 
     | 
    
         
             
            summary: A GraphQL language and runtime for Ruby
         
     | 
| 
         @@ -1,92 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
            module GraphQL
         
     | 
| 
       3 
     | 
    
         
            -
              module Execution
         
     | 
| 
       4 
     | 
    
         
            -
                module Instrumentation
         
     | 
| 
       5 
     | 
    
         
            -
                  # This function implements the instrumentation policy:
         
     | 
| 
       6 
     | 
    
         
            -
                  #
         
     | 
| 
       7 
     | 
    
         
            -
                  # - Instrumenters are a stack; the first `before_query` will have the last `after_query`
         
     | 
| 
       8 
     | 
    
         
            -
                  # - If a `before_` hook returned without an error, its corresponding `after_` hook will run.
         
     | 
| 
       9 
     | 
    
         
            -
                  # - If the `before_` hook did _not_ run, the `after_` hook will not be called.
         
     | 
| 
       10 
     | 
    
         
            -
                  #
         
     | 
| 
       11 
     | 
    
         
            -
                  # When errors are raised from `after_` hooks:
         
     | 
| 
       12 
     | 
    
         
            -
                  #   - Subsequent `after_` hooks _are_ called
         
     | 
| 
       13 
     | 
    
         
            -
                  #   - The first raised error is captured; later errors are ignored
         
     | 
| 
       14 
     | 
    
         
            -
                  #   - If an error was capture, it's re-raised after all hooks are finished
         
     | 
| 
       15 
     | 
    
         
            -
                  #
         
     | 
| 
       16 
     | 
    
         
            -
                  # Partial runs of instrumentation are possible:
         
     | 
| 
       17 
     | 
    
         
            -
                  # - If a `before_multiplex` hook raises an error, no `before_query` hooks will run
         
     | 
| 
       18 
     | 
    
         
            -
                  # - If a `before_query` hook raises an error, subsequent `before_query` hooks will not run (on any query)
         
     | 
| 
       19 
     | 
    
         
            -
                  def self.apply_instrumenters(multiplex)
         
     | 
| 
       20 
     | 
    
         
            -
                    schema = multiplex.schema
         
     | 
| 
       21 
     | 
    
         
            -
                    queries = multiplex.queries
         
     | 
| 
       22 
     | 
    
         
            -
                    query_instrumenters = schema.instrumenters[:query]
         
     | 
| 
       23 
     | 
    
         
            -
                    multiplex_instrumenters = schema.instrumenters[:multiplex]
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                    # First, run multiplex instrumentation, then query instrumentation for each query
         
     | 
| 
       26 
     | 
    
         
            -
                    call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
         
     | 
| 
       27 
     | 
    
         
            -
                      each_query_call_hooks(query_instrumenters, queries) do
         
     | 
| 
       28 
     | 
    
         
            -
                        # Let them be executed
         
     | 
| 
       29 
     | 
    
         
            -
                        yield
         
     | 
| 
       30 
     | 
    
         
            -
                      end
         
     | 
| 
       31 
     | 
    
         
            -
                    end
         
     | 
| 
       32 
     | 
    
         
            -
                  end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                  class << self
         
     | 
| 
       35 
     | 
    
         
            -
                    private
         
     | 
| 
       36 
     | 
    
         
            -
                    # Call the before_ hooks of each query,
         
     | 
| 
       37 
     | 
    
         
            -
                    # Then yield if no errors.
         
     | 
| 
       38 
     | 
    
         
            -
                    # `call_hooks` takes care of appropriate cleanup.
         
     | 
| 
       39 
     | 
    
         
            -
                    def each_query_call_hooks(instrumenters, queries, i = 0)
         
     | 
| 
       40 
     | 
    
         
            -
                      if i >= queries.length
         
     | 
| 
       41 
     | 
    
         
            -
                        yield
         
     | 
| 
       42 
     | 
    
         
            -
                      else
         
     | 
| 
       43 
     | 
    
         
            -
                        query = queries[i]
         
     | 
| 
       44 
     | 
    
         
            -
                        call_hooks(instrumenters, query, :before_query, :after_query) {
         
     | 
| 
       45 
     | 
    
         
            -
                          each_query_call_hooks(instrumenters, queries, i + 1) {
         
     | 
| 
       46 
     | 
    
         
            -
                            yield
         
     | 
| 
       47 
     | 
    
         
            -
                          }
         
     | 
| 
       48 
     | 
    
         
            -
                        }
         
     | 
| 
       49 
     | 
    
         
            -
                      end
         
     | 
| 
       50 
     | 
    
         
            -
                    end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                    # Call each before hook, and if they all succeed, yield.
         
     | 
| 
       53 
     | 
    
         
            -
                    # If they don't all succeed, call after_ for each one that succeeded.
         
     | 
| 
       54 
     | 
    
         
            -
                    def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
         
     | 
| 
       55 
     | 
    
         
            -
                      begin
         
     | 
| 
       56 
     | 
    
         
            -
                        successful = []
         
     | 
| 
       57 
     | 
    
         
            -
                        instrumenters.each do |instrumenter|
         
     | 
| 
       58 
     | 
    
         
            -
                          instrumenter.public_send(before_hook_name, object)
         
     | 
| 
       59 
     | 
    
         
            -
                          successful << instrumenter
         
     | 
| 
       60 
     | 
    
         
            -
                        end
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                        # if any before hooks raise an exception, quit calling before hooks,
         
     | 
| 
       63 
     | 
    
         
            -
                        # but call the after hooks on anything that succeeded but also
         
     | 
| 
       64 
     | 
    
         
            -
                        # raise the exception that came from the before hook.
         
     | 
| 
       65 
     | 
    
         
            -
                      rescue GraphQL::ExecutionError => err
         
     | 
| 
       66 
     | 
    
         
            -
                        object.context.errors << err
         
     | 
| 
       67 
     | 
    
         
            -
                      rescue => e
         
     | 
| 
       68 
     | 
    
         
            -
                        raise call_after_hooks(successful, object, after_hook_name, e)
         
     | 
| 
       69 
     | 
    
         
            -
                      end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                      begin
         
     | 
| 
       72 
     | 
    
         
            -
                        yield # Call the user code
         
     | 
| 
       73 
     | 
    
         
            -
                      ensure
         
     | 
| 
       74 
     | 
    
         
            -
                        ex = call_after_hooks(successful, object, after_hook_name, nil)
         
     | 
| 
       75 
     | 
    
         
            -
                        raise ex if ex
         
     | 
| 
       76 
     | 
    
         
            -
                      end
         
     | 
| 
       77 
     | 
    
         
            -
                    end
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                    def call_after_hooks(instrumenters, object, after_hook_name, ex)
         
     | 
| 
       80 
     | 
    
         
            -
                      instrumenters.reverse_each do |instrumenter|
         
     | 
| 
       81 
     | 
    
         
            -
                        begin
         
     | 
| 
       82 
     | 
    
         
            -
                          instrumenter.public_send(after_hook_name, object)
         
     | 
| 
       83 
     | 
    
         
            -
                        rescue => e
         
     | 
| 
       84 
     | 
    
         
            -
                          ex = e
         
     | 
| 
       85 
     | 
    
         
            -
                        end
         
     | 
| 
       86 
     | 
    
         
            -
                      end
         
     | 
| 
       87 
     | 
    
         
            -
                      ex
         
     | 
| 
       88 
     | 
    
         
            -
                    end
         
     | 
| 
       89 
     | 
    
         
            -
                  end
         
     | 
| 
       90 
     | 
    
         
            -
                end
         
     | 
| 
       91 
     | 
    
         
            -
              end
         
     | 
| 
       92 
     | 
    
         
            -
            end
         
     |