graphql 2.5.4 → 2.5.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fcafc24832a376132e4f15c981133f693685ad070918284587f775d05dd44d7
4
- data.tar.gz: 41bba1c56371a6d4ad643f75cece09cc13474dcf02b24dce0b31a1c8c89ee480
3
+ metadata.gz: f669e7593a3d951ee74480991e79d6a10fcd4b069746cd6244897b32fb6373fa
4
+ data.tar.gz: decb90c238b320f288b9a672b068e543745b9f46f82249fe93771230f8096257
5
5
  SHA512:
6
- metadata.gz: 308136d27234edd6c84310f1e18d5a5841cdbcbf7ca8fe56eb9dd070fa52c10ad6d4d46022b51d22fe4adbcae9f7428ea6a6d7b756eef5d2be1f1337393a3f3b
7
- data.tar.gz: a0a3027d2f155daa8e52156765f6f6dcd53b35057ba010cc9e0ece130c283e2a5292c020eee81882074b6e7524c0085670dba9d47337619279915df18e097655
6
+ metadata.gz: f1afa8434158b861073704a39b1ce84ee76c2bd636669c734ab43af66cf15c396ddaadc2f7c1dcf1fa61c030a0bddc0fc362f6db1bc6ad144efcaa61142565fd
7
+ data.tar.gz: ef88e624f49aafc803c6acfef8e44e1336f3494ce83bdf5b074825450cc6bc10eafd5738b012e177ceb5581c0425b9e93466e84d50bcec28b00169dbbc82c9b5
@@ -28,14 +28,14 @@ module GraphQL
28
28
  end
29
29
  when nil
30
30
  subject.logger.warn <<~GRAPHQL
31
- GraphQL-Ruby's complexity cost system is getting some "breaking fixes" in a future version. See the migration notes at https://graphql-ruby.org/api-docs/#{GraphQL::VERSION}/Schema.html#complexity_cost_cacluation_mode-class_method
31
+ GraphQL-Ruby's complexity cost system is getting some "breaking fixes" in a future version. See the migration notes at https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#complexity_cost_calculation_mode_for-class_method
32
32
 
33
33
  To opt into the future behavior, configure your schema (#{subject.schema.name ? subject.schema.name : subject.schema.ancestors}) with:
34
34
 
35
35
  complexity_cost_calculation_mode(:future) # or `:legacy`, `:compare`
36
36
 
37
37
  GRAPHQL
38
- max_possible_complexity
38
+ max_possible_complexity(mode: :legacy)
39
39
  else
40
40
  raise ArgumentError, "Expected `:future`, `:legacy`, `:compare`, or `nil` from `#{query.schema}.complexity_cost_calculation_mode_for` but got: #{query.schema.complexity_cost_calculation_mode.inspect}"
41
41
  end
@@ -21,15 +21,25 @@ module GraphQL
21
21
  @graphql_metadata = nil
22
22
  @graphql_selections = selections
23
23
  @graphql_is_eager = is_eager
24
+ @base_path = nil
24
25
  end
25
26
 
27
+ # TODO test full path in Partial
28
+ attr_writer :base_path
29
+
26
30
  def path
27
31
  @path ||= build_path([])
28
32
  end
29
33
 
30
34
  def build_path(path_array)
31
35
  graphql_result_name && path_array.unshift(graphql_result_name)
32
- @graphql_parent ? @graphql_parent.build_path(path_array) : path_array
36
+ if @graphql_parent
37
+ @graphql_parent.build_path(path_array)
38
+ elsif @base_path
39
+ @base_path + path_array
40
+ else
41
+ path_array
42
+ end
33
43
  end
34
44
 
35
45
  attr_accessor :graphql_dead
@@ -57,53 +57,142 @@ module GraphQL
57
57
  end
58
58
 
59
59
  def final_result
60
- @response && @response.graphql_result_data
60
+ @response.respond_to?(:graphql_result_data) ? @response.graphql_result_data : @response
61
61
  end
62
62
 
63
63
  def inspect
64
64
  "#<#{self.class.name} response=#{@response.inspect}>"
65
65
  end
66
66
 
67
- # This _begins_ the execution. Some deferred work
68
- # might be stored up in lazies.
69
67
  # @return [void]
70
68
  def run_eager
71
- root_operation = query.selected_operation
72
- root_op_type = root_operation.operation_type || "query"
73
- root_type = schema.root_type_for_operation(root_op_type)
74
- runtime_object = root_type.wrap(query.root_value, context)
75
- runtime_object = schema.sync_lazy(runtime_object)
76
- is_eager = root_op_type == "mutation"
77
- @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
78
- st = get_current_runtime_state
79
- st.current_result = @response
80
-
81
- if runtime_object.nil?
82
- # Root .authorized? returned false.
83
- @response = nil
69
+ root_type = query.root_type
70
+ case query
71
+ when GraphQL::Query
72
+ ast_node = query.selected_operation
73
+ selections = ast_node.selections
74
+ object = query.root_value
75
+ is_eager = ast_node.operation_type == "mutation"
76
+ base_path = nil
77
+ when GraphQL::Query::Partial
78
+ ast_node = query.ast_nodes.first
79
+ selections = query.ast_nodes.map(&:selections).inject(&:+)
80
+ object = query.object
81
+ is_eager = false
82
+ base_path = query.path
84
83
  else
85
- call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
- each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
87
- @response.ordered_result_keys ||= ordered_result_keys
88
- if is_selection_array
89
- selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
90
- selection_response.ordered_result_keys = ordered_result_keys
91
- final_response = @response
92
- else
93
- selection_response = @response
94
- final_response = nil
95
- end
84
+ raise ArgumentError, "Unexpected Runnable, can't execute: #{query.class} (#{query.inspect})"
85
+ end
86
+ object = schema.sync_lazy(object) # TODO test query partial with lazy root object
87
+ runtime_state = get_current_runtime_state
88
+ case root_type.kind.name
89
+ when "OBJECT"
90
+ object_proxy = root_type.wrap(object, context)
91
+ object_proxy = schema.sync_lazy(object_proxy)
92
+ if object_proxy.nil?
93
+ @response = nil
94
+ else
95
+ @response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
96
+ @response.base_path = base_path
97
+ runtime_state.current_result = @response
98
+ call_method_on_directives(:resolve, object, ast_node.directives) do
99
+ each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
100
+ @response.ordered_result_keys ||= ordered_result_keys
101
+ if is_selection_array
102
+ selection_response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
103
+ selection_response.ordered_result_keys = ordered_result_keys
104
+ final_response = @response
105
+ else
106
+ selection_response = @response
107
+ final_response = nil
108
+ end
96
109
 
97
- @dataloader.append_job {
98
- evaluate_selections(
99
- selections,
100
- selection_response,
101
- final_response,
102
- nil,
110
+ @dataloader.append_job {
111
+ evaluate_selections(
112
+ selections,
113
+ selection_response,
114
+ final_response,
115
+ nil,
116
+ )
117
+ }
118
+ end
119
+ end
120
+ end
121
+ when "LIST"
122
+ inner_type = root_type.unwrap
123
+ case inner_type.kind.name
124
+ when "SCALAR", "ENUM"
125
+ result_name = ast_node.alias || ast_node.name
126
+ owner_type = query.field_definition.owner
127
+ selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
128
+ selection_result.base_path = base_path
129
+ selection_result.ordered_result_keys = [result_name]
130
+ runtime_state = get_current_runtime_state
131
+ runtime_state.current_result = selection_result
132
+ runtime_state.current_result_name = result_name
133
+ field_defn = query.field_definition
134
+ continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
135
+ if HALT != continue_value
136
+ continue_field(continue_value, owner_type, field_defn, root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
137
+ end
138
+ @response = selection_result[result_name]
139
+ else
140
+ @response = GraphQLResultArray.new(nil, root_type, nil, nil, false, selections, false, ast_node, nil, nil)
141
+ @response.base_path = base_path
142
+ idx = nil
143
+ object.each do |inner_value|
144
+ idx ||= 0
145
+ this_idx = idx
146
+ idx += 1
147
+ @dataloader.append_job do
148
+ runtime_state.current_result_name = this_idx
149
+ runtime_state.current_result = @response
150
+ continue_field(
151
+ inner_value, root_type, nil, inner_type, nil, @response.graphql_selections, false, object_proxy,
152
+ nil, this_idx, @response, false, runtime_state
103
153
  )
104
- }
154
+ end
155
+ end
156
+ end
157
+ when "SCALAR", "ENUM"
158
+ result_name = ast_node.alias || ast_node.name
159
+ owner_type = query.field_definition.owner
160
+ selection_result = GraphQLResultHash.new(nil, query.parent_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
161
+ selection_result.ordered_result_keys = [result_name]
162
+ selection_result.base_path = base_path
163
+ runtime_state = get_current_runtime_state
164
+ runtime_state.current_result = selection_result
165
+ runtime_state.current_result_name = result_name
166
+ field_defn = query.field_definition
167
+ continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
168
+ if HALT != continue_value
169
+ continue_field(continue_value, owner_type, field_defn, query.root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
170
+ end
171
+ @response = selection_result[result_name]
172
+ when "UNION", "INTERFACE"
173
+ resolved_type, _resolved_obj = resolve_type(root_type, object)
174
+ resolved_type = schema.sync_lazy(resolved_type)
175
+ object_proxy = resolved_type.wrap(object, context)
176
+ object_proxy = schema.sync_lazy(object_proxy)
177
+ @response = GraphQLResultHash.new(nil, resolved_type, object_proxy, nil, false, selections, false, query.ast_nodes.first, nil, nil)
178
+ @response.base_path = base_path
179
+ each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
180
+ @response.ordered_result_keys ||= ordered_result_keys
181
+ if is_selection_array == true
182
+ raise "This isn't supported yet"
105
183
  end
184
+
185
+ @dataloader.append_job {
186
+ evaluate_selections(
187
+ selections,
188
+ @response,
189
+ nil,
190
+ runtime_state,
191
+ )
192
+ }
106
193
  end
194
+ else
195
+ raise "Invariant: unsupported type kind for partial execution: #{root_type.kind.inspect} (#{root_type})"
107
196
  end
108
197
  nil
109
198
  end
@@ -26,7 +26,7 @@ module GraphQL
26
26
  query = case opts
27
27
  when Hash
28
28
  schema.query_class.new(schema, nil, **opts)
29
- when GraphQL::Query
29
+ when GraphQL::Query, GraphQL::Query::Partial
30
30
  opts
31
31
  else
32
32
  raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Query
4
+ # This class is _like_ a {GraphQL::Query}, except it can run on an arbitrary path within a query string.
5
+ #
6
+ # It depends on a "parent" {Query}.
7
+ #
8
+ # During execution, it calls query-related tracing hooks but passes itself as `query:`.
9
+ #
10
+ # The {Partial} will use your {Schema.resolve_type} hook to find the right GraphQL type to use for
11
+ # `object` in some cases.
12
+ #
13
+ # @see Query#run_partials Run via {Query#run_partials}
14
+ class Partial
15
+ include Query::Runnable
16
+
17
+ # @param path [Array<String, Integer>] A path in `query.query_string` to start executing from
18
+ # @param object [Object] A starting object for execution
19
+ # @param query [GraphQL::Query] A full query instance that this partial is based on. Caches are shared.
20
+ # @param context [Hash] Extra context values to merge into `query.context`, if provided
21
+ def initialize(path:, object:, query:, context: nil)
22
+ @path = path
23
+ @object = object
24
+ @query = query
25
+ @schema = query.schema
26
+ context_vals = @query.context.to_h
27
+ if context
28
+ context_vals = context_vals.merge(context)
29
+ end
30
+ @context = GraphQL::Query::Context.new(query: self, schema: @query.schema, values: context_vals)
31
+ @multiplex = nil
32
+ @result_values = nil
33
+ @result = nil
34
+ selections = [@query.selected_operation]
35
+ type = @query.root_type
36
+ parent_type = nil
37
+ field_defn = nil
38
+ @path.each do |name_in_doc|
39
+ if name_in_doc.is_a?(Integer)
40
+ if type.list?
41
+ type = type.unwrap
42
+ next
43
+ else
44
+ raise ArgumentError, "Received path with index `#{name_in_doc}`, but type wasn't a list. Type: #{type.to_type_signature}, path: #{@path}"
45
+ end
46
+ end
47
+
48
+ next_selections = []
49
+ selections.each do |selection|
50
+ selections_to_check = []
51
+ selections_to_check.concat(selection.selections)
52
+ while (sel = selections_to_check.shift)
53
+ case sel
54
+ when GraphQL::Language::Nodes::InlineFragment
55
+ selections_to_check.concat(sel.selections)
56
+ when GraphQL::Language::Nodes::FragmentSpread
57
+ fragment = @query.fragments[sel.name]
58
+ selections_to_check.concat(fragment.selections)
59
+ when GraphQL::Language::Nodes::Field
60
+ if sel.alias == name_in_doc || sel.name == name_in_doc
61
+ next_selections << sel
62
+ end
63
+ else
64
+ raise "Unexpected selection in partial path: #{sel.class}, #{sel.inspect}"
65
+ end
66
+ end
67
+ end
68
+
69
+ if next_selections.empty?
70
+ raise ArgumentError, "Path `#{@path.inspect}` is not present in this query. `#{name_in_doc.inspect}` was not found. Try a different path or rewrite the query to include it."
71
+ end
72
+ field_name = next_selections.first.name
73
+ field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
74
+ parent_type = type
75
+ type = field_defn.type
76
+ if type.non_null?
77
+ type = type.of_type
78
+ end
79
+ selections = next_selections
80
+ end
81
+ @parent_type = parent_type
82
+ @ast_nodes = selections
83
+ @root_type = type
84
+ @field_definition = field_defn
85
+ @leaf = @root_type.unwrap.kind.leaf?
86
+ end
87
+
88
+ def leaf?
89
+ @leaf
90
+ end
91
+
92
+ attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :parent_type, :schema
93
+
94
+ attr_accessor :multiplex, :result_values
95
+
96
+ class Result < GraphQL::Query::Result
97
+ def path
98
+ @query.path
99
+ end
100
+
101
+ # @return [GraphQL::Query::Partial]
102
+ def partial
103
+ @query
104
+ end
105
+ end
106
+
107
+ def result
108
+ @result ||= Result.new(query: self, values: result_values)
109
+ end
110
+
111
+ def current_trace
112
+ @query.current_trace
113
+ end
114
+
115
+ def types
116
+ @query.types
117
+ end
118
+
119
+ def resolve_type(...)
120
+ @query.resolve_type(...)
121
+ end
122
+
123
+ def variables
124
+ @query.variables
125
+ end
126
+
127
+ def fragments
128
+ @query.fragments
129
+ end
130
+
131
+ def valid?
132
+ @query.valid?
133
+ end
134
+
135
+ def analyzers
136
+ EmptyObjects::EMPTY_ARRAY
137
+ end
138
+
139
+ def analysis_errors=(_ignored)
140
+ # pass
141
+ end
142
+
143
+ def subscription?
144
+ @query.subscription?
145
+ end
146
+
147
+ def selected_operation
148
+ ast_nodes.first
149
+ end
150
+
151
+ def static_errors
152
+ @query.static_errors
153
+ end
154
+ end
155
+ end
156
+ end
data/lib/graphql/query.rb CHANGED
@@ -10,12 +10,47 @@ module GraphQL
10
10
  autoload :Context, "graphql/query/context"
11
11
  autoload :Fingerprint, "graphql/query/fingerprint"
12
12
  autoload :NullContext, "graphql/query/null_context"
13
+ autoload :Partial, "graphql/query/partial"
13
14
  autoload :Result, "graphql/query/result"
14
15
  autoload :Variables, "graphql/query/variables"
15
16
  autoload :InputValidationResult, "graphql/query/input_validation_result"
16
17
  autoload :VariableValidationError, "graphql/query/variable_validation_error"
17
18
  autoload :ValidationPipeline, "graphql/query/validation_pipeline"
18
19
 
20
+ # Code shared with {Partial}
21
+ module Runnable
22
+ def after_lazy(value, &block)
23
+ if !defined?(@runtime_instance)
24
+ @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
25
+ end
26
+
27
+ if @runtime_instance
28
+ @runtime_instance.minimal_after_lazy(value, &block)
29
+ else
30
+ @schema.after_lazy(value, &block)
31
+ end
32
+ end
33
+
34
+ # Node-level cache for calculating arguments. Used during execution and query analysis.
35
+ # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
36
+ # @param definition [GraphQL::Schema::Field]
37
+ # @param parent_object [GraphQL::Schema::Object]
38
+ # @return [Hash{Symbol => Object}]
39
+ def arguments_for(ast_node, definition, parent_object: nil)
40
+ arguments_cache.fetch(ast_node, definition, parent_object)
41
+ end
42
+
43
+ def arguments_cache
44
+ @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
45
+ end
46
+
47
+ # @api private
48
+ def handle_or_reraise(err)
49
+ @schema.handle_or_reraise(context, err)
50
+ end
51
+ end
52
+
53
+ include Runnable
19
54
  class OperationNameMissingError < GraphQL::ExecutionError
20
55
  def initialize(name)
21
56
  msg = if name.nil?
@@ -198,19 +233,10 @@ module GraphQL
198
233
  # @return [GraphQL::Execution::Lookahead]
199
234
  def lookahead
200
235
  @lookahead ||= begin
201
- ast_node = selected_operation
202
- if ast_node.nil?
236
+ if selected_operation.nil?
203
237
  GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
204
238
  else
205
- root_type = case ast_node.operation_type
206
- when nil, "query"
207
- types.query_root # rubocop:disable Development/ContextIsPassedCop
208
- when "mutation"
209
- types.mutation_root # rubocop:disable Development/ContextIsPassedCop
210
- when "subscription"
211
- types.subscription_root # rubocop:disable Development/ContextIsPassedCop
212
- end
213
- GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
239
+ GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation])
214
240
  end
215
241
  end
216
242
  end
@@ -236,6 +262,18 @@ module GraphQL
236
262
  with_prepared_ast { @operations }
237
263
  end
238
264
 
265
+ # Run subtree partials of this query and return their results.
266
+ # Each partial is identified with a `path:` and `object:`
267
+ # where the path references a field in the AST and the object will be treated
268
+ # as the return value from that field. Subfields of the field named by `path`
269
+ # will be executed with `object` as the starting point
270
+ # @param partials_hashes [Array<Hash{Symbol => Object}>] Hashes with `path:` and `object:` keys
271
+ # @return [Array<GraphQL::Query::Result>]
272
+ def run_partials(partials_hashes)
273
+ partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
274
+ Execution::Interpreter.run_all(@schema, partials, context: @context)
275
+ end
276
+
239
277
  # Get the result for this query, executing it once
240
278
  # @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
241
279
  def result
@@ -278,19 +316,6 @@ module GraphQL
278
316
  end
279
317
  end
280
318
 
281
- # Node-level cache for calculating arguments. Used during execution and query analysis.
282
- # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
283
- # @param definition [GraphQL::Schema::Field]
284
- # @param parent_object [GraphQL::Schema::Object]
285
- # @return [Hash{Symbol => Object}]
286
- def arguments_for(ast_node, definition, parent_object: nil)
287
- arguments_cache.fetch(ast_node, definition, parent_object)
288
- end
289
-
290
- def arguments_cache
291
- @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
292
- end
293
-
294
319
  # A version of the given query string, with:
295
320
  # - Variables inlined to the query
296
321
  # - Strings replaced with `<REDACTED>`
@@ -357,17 +382,21 @@ module GraphQL
357
382
 
358
383
  def root_type_for_operation(op_type)
359
384
  case op_type
360
- when "query"
385
+ when "query", nil
361
386
  types.query_root # rubocop:disable Development/ContextIsPassedCop
362
387
  when "mutation"
363
388
  types.mutation_root # rubocop:disable Development/ContextIsPassedCop
364
389
  when "subscription"
365
390
  types.subscription_root # rubocop:disable Development/ContextIsPassedCop
366
391
  else
367
- raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
392
+ raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'"
368
393
  end
369
394
  end
370
395
 
396
+ def root_type
397
+ root_type_for_operation(selected_operation.operation_type)
398
+ end
399
+
371
400
  def types
372
401
  @visibility_profile || warden.visibility_profile
373
402
  end
@@ -400,23 +429,6 @@ module GraphQL
400
429
  with_prepared_ast { @subscription }
401
430
  end
402
431
 
403
- # @api private
404
- def handle_or_reraise(err)
405
- schema.handle_or_reraise(context, err)
406
- end
407
-
408
- def after_lazy(value, &block)
409
- if !defined?(@runtime_instance)
410
- @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
411
- end
412
-
413
- if @runtime_instance
414
- @runtime_instance.minimal_after_lazy(value, &block)
415
- else
416
- @schema.after_lazy(value, &block)
417
- end
418
- end
419
-
420
432
  attr_reader :logger
421
433
 
422
434
  private
@@ -176,30 +176,33 @@ module GraphQL
176
176
  return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
177
177
  end
178
178
 
179
- # Inject missing required arguments
180
- missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
181
- if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
182
- m[argument.graphql_name] = nil
183
- end
184
-
185
- m
186
- end
187
179
 
188
180
  result = nil
189
- [input, missing_required_inputs].each do |args_to_validate|
190
- args_to_validate.each do |argument_name, value|
191
- argument = types.argument(self, argument_name)
192
- # Items in the input that are unexpected
193
- if argument.nil?
194
- result ||= Query::InputValidationResult.new
195
- result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
196
- else
197
- # Items in the input that are expected, but have invalid values
198
- argument_result = argument.type.validate_input(value, ctx)
181
+
182
+
183
+ input.each do |argument_name, value|
184
+ argument = types.argument(self, argument_name)
185
+ # Items in the input that are unexpected
186
+ if argument.nil?
187
+ result ||= Query::InputValidationResult.new
188
+ result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
189
+ else
190
+ # Items in the input that are expected, but have invalid values
191
+ argument_result = argument.type.validate_input(value, ctx)
192
+ if !argument_result.valid?
199
193
  result ||= Query::InputValidationResult.new
200
- if !argument_result.valid?
201
- result.merge_result!(argument_name, argument_result)
202
- end
194
+ result.merge_result!(argument_name, argument_result)
195
+ end
196
+ end
197
+ end
198
+
199
+ # Check for missing non-null arguments
200
+ ctx.types.arguments(self).each do |argument|
201
+ if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value?
202
+ result ||= Query::InputValidationResult.new
203
+ argument_result = argument.type.validate_input(nil, ctx)
204
+ if !argument_result.valid?
205
+ result.merge_result!(argument.graphql_name, argument_result)
203
206
  end
204
207
  end
205
208
  end
@@ -12,7 +12,7 @@ module GraphQL
12
12
  # @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
13
13
  # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
14
14
  # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
15
- def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
15
+ def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? Rails.env.production? : nil), migration_errors: false)
16
16
  profiles&.each { |name, ctx|
17
17
  ctx[:visibility_profile] = name
18
18
  ctx.freeze
@@ -1189,7 +1189,7 @@ module GraphQL
1189
1189
  # @param context [GraphQL::Query::Context] The query context for the currently-executing query
1190
1190
  # @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
1191
1191
  def resolve_type(abstract_type, application_object, context)
1192
- raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{abstract_type.name})"
1192
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, `loads:`, or `run_partials` (tried to resolve: #{abstract_type.name})"
1193
1193
  end
1194
1194
  # rubocop:enable Lint/DuplicateMethods
1195
1195
 
@@ -137,7 +137,9 @@ module GraphQL
137
137
 
138
138
  def dataloader_fiber_resume(source)
139
139
  prev_ev = Fiber[PREVIOUS_EV_KEY]
140
- begin_notifications_event(prev_ev.name, prev_ev.payload)
140
+ if prev_ev
141
+ begin_notifications_event(prev_ev.name, prev_ev.payload)
142
+ end
141
143
  super
142
144
  end
143
145
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.5.4"
3
+ VERSION = "2.5.6"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.4
4
+ version: 2.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-18 00:00:00.000000000 Z
10
+ date: 2025-05-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -163,6 +163,20 @@ dependencies:
163
163
  - - ">="
164
164
  - !ruby/object:Gem::Version
165
165
  version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: ostruct
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
166
180
  - !ruby/object:Gem::Dependency
167
181
  name: rake
168
182
  requirement: !ruby/object:Gem::Requirement
@@ -536,6 +550,7 @@ files:
536
550
  - lib/graphql/query/fingerprint.rb
537
551
  - lib/graphql/query/input_validation_result.rb
538
552
  - lib/graphql/query/null_context.rb
553
+ - lib/graphql/query/partial.rb
539
554
  - lib/graphql/query/result.rb
540
555
  - lib/graphql/query/validation_pipeline.rb
541
556
  - lib/graphql/query/variable_validation_error.rb
@@ -801,7 +816,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
801
816
  - !ruby/object:Gem::Version
802
817
  version: '0'
803
818
  requirements: []
804
- rubygems_version: 3.6.6
819
+ rubygems_version: 3.6.8
805
820
  specification_version: 4
806
821
  summary: A GraphQL language and runtime for Ruby
807
822
  test_files: []