graphql 2.6.1 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +29 -13
  3. data/lib/graphql/backtrace/table.rb +10 -1
  4. data/lib/graphql/current.rb +7 -1
  5. data/lib/graphql/dataloader.rb +1 -1
  6. data/lib/graphql/execution/directive_checks.rb +2 -0
  7. data/lib/graphql/execution/field_resolve_step.rb +178 -65
  8. data/lib/graphql/execution/finalize.rb +21 -8
  9. data/lib/graphql/execution/input_values.rb +110 -38
  10. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
  11. data/lib/graphql/execution/interpreter/runtime.rb +36 -15
  12. data/lib/graphql/execution/load_argument_step.rb +41 -3
  13. data/lib/graphql/execution/next.rb +20 -12
  14. data/lib/graphql/execution/prepare_object_step.rb +24 -5
  15. data/lib/graphql/execution/resolve_type_step.rb +27 -0
  16. data/lib/graphql/execution/runner.rb +65 -30
  17. data/lib/graphql/execution/selections_step.rb +1 -1
  18. data/lib/graphql/execution.rb +8 -1
  19. data/lib/graphql/execution_error.rb +6 -12
  20. data/lib/graphql/introspection/entry_points.rb +2 -2
  21. data/lib/graphql/introspection/schema_type.rb +6 -2
  22. data/lib/graphql/language/lexer.rb +1 -1
  23. data/lib/graphql/language/parser.rb +1 -1
  24. data/lib/graphql/language.rb +8 -2
  25. data/lib/graphql/pagination/connections.rb +1 -3
  26. data/lib/graphql/query.rb +2 -2
  27. data/lib/graphql/schema/argument.rb +3 -3
  28. data/lib/graphql/schema/directive/feature.rb +4 -0
  29. data/lib/graphql/schema/directive/transform.rb +20 -0
  30. data/lib/graphql/schema/has_single_input_argument.rb +24 -13
  31. data/lib/graphql/schema/input_object.rb +4 -0
  32. data/lib/graphql/schema/interface.rb +1 -1
  33. data/lib/graphql/schema/introspection_system.rb +6 -21
  34. data/lib/graphql/schema/printer.rb +1 -1
  35. data/lib/graphql/schema/ractor_shareable.rb +1 -0
  36. data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
  37. data/lib/graphql/schema/resolver.rb +0 -7
  38. data/lib/graphql/schema/subscription.rb +53 -8
  39. data/lib/graphql/schema/timeout.rb +2 -2
  40. data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
  41. data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
  42. data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
  43. data/lib/graphql/schema/validator/format_validator.rb +3 -3
  44. data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
  45. data/lib/graphql/schema/validator/length_validator.rb +6 -6
  46. data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
  47. data/lib/graphql/schema/validator/required_validator.rb +6 -4
  48. data/lib/graphql/schema/validator.rb +9 -0
  49. data/lib/graphql/schema/visibility/profile.rb +6 -4
  50. data/lib/graphql/schema/visibility/visit.rb +1 -1
  51. data/lib/graphql/schema/visibility.rb +30 -22
  52. data/lib/graphql/schema.rb +31 -10
  53. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +6 -0
  54. data/lib/graphql/subscriptions/event.rb +0 -1
  55. data/lib/graphql/tracing/perfetto_trace.rb +5 -3
  56. data/lib/graphql/version.rb +1 -1
  57. metadata +3 -2
@@ -38,6 +38,7 @@ module GraphQL
38
38
  def argument_values(owner_defn, argument_nodes, field_resolve_step)
39
39
  arg_defns = @query.types.arguments(owner_defn)
40
40
  argument_values = {}
41
+ errors = nil
41
42
 
42
43
  arg_defns.each do |argument_definition|
43
44
  arg_ruby_key = argument_definition.keyword
@@ -52,11 +53,12 @@ module GraphQL
52
53
  arg_value = value_from_ast(arg_node.value, argument_definition.type)
53
54
  argument_value(argument_values, arg_ruby_key, argument_definition, arg_value, nil, field_resolve_step)
54
55
  end
56
+ rescue GraphQL::RuntimeError => exec_err
57
+ errors ||= []
58
+ errors << exec_err
55
59
  end
56
60
 
57
- argument_values
58
- rescue GraphQL::ExecutionError => exec_err
59
- exec_err
61
+ return argument_values, errors
60
62
  end
61
63
 
62
64
  private
@@ -82,20 +84,43 @@ module GraphQL
82
84
  elsif type.kind.input_object?
83
85
  coerced_obj = {}
84
86
 
85
- @query.types.arguments(type).each do |arg|
86
- arg_key = arg.keyword
87
- if value.key?(arg.graphql_name)
88
- arg_value = value[arg.graphql_name]
89
- elsif value.key?(sym_name = arg.graphql_name.to_sym)
90
- arg_value = value[sym_name]
91
- elsif arg.default_value?
92
- coerced_obj[arg_key] = arg.default_value
93
- next
94
- else
95
- next
96
- end
87
+ if value.is_a?(Hash)
88
+ @query.types.arguments(type).each do |arg|
89
+ arg_key = arg.keyword
90
+ if value.key?(arg.graphql_name)
91
+ arg_value = value[arg.graphql_name]
92
+ elsif value.key?(sym_name = arg.graphql_name.to_sym)
93
+ arg_value = value[sym_name]
94
+ elsif arg.default_value?
95
+ coerced_obj[arg_key] = arg.default_value
96
+ next
97
+ else
98
+ next
99
+ end
100
+
101
+ if arg_value.nil? && arg.replace_null_with_default?
102
+ arg_value = arg.default_value
103
+ end
97
104
 
98
- coerced_obj[arg_key] = variable_value(arg_value, arg.type)
105
+ coerced_obj[arg_key] = variable_value(arg_value, arg.type)
106
+ end
107
+ else
108
+ @query.types.arguments(type).each do |arg|
109
+ arg_key = arg.keyword
110
+ arg_name = arg.graphql_name
111
+ if (v_node = value.arguments.find { |a| a.name == arg_name }) # rubocop:disable Development/ContextIsPassedCop
112
+ arg_value = v_node.value
113
+ coerced_obj[arg_key] = if arg_value.nil? && arg.replace_null_with_default?
114
+ arg.default_value
115
+ else
116
+ variable_value(arg_value, arg.type)
117
+ end
118
+ elsif arg.default_value?
119
+ coerced_obj[arg_key] = arg.default_value
120
+ else
121
+ # Nothing
122
+ end
123
+ end
99
124
  end
100
125
 
101
126
  coerced_obj
@@ -109,11 +134,18 @@ module GraphQL
109
134
  def argument_value(argument_values, argument_key, argument_definition, arg_value, override_type, field_resolve_step)
110
135
  treat_as_type = override_type || argument_definition.type
111
136
  if treat_as_type.non_null?
137
+ if arg_value.nil?
138
+ treat_as_type.coerce_input(arg_value, @query.context)
139
+ end
112
140
  treat_as_type = treat_as_type.of_type
113
141
  end
114
142
 
143
+ if arg_value.nil? && argument_definition.replace_null_with_default?
144
+ arg_value = argument_definition.default_value
145
+ end
146
+
115
147
  if treat_as_type.kind.list? && !arg_value.nil?
116
- inner_t = treat_as_type.unwrap
148
+ inner_t = treat_as_type.of_type
117
149
  arg_value = if arg_value.is_a?(Array)
118
150
  values = Array.new(arg_value.size)
119
151
  arg_value.each_with_index { |inner_v, idx| argument_value(values, idx, argument_definition, inner_v, inner_t, field_resolve_step)}
@@ -150,7 +182,7 @@ module GraphQL
150
182
  arg_value = begin
151
183
  argument_definition.prepare_value(nil, arg_value, context: @query.context)
152
184
  rescue StandardError => err
153
- @runner.schema.handle_or_reraise(@query.context, err)
185
+ @runner.schema.handle_or_reraise(@query.context, err, object: nil, arguments: argument_values, field: field_resolve_step&.field_definition)
154
186
  end
155
187
  end
156
188
 
@@ -205,8 +237,6 @@ module GraphQL
205
237
  nil
206
238
  elsif value_node.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
207
239
  variable_values[value_node.name]
208
- elsif value_node.is_a?(GraphQL::Language::Nodes::NullValue)
209
- nil
210
240
  elsif type.list?
211
241
  inner_type = type.of_type
212
242
  if value_node.is_a?(Array)
@@ -214,37 +244,53 @@ module GraphQL
214
244
  value_from_ast(inner_value_node, inner_type)
215
245
  end
216
246
  coerced_items.freeze
247
+ elsif value_node.is_a?(Language::Nodes::NullValue)
248
+ nil
217
249
  else
218
250
  item_value = value_from_ast(value_node, inner_type)
219
251
  [item_value].freeze
220
252
  end
221
-
222
253
  elsif type.kind.input_object?
223
254
  coerced_obj = {}
224
- arg_nodes_by_name = value_node.arguments.each_with_object({}) do |arg_node, acc| # rubocop:disable Development/ContextIsPassedCop
225
- acc[arg_node.name] = arg_node
226
- end
227
-
228
- @query.types.arguments(type).each do |arg|
229
- arg_node = arg_nodes_by_name[arg.graphql_name]
230
- arg_key = arg.keyword
231
- if arg_node.nil? || (arg_node.value.is_a?(Language::Nodes::VariableIdentifier) && !variable_values.key?(arg_node.value.name))
232
- if arg.default_value?
233
- coerced_obj[arg_key] = arg.default_value
255
+ # TODO manually handle NullValue here?
256
+ if value_node.is_a?(Hash)
257
+ @query.types.arguments(type).each do |arg|
258
+ arg_value = value_node[arg.keyword]
259
+ arg_key = arg.keyword
260
+ if arg_value.nil?
261
+ if arg.default_value?
262
+ coerced_obj[arg_key] = arg.default_value
263
+ end
264
+ next
234
265
  end
235
- next
266
+
267
+ coerced_obj[arg_key] = value_from_ast(arg_value, arg.type)
268
+ end
269
+ else
270
+ arg_nodes_by_name = value_node.arguments.each_with_object({}) do |arg_node, acc| # rubocop:disable Development/ContextIsPassedCop
271
+ acc[arg_node.name] = arg_node
236
272
  end
237
273
 
238
- arg_value = value_from_ast(arg_node.value, arg.type)
239
- coerced_obj[arg_key] = arg_value
274
+ @query.types.arguments(type).each do |arg|
275
+ arg_node = arg_nodes_by_name[arg.graphql_name]
276
+ arg_key = arg.keyword
277
+ if arg_node.nil? || (arg_node.value.is_a?(Language::Nodes::VariableIdentifier) && !variable_values.key?(arg_node.value.name))
278
+ if arg.default_value?
279
+ coerced_obj[arg_key] = arg.default_value
280
+ end
281
+ next
282
+ end
283
+
284
+ arg_value = value_from_ast(arg_node.value, arg.type)
285
+ coerced_obj[arg_key] = arg_value
286
+ end
240
287
  end
241
288
 
289
+
242
290
  coerced_obj
243
291
  elsif type.kind.leaf?
244
- if type.kind.enum?
245
- if value_node.is_a?(GraphQL::Language::Nodes::Enum)
246
- value_node = value_node.name
247
- end
292
+ if value_node.is_a?(Language::Nodes::AbstractNode) || value_node.is_a?(Array)
293
+ value_node = coerce_untyped_input(value_node)
248
294
  end
249
295
 
250
296
  begin
@@ -256,6 +302,32 @@ module GraphQL
256
302
  raise "Unexpected input type: #{type.to_type_signature}."
257
303
  end
258
304
  end
305
+
306
+ private
307
+
308
+ def coerce_untyped_input(input_value)
309
+ case input_value
310
+ when Language::Nodes::AbstractNode
311
+ case input_value
312
+ when Language::Nodes::NullValue
313
+ nil
314
+ when Language::Nodes::Enum
315
+ input_value.name
316
+ when Language::Nodes::InputObject
317
+ value_h = {}
318
+ input_value.arguments.each do |arg| # rubocop:disable Development/ContextIsPassedCop
319
+ value_h[arg.name] = coerce_untyped_input(arg.value)
320
+ end
321
+ value_h
322
+ else
323
+ raise "Unhandled untyped input AST node: #{input_value.class}"
324
+ end
325
+ when Array
326
+ input_value.map { |v| coerce_untyped_input(v) }
327
+ else
328
+ input_value
329
+ end
330
+ end
259
331
  end
260
332
  end
261
333
  end
@@ -30,7 +30,10 @@ module GraphQL
30
30
  @storage[argument_owner][parent_object][ast_node] = resolved_args
31
31
  end
32
32
  end
33
+ end
33
34
 
35
+ def cached_arguments_for(ast_node, argument_owner)
36
+ @storage[argument_owner][nil][ast_node]
34
37
  end
35
38
 
36
39
  # @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
@@ -198,7 +198,7 @@ module GraphQL
198
198
 
199
199
  def each_gathered_selections(response_hash)
200
200
  ordered_result_keys = []
201
- gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
201
+ gathered_selections = gather_selections(response_hash, response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
202
202
  ordered_result_keys.uniq!
203
203
  if gathered_selections.is_a?(Array)
204
204
  gathered_selections.each do |item|
@@ -209,15 +209,13 @@ module GraphQL
209
209
  end
210
210
  end
211
211
 
212
- def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
212
+ def gather_selections(graphql_response, owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
213
213
  selections.each do |node|
214
- # Skip gathering this if the directive says so
215
- if !directives_include?(node, owner_object, owner_type)
216
- next
217
- end
218
-
219
214
  if node.is_a?(GraphQL::Language::Nodes::Field)
220
215
  response_key = node.alias || node.name
216
+ if !directives_include?(node, owner_object, owner_type, graphql_response, response_key)
217
+ next
218
+ end
221
219
  ordered_result_keys << response_key
222
220
  selections = selections_by_name[response_key]
223
221
  # if there was already a selection of this field,
@@ -234,6 +232,9 @@ module GraphQL
234
232
  selections_by_name[response_key] = node
235
233
  end
236
234
  else
235
+ if !directives_include?(node, owner_object, owner_type, graphql_response, nil)
236
+ next
237
+ end
237
238
  # This is an InlineFragment or a FragmentSpread
238
239
  if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
239
240
  next_selections = {}
@@ -255,14 +256,14 @@ module GraphQL
255
256
  type_defn = query.types.type(node.type.name)
256
257
 
257
258
  if query.types.possible_types(type_defn).include?(owner_type)
258
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
259
+ result = gather_selections(graphql_response, owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
259
260
  if !result.equal?(next_selections)
260
261
  selections_to_run = result
261
262
  end
262
263
  end
263
264
  else
264
265
  # it's an untyped fragment, definitely continue
265
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
266
+ result = gather_selections(graphql_response, owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
266
267
  if !result.equal?(next_selections)
267
268
  selections_to_run = result
268
269
  end
@@ -271,7 +272,7 @@ module GraphQL
271
272
  fragment_def = query.fragments[node.name]
272
273
  type_defn = query.types.type(fragment_def.type.name)
273
274
  if query.types.possible_types(type_defn).include?(owner_type)
274
- result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
275
+ result = gather_selections(graphql_response, owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
275
276
  if !result.equal?(next_selections)
276
277
  selections_to_run = result
277
278
  end
@@ -339,7 +340,7 @@ module GraphQL
339
340
  end
340
341
  field_name = ast_node.name
341
342
  owner_type = selections_result.graphql_result_type
342
- field_defn = query.types.field(owner_type, field_name)
343
+ field_defn = query.types.field(owner_type, field_name) || raise(GraphQL::Error, "No field definition found for #{owner_type.graphql_name}.#{field_name} (at #{ast_node.position})")
343
344
 
344
345
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
345
346
  runtime_state = get_current_runtime_state
@@ -579,7 +580,7 @@ module GraphQL
579
580
  value.path ||= current_path
580
581
  value.ast_node ||= ast_node
581
582
  context.errors << value
582
- if selection_result
583
+ if selection_result && result_name
583
584
  set_result(selection_result, result_name, nil, false, is_non_null)
584
585
  end
585
586
  end
@@ -856,11 +857,31 @@ module GraphQL
856
857
  end
857
858
 
858
859
  # Check {Schema::Directive.include?} for each directive that's present
859
- def directives_include?(node, graphql_object, parent_type)
860
+ def directives_include?(node, graphql_object, parent_type, selection_result, extra_path_part)
860
861
  node.directives.each do |dir_node|
861
862
  dir_defn = @schema_directives.fetch(dir_node.name)
862
- args = arguments(graphql_object, dir_defn, dir_node)
863
- if !dir_defn.include?(graphql_object, args, context)
863
+ raw_dir_args = arguments(nil, dir_defn, dir_node)
864
+ if !raw_dir_args.is_a?(GraphQL::ExecutionError)
865
+ begin
866
+ dir_defn.validate!(raw_dir_args, context)
867
+ rescue GraphQL::ExecutionError => err
868
+ raw_dir_args = err
869
+ end
870
+ end
871
+
872
+ if extra_path_part && raw_dir_args.is_a?(GraphQL::ExecutionError)
873
+ raw_dir_args.path = current_path + [extra_path_part]
874
+ end
875
+
876
+ dir_args = continue_value(
877
+ raw_dir_args, # value
878
+ nil, # field
879
+ false, # is_non_null
880
+ dir_node, # ast_node
881
+ nil, # result_name
882
+ selection_result
883
+ )
884
+ if dir_args == HALT || !dir_defn.include?(graphql_object, dir_args, context)
864
885
  return false
865
886
  end
866
887
  end
@@ -10,18 +10,42 @@ module GraphQL
10
10
  @argument_definition = argument_definition
11
11
  @argument_key = argument_key
12
12
  @loaded_value = nil
13
+ @is_authorized = true
13
14
  end
14
15
 
15
16
  def value
16
- @loaded_value = @field_resolve_step.sync(@loaded_value)
17
+ @field_resolve_step.set_current_field
18
+ schema = @field_resolve_step.runner.schema
19
+ @loaded_value = schema.sync_lazy(@loaded_value)
17
20
  assign_value
21
+ rescue GraphQL::UnauthorizedError => auth_err
22
+ @is_authorized = false
23
+ schema.unauthorized_object(auth_err)
24
+ rescue GraphQL::RuntimeError => err
25
+ @loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
26
+ err.unsubscribed_result
27
+ else
28
+ err
29
+ end
30
+ assign_value
31
+ rescue StandardError => stderr
32
+ begin
33
+ @field_resolve_step.selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
34
+ rescue GraphQL::ExecutionError => ex_err
35
+ @loaded_value = ex_err
36
+ end
37
+ assign_value
38
+ ensure
39
+ @field_resolve_step.set_current_field(nil)
18
40
  end
19
41
 
20
42
  def call
43
+ @field_resolve_step.set_current_field
21
44
  context = @field_resolve_step.selections_step.query.context
22
45
  @loaded_value = begin
23
46
  @load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
24
47
  rescue GraphQL::UnauthorizedError => auth_err
48
+ @is_authorized = false
25
49
  context.schema.unauthorized_object(auth_err)
26
50
  end
27
51
  if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
@@ -30,15 +54,22 @@ module GraphQL
30
54
  assign_value
31
55
  end
32
56
  rescue GraphQL::RuntimeError => err
33
- @loaded_value = err
57
+ @loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
58
+ @is_authorized = false
59
+ err.unsubscribed_result
60
+ else
61
+ err
62
+ end
34
63
  assign_value
35
64
  rescue StandardError => stderr
36
65
  @loaded_value = begin
37
- context.query.handle_or_reraise(stderr)
66
+ context.query.handle_or_reraise(stderr, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: nil) # rubocop:disable Development/ContextIsPassedCop
38
67
  rescue GraphQL::ExecutionError => ex_err
39
68
  ex_err
40
69
  end
41
70
  assign_value
71
+ ensure
72
+ @field_resolve_step.set_current_field(nil)
42
73
  end
43
74
 
44
75
  private
@@ -47,6 +78,13 @@ module GraphQL
47
78
  if @loaded_value.is_a?(GraphQL::RuntimeError)
48
79
  @loaded_value.path = @field_resolve_step.path
49
80
  @field_resolve_step.arguments = @loaded_value
81
+ elsif @is_authorized == false
82
+ # An unauthorized_object hook ate the error
83
+ @field_resolve_step.arguments = EmptyObjects::EMPTY_HASH
84
+ field_pending_steps = @field_resolve_step.pending_steps
85
+ field_pending_steps.clear
86
+ @field_resolve_step.build_errors_result(nil, nil)
87
+ return
50
88
  else
51
89
  query = @field_resolve_step.selections_step.query
52
90
  query.current_trace.object_loaded(@argument_definition, @loaded_value, query.context)
@@ -4,15 +4,27 @@ require "graphql/execution/input_values"
4
4
  require "graphql/execution/field_resolve_step"
5
5
  require "graphql/execution/finalize"
6
6
  require "graphql/execution/load_argument_step"
7
+ require "graphql/execution/resolve_type_step"
7
8
  require "graphql/execution/runner"
8
9
  require "graphql/execution/selections_step"
9
10
  module GraphQL
10
11
  module Execution
11
12
  module Finalizer
12
13
  attr_accessor :path
14
+
13
15
  def finalize_graphql_result(query, result_data, result_key)
14
- raise RequiredImplementationMissingError
16
+ raise RequiredImplementationMissingError, "#{self.class} must implement #finalize_graphql_result(query, result_data, result_key)\n\nresult_data: #{result_data}\nresult_key: #{result_key.inspect}"
17
+ end
18
+
19
+ def ast_node
20
+ ast_nodes&.first
21
+ end
22
+
23
+ def ast_node=(new_node)
24
+ @ast_nodes = [new_node]
15
25
  end
26
+
27
+ attr_accessor :ast_nodes
16
28
  end
17
29
 
18
30
  module HaltExecution
@@ -26,7 +38,7 @@ module GraphQL
26
38
 
27
39
  module Next
28
40
  module SchemaExtension
29
- def execute_next(query_str = nil, context: nil, document: nil, operation_name: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
41
+ def execute_next(query_str = nil, query: nil, subscription_topic: nil, context: nil, document: nil, operation_name: nil, variables: nil, warden: nil, root_value: nil, validate: true, visibility_profile: nil)
30
42
  multiplex_context = if context
31
43
  {
32
44
  backtrace: context[:backtrace],
@@ -39,7 +51,8 @@ module GraphQL
39
51
  {}
40
52
  end
41
53
  query_opts = {
42
- query: query_str,
54
+ query: query || query_str,
55
+ subscription_topic: subscription_topic,
43
56
  document: document,
44
57
  context: context,
45
58
  validate: validate,
@@ -47,6 +60,7 @@ module GraphQL
47
60
  root_value: root_value,
48
61
  operation_name: operation_name,
49
62
  visibility_profile: visibility_profile,
63
+ warden: warden,
50
64
  }
51
65
  m_results = multiplex_next([query_opts], context: multiplex_context, max_complexity: nil)
52
66
  m_results[0]
@@ -55,17 +69,11 @@ module GraphQL
55
69
  def multiplex_next(query_options, context: {}, max_complexity: self.max_complexity)
56
70
  Next.run_all(self, query_options, context: context, max_complexity: max_complexity)
57
71
  end
58
-
59
- def execution_next_options
60
- @execution_next_options || find_inherited_value(:execution_next_options, EmptyObjects::EMPTY_HASH)
61
- end
62
-
63
- attr_writer :execution_next_options
64
72
  end
65
73
 
66
- def self.use(schema, authorization: true)
74
+ def self.use(schema, as_default: false)
67
75
  schema.extend(SchemaExtension)
68
- schema.execution_next_options = { authorization: authorization }
76
+ schema.default_execution_next(as_default)
69
77
  end
70
78
 
71
79
  def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
@@ -82,7 +90,7 @@ module GraphQL
82
90
  query
83
91
  end
84
92
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
85
- runner = Runner.new(multiplex, **schema.execution_next_options)
93
+ runner = Runner.new(multiplex)
86
94
  runner.execute
87
95
  end
88
96
  end
@@ -19,6 +19,7 @@ module GraphQL
19
19
  end
20
20
 
21
21
  def value
22
+ @field_resolve_step.set_current_field
22
23
  if @authorized_value
23
24
  query = @field_resolve_step.selections_step.query
24
25
  query.current_trace.begin_authorized(@resolved_type, @object, query.context)
@@ -28,21 +29,30 @@ module GraphQL
28
29
  ctx = @field_resolve_step.selections_step.query.context
29
30
  st = @field_resolve_step.static_type
30
31
  ctx.query.current_trace.begin_resolve_type(st, @object, ctx)
31
- @resolved_type, _ignored_value = @field_resolve_step.sync(@resolved_type)
32
+ @resolved_type, new_value = @field_resolve_step.sync(@resolved_type)
33
+ ResolveTypeStep.assert_valid_resolved_type(st, @resolved_type, new_value, @field_resolve_step)
34
+ if new_value
35
+ @object = new_value
36
+ end
32
37
  ctx.query.current_trace.end_resolve_type(st, @object, ctx, @resolved_type)
33
38
  end
34
39
  @runner.add_step(self)
40
+ ensure
41
+ @field_resolve_step.set_current_field(nil)
35
42
  end
36
43
 
37
44
  def call
45
+ @field_resolve_step.set_current_field
38
46
  case @next_step
39
47
  when :resolve_type
40
48
  static_type = @field_resolve_step.static_type
41
49
  if static_type.kind.abstract?
42
- ctx = @field_resolve_step.selections_step.query.context
43
- ctx.query.current_trace.begin_resolve_type(static_type, @object, ctx)
44
- @resolved_type, _ignored_value = @runner.schema.resolve_type(static_type, @object, ctx)
45
- ctx.query.current_trace.end_resolve_type(static_type, @object, ctx, @resolved_type)
50
+ query = @field_resolve_step.selections_step.query
51
+ @resolved_type, new_value = ResolveTypeStep.resolve_type(static_type, @object, query)
52
+ if new_value
53
+ ResolveTypeStep.assert_valid_resolved_type(static_type, @resolved_type, new_value, @field_resolve_step)
54
+ @object = new_value
55
+ end
46
56
  else
47
57
  @resolved_type = static_type
48
58
  end
@@ -59,6 +69,8 @@ module GraphQL
59
69
  else
60
70
  raise ArgumentError, "This is a bug, unknown step: #{@next_step.inspect}"
61
71
  end
72
+ ensure
73
+ @field_resolve_step.set_current_field(nil)
62
74
  end
63
75
 
64
76
  def authorize
@@ -85,6 +97,13 @@ module GraphQL
85
97
  end
86
98
  rescue GraphQL::RuntimeError => err
87
99
  @graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
100
+ rescue StandardError => err
101
+ query ||= @field_resolve_step.selections_step.query
102
+ begin
103
+ query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
104
+ rescue GraphQL::RuntimeError => err
105
+ @graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
106
+ end
88
107
  end
89
108
 
90
109
  def create_result
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class ResolveTypeStep
5
+ def self.resolve_type(type, object, query)
6
+ query.current_trace.begin_resolve_type(type, object, query.context)
7
+ resolved_type_response = query.resolve_type(type, object)
8
+ resolved_type = if resolved_type_response.is_a?(Array)
9
+ resolved_type_response.first
10
+ else
11
+ resolved_type_response
12
+ end
13
+ query.current_trace.end_resolve_type(type, object, query.context, resolved_type)
14
+ resolved_type_response
15
+ end
16
+
17
+ def self.assert_valid_resolved_type(abstract_type, resolved_type, object, field_resolution_step, query: field_resolution_step.selections_step.query)
18
+ possible_types = query.types.possible_types(abstract_type)
19
+ if !possible_types.include?(resolved_type)
20
+ err_class = abstract_type::UnresolvedTypeError
21
+ type_error = err_class.new(object, field_resolution_step.field_definition, abstract_type, resolved_type, possible_types)
22
+ query.schema.type_error(type_error, query.context)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end