graphql 2.5.23 → 2.5.25

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis.rb +20 -13
  3. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  4. data/lib/graphql/execution/finalize.rb +217 -0
  5. data/lib/graphql/execution/input_values.rb +261 -0
  6. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  7. data/lib/graphql/execution/load_argument_step.rb +64 -0
  8. data/lib/graphql/execution/next.rb +23 -5
  9. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  10. data/lib/graphql/execution/runner.rb +410 -0
  11. data/lib/graphql/execution/selections_step.rb +91 -0
  12. data/lib/graphql/execution.rb +2 -2
  13. data/lib/graphql/execution_error.rb +1 -1
  14. data/lib/graphql/query/context.rb +6 -0
  15. data/lib/graphql/query/partial.rb +18 -3
  16. data/lib/graphql/query.rb +10 -1
  17. data/lib/graphql/runtime_error.rb +6 -0
  18. data/lib/graphql/schema/directive.rb +23 -9
  19. data/lib/graphql/schema/field/connection_extension.rb +2 -15
  20. data/lib/graphql/schema/field/scope_extension.rb +0 -4
  21. data/lib/graphql/schema/field.rb +20 -20
  22. data/lib/graphql/schema/field_extension.rb +11 -41
  23. data/lib/graphql/schema/interface.rb +26 -0
  24. data/lib/graphql/schema/list.rb +4 -0
  25. data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
  26. data/lib/graphql/schema/resolver.rb +2 -2
  27. data/lib/graphql/schema.rb +12 -10
  28. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +25 -25
  29. data/lib/graphql/subscriptions.rb +15 -0
  30. data/lib/graphql/tracing/trace.rb +6 -0
  31. data/lib/graphql/unauthorized_error.rb +1 -1
  32. data/lib/graphql/version.rb +1 -1
  33. data/lib/graphql.rb +1 -3
  34. metadata +10 -7
  35. data/lib/graphql/execution/next/field_resolve_step.rb +0 -743
  36. data/lib/graphql/execution/next/load_argument_step.rb +0 -64
  37. data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
  38. data/lib/graphql/execution/next/runner.rb +0 -411
  39. data/lib/graphql/execution/next/selections_step.rb +0 -37
@@ -1,743 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Execution
4
- module Next
5
- class FieldResolveStep
6
- def initialize(parent_type:, runner:, key:, selections_step:)
7
- @selections_step = selections_step
8
- @key = key
9
- @parent_type = parent_type
10
- @ast_node = @ast_nodes = nil
11
- @runner = runner
12
- @field_definition = nil
13
- @arguments = nil
14
- @field_results = nil
15
- @path = nil
16
- @enqueued_authorization = false
17
- @all_next_objects = nil
18
- @all_next_results = nil
19
- @static_type = nil
20
- @next_selections = nil
21
- @object_is_authorized = nil
22
- @finish_extension_idx = nil
23
- @was_scoped = nil
24
- @pending_steps = nil
25
- end
26
-
27
- attr_reader :ast_node, :key, :parent_type, :selections_step, :runner,
28
- :field_definition, :object_is_authorized, :was_scoped, :field_results
29
-
30
- attr_accessor :pending_steps, :arguments
31
-
32
- def path
33
- @path ||= [*@selections_step.path, @key].freeze
34
- end
35
-
36
- def ast_nodes
37
- @ast_nodes ||= [@ast_node]
38
- end
39
-
40
- def append_selection(ast_node)
41
- if @ast_node.nil?
42
- @ast_node = ast_node
43
- elsif @ast_nodes.nil?
44
- @ast_nodes = [@ast_node, ast_node]
45
- else
46
- @ast_nodes << ast_node
47
- end
48
- nil
49
- end
50
-
51
- def coerce_arguments(argument_owner, ast_arguments_or_hash, run_loads = true)
52
- arg_defns = @selections_step.query.types.arguments(argument_owner)
53
- if arg_defns.empty?
54
- return EmptyObjects::EMPTY_HASH
55
- end
56
- args_hash = {}
57
-
58
- if ast_arguments_or_hash.nil? # This can happen with `.trigger`
59
- return args_hash
60
- end
61
-
62
- arg_inputs_are_h = ast_arguments_or_hash.is_a?(Hash)
63
-
64
- arg_defns.each do |arg_defn|
65
- arg_value = nil
66
- was_found = false
67
- if arg_inputs_are_h
68
- ast_arguments_or_hash.each do |key, value|
69
- if key == arg_defn.keyword || key.to_s == arg_defn.graphql_name
70
- arg_value = value
71
- was_found = true
72
- break
73
- end
74
- end
75
- else
76
- ast_arguments_or_hash.each do |arg_node|
77
- if arg_node.name == arg_defn.graphql_name
78
- arg_value = arg_node.value
79
- was_found = true
80
- break
81
- end
82
- end
83
- end
84
-
85
- if arg_value.is_a?(Language::Nodes::VariableIdentifier)
86
- vars = @selections_step.query.variables
87
- arg_value = if vars.key?(arg_value.name)
88
- vars[arg_value.name]
89
- elsif vars.key?(arg_value.name.to_sym)
90
- vars[arg_value.name.to_sym]
91
- else
92
- was_found = false
93
- nil
94
- end
95
- end
96
-
97
- if !was_found && arg_defn.default_value?
98
- was_found = true
99
- arg_value = arg_defn.default_value
100
- end
101
-
102
- if was_found
103
- coerce_argument_value(args_hash, arg_defn, arg_value, run_loads)
104
- end
105
- end
106
-
107
- args_hash
108
- end
109
-
110
- def coerce_argument_value(arguments, arg_defn, arg_value, run_loads, target_keyword: run_loads ? arg_defn.keyword : arg_defn.graphql_name, as_type: nil)
111
- arg_t = as_type || arg_defn.type
112
- if arg_t.non_null?
113
- arg_t = arg_t.of_type
114
- end
115
-
116
- if arg_value.is_a?(Language::Nodes::VariableIdentifier)
117
- vars = @selections_step.query.variables
118
- arg_value = if vars.key?(arg_value.name)
119
- vars[arg_value.name]
120
- elsif vars.key?(arg_value.name.to_sym)
121
- vars[arg_value.name.to_sym]
122
- else
123
- nil
124
- end
125
- end
126
-
127
- if arg_value.is_a?(Language::Nodes::NullValue)
128
- arg_value = nil
129
- elsif arg_value.is_a?(Language::Nodes::Enum)
130
- arg_value = arg_value.name
131
- end
132
-
133
- ctx = @selections_step.query.context
134
- arg_value = if arg_t.list?
135
- if arg_value.nil?
136
- arg_value
137
- else
138
- arg_value = Array(arg_value)
139
- inner_t = arg_t.of_type
140
- result = Array.new(arg_value.size)
141
- arg_value.each_with_index { |v, i| coerce_argument_value(result, arg_defn, v, run_loads, target_keyword: i, as_type: inner_t) }
142
- result
143
- end
144
- elsif arg_t.kind.leaf?
145
- begin
146
- arg_t.coerce_input(arg_value, ctx)
147
- rescue GraphQL::UnauthorizedEnumValueError => enum_err
148
- begin
149
- @runner.schema.unauthorized_object(enum_err)
150
- rescue GraphQL::ExecutionError => ex_err
151
- ex_err
152
- end
153
- end
154
- elsif arg_t.kind.input_object?
155
- input_obj_vals = arg_value.is_a?(Language::Nodes::InputObject) ? arg_value.arguments : arg_value # rubocop:disable Development/ContextIsPassedCop
156
- input_obj_args = coerce_arguments(arg_t, input_obj_vals)
157
- arg_t.new(nil, ruby_kwargs: input_obj_args, context: @selections_step.query.context, defaults_used: nil)
158
- else
159
- raise "Unsupported argument value: #{arg_t.to_type_signature} / #{arg_value.class} (#{arg_value.inspect})"
160
- end
161
-
162
- if as_type.nil? # only on root arguments, not list elements
163
- arg_value = begin
164
- begin
165
- arg_defn.prepare_value(nil, arg_value, context: ctx)
166
- rescue StandardError => err
167
- @runner.schema.handle_or_reraise(ctx, err)
168
- end
169
- rescue GraphQL::ExecutionError => exec_err
170
- exec_err
171
- end
172
- end
173
-
174
- if arg_value.is_a?(GraphQL::Error)
175
- @arguments = arg_value
176
- elsif run_loads && arg_defn.loads && as_type.nil? && !arg_value.nil?
177
- # This is for legacy compat:
178
- load_receiver = if (r = @field_definition.resolver)
179
- r.new(field: @field_definition, context: @selections_step.query.context, object: nil)
180
- else
181
- @field_definition
182
- end
183
- @pending_steps ||= []
184
- if arg_t.list?
185
- results = Array.new(arg_value.size, nil)
186
- arguments[arg_defn.keyword] = results
187
- arg_value.each_with_index do |inner_v, idx|
188
- loads_step = LoadArgumentStep.new(
189
- field_resolve_step: self,
190
- load_receiver: load_receiver,
191
- argument_value: inner_v,
192
- argument_definition: arg_defn,
193
- arguments: results,
194
- argument_key: idx,
195
- )
196
- @pending_steps.push(loads_step)
197
- @runner.add_step(loads_step)
198
- end
199
- else
200
- loads_step = LoadArgumentStep.new(
201
- field_resolve_step: self,
202
- load_receiver: load_receiver,
203
- argument_value: arg_value,
204
- argument_definition: arg_defn,
205
- arguments: arguments,
206
- argument_key: arg_defn.keyword,
207
- )
208
- @pending_steps.push(loads_step)
209
- @runner.add_step(loads_step)
210
- end
211
- else
212
- arguments[target_keyword] = arg_value
213
- end
214
- nil
215
- end
216
-
217
- # Implement that Lazy API
218
- def value
219
- query = @selections_step.query
220
- query.current_trace.begin_execute_field(@field_definition, @arguments, @field_results, query)
221
- @field_results = sync(@field_results)
222
- query.current_trace.end_execute_field(@field_definition, @arguments, @field_results, query, @field_results)
223
- @runner.add_step(self)
224
- true
225
- end
226
-
227
- def sync(lazy)
228
- if lazy.is_a?(Array)
229
- lazy.map! { |l| sync(l)}
230
- else
231
- @runner.schema.sync_lazy(lazy)
232
- end
233
- rescue GraphQL::UnauthorizedError => auth_err
234
- @runner.schema.unauthorized_object(auth_err)
235
- rescue GraphQL::ExecutionError => err
236
- err
237
- rescue StandardError => stderr
238
- begin
239
- @selections_step.query.handle_or_reraise(stderr)
240
- rescue GraphQL::ExecutionError => ex_err
241
- ex_err
242
- end
243
- end
244
-
245
- def call
246
- if @enqueued_authorization
247
- enqueue_next_steps
248
- elsif @finish_extension_idx
249
- finish_extensions
250
- elsif @field_results
251
- build_results
252
- elsif @arguments
253
- execute_field
254
- else
255
- build_arguments
256
- end
257
- rescue StandardError => err
258
- if @field_definition && !err.message.start_with?("Resolving ")
259
- # TODO remove this check ^^^^^^ when NullDataloader isn't recursive
260
- raise err, "Resolving #{@field_definition.path}: #{err.message}", err.backtrace
261
- else
262
- raise
263
- end
264
- end
265
-
266
- def add_graphql_error(err)
267
- err.path = path
268
- err.ast_nodes = ast_nodes
269
- @selections_step.query.context.add_error(err)
270
- err
271
- end
272
-
273
- module AlwaysAuthorized
274
- def self.[](_key)
275
- true
276
- end
277
- end
278
-
279
- def build_arguments
280
- query = @selections_step.query
281
- field_name = @ast_node.name
282
- @field_definition = query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}")
283
- arguments = coerce_arguments(@field_definition, @ast_node.arguments) # rubocop:disable Development/ContextIsPassedCop
284
- @arguments ||= arguments # may have already been set to an error
285
-
286
- if (@pending_steps.nil? || @pending_steps.size == 0) &&
287
- @field_results.nil? # Make sure the arguments flow didn't already call through
288
- execute_field
289
- end
290
- end
291
-
292
- def execute_field
293
- objects = @selections_step.objects
294
- # TODO not as good because only one error?
295
- if @arguments.is_a?(GraphQL::Error)
296
- @field_results = Array.new(objects.size, @arguments)
297
- @object_is_authorized = AlwaysAuthorized
298
- build_results
299
- return
300
- end
301
-
302
- query = @selections_step.query
303
- ctx = query.context
304
- if (v = @field_definition.validators).any? # rubocop:disable Development/NoneWithoutBlockCop
305
- begin
306
- Schema::Validator.validate!(v, nil, ctx, @arguments)
307
- rescue GraphQL::RuntimeError => err
308
- @field_results = Array.new(objects.size, err)
309
- @object_is_authorized = AlwaysAuthorized
310
- build_results
311
- return
312
- end
313
- end
314
-
315
- @field_definition.extras.each do |extra|
316
- case extra
317
- when :lookahead
318
- if @arguments.frozen?
319
- @arguments = @arguments.dup
320
- end
321
- @arguments[:lookahead] = Execution::Lookahead.new(
322
- query: query,
323
- ast_nodes: ast_nodes,
324
- field: @field_definition,
325
- )
326
- when :ast_node
327
- if @arguments.frozen?
328
- @arguments = @arguments.dup
329
- end
330
- @arguments[:ast_node] = ast_node
331
- else
332
- raise ArgumentError, "This `extra` isn't supported yet: #{extra.inspect}. Open an issue on GraphQL-Ruby to add compatibility for it."
333
- end
334
- end
335
-
336
- if @field_definition.dynamic_introspection
337
- # TODO break this backwards compat somehow?
338
- objects = @selections_step.graphql_objects
339
- end
340
-
341
- if @runner.authorization && @runner.authorizes?(@field_definition, ctx)
342
- authorized_objects = []
343
- @object_is_authorized = objects.map { |o|
344
- is_authed = @field_definition.authorized?(o, @arguments, ctx)
345
- if is_authed
346
- authorized_objects << o
347
- else
348
- begin
349
- err = GraphQL::UnauthorizedFieldError.new(object: o, type: @parent_type, context: ctx, field: @field_definition)
350
- authorized_objects << query.schema.unauthorized_object(err)
351
- is_authed = true
352
- rescue GraphQL::ExecutionError => exec_err
353
- add_graphql_error(exec_err)
354
- end
355
- end
356
- is_authed
357
- }
358
- if authorized_objects.size == 0
359
- return
360
- end
361
- else
362
- authorized_objects = objects
363
- @object_is_authorized = AlwaysAuthorized
364
- end
365
-
366
- if @parent_type.default_relay? && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
367
- @was_scoped = true
368
- end
369
-
370
- query.current_trace.begin_execute_field(@field_definition, @arguments, authorized_objects, query)
371
- has_extensions = @field_definition.extensions.size > 0
372
- if has_extensions
373
- @extended = GraphQL::Schema::Field::ExtendedState.new(@arguments, authorized_objects)
374
- @field_results = @field_definition.run_next_extensions_before_resolve(authorized_objects, @arguments, ctx, @extended) do |objs, args|
375
- if (added_extras = @extended.added_extras)
376
- args = args.dup
377
- added_extras.each { |e| args.delete(e) }
378
- end
379
- resolve_batch(objs, ctx, args)
380
- end
381
- @finish_extension_idx = 0
382
- else
383
- @field_results = resolve_batch(authorized_objects, ctx, @arguments)
384
- end
385
-
386
- query.current_trace.end_execute_field(@field_definition, @arguments, authorized_objects, query, @field_results)
387
-
388
- if any_lazy_results?
389
- @runner.dataloader.lazy_at_depth(path.size, self)
390
- elsif has_extensions
391
- finish_extensions
392
- elsif @pending_steps.nil? || @pending_steps.empty?
393
- build_results
394
- end
395
- end
396
-
397
- def any_lazy_results?
398
- lazies = false
399
- if @runner.resolves_lazies # TODO extract this
400
- @field_results.each do |field_result|
401
- if @runner.lazy?(field_result)
402
- lazies = true
403
- break
404
- elsif field_result.is_a?(Array)
405
- field_result.each do |inner_fr|
406
- if @runner.lazy?(inner_fr)
407
- break lazies = true
408
- end
409
- end
410
- if lazies
411
- break
412
- end
413
- end
414
- end
415
- end
416
- lazies
417
- end
418
-
419
- def finish_extensions
420
- ctx = @selections_step.query.context
421
- memos = @extended.memos || EmptyObjects::EMPTY_HASH
422
- while ext = @field_definition.extensions[@finish_extension_idx]
423
- # These two are hardcoded here because of how they need to interact with runtime metadata.
424
- # It would probably be better
425
- case ext
426
- when Schema::Field::ConnectionExtension
427
- conns = ctx.schema.connections
428
- @field_results = @field_results.map.each_with_index do |value, idx|
429
- object = @extended.object[idx]
430
- conn = conns.populate_connection(@field_definition, object, value, @arguments, ctx)
431
- if conn
432
- conn.was_authorized_by_scope_items = @was_scoped
433
- end
434
- conn
435
- end
436
- when Schema::Field::ScopeExtension
437
- if @was_scoped.nil?
438
- if (rt = @field_definition.type.unwrap).respond_to?(:scope_items)
439
- @was_scoped = true
440
- @field_results = @field_results.map { |v| v.nil? ? v : rt.scope_items(v, ctx) }
441
- else
442
- @was_scoped = false
443
- end
444
- end
445
- else
446
- memo = memos[@finish_extension_idx]
447
- @field_results = ext.after_resolve_next(objects: @extended.object, arguments: @extended.arguments, context: ctx, values: @field_results, memo: memo) # rubocop:disable Development/ContextIsPassedCop
448
- end
449
- @finish_extension_idx += 1
450
- if any_lazy_results?
451
- @runner.dataloader.lazy_at_depth(path.size, self)
452
- return
453
- end
454
- end
455
-
456
- @finish_extension_idx = nil
457
- build_results
458
- end
459
-
460
- def build_results
461
- return_type = @field_definition.type
462
- return_result_type = return_type.unwrap
463
-
464
- if return_result_type.kind.composite?
465
- @static_type = return_result_type
466
- if @ast_nodes
467
- @next_selections = []
468
- @ast_nodes.each do |ast_node|
469
- @next_selections.concat(ast_node.selections)
470
- end
471
- else
472
- @next_selections = @ast_node.selections
473
- end
474
-
475
- @all_next_objects = []
476
- @all_next_results = []
477
-
478
- is_list = return_type.list?
479
- is_non_null = return_type.non_null?
480
- results = @selections_step.results
481
- field_result_idx = 0
482
- i = 0
483
- s = results.size
484
- while i < s do
485
- result_h = results[i]
486
- if @object_is_authorized[i]
487
- result = @field_results[field_result_idx]
488
- field_result_idx += 1
489
- else
490
- result = nil
491
- end
492
- i += 1
493
- build_graphql_result(result_h, @key, result, return_type, is_non_null, is_list, false)
494
- end
495
- @enqueued_authorization = true
496
-
497
- if @pending_steps.nil? || @pending_steps.size == 0
498
- enqueue_next_steps
499
- else
500
- # Do nothing -- it will enqueue itself later
501
- end
502
- else
503
- ctx = @selections_step.query.context
504
- results = @selections_step.results
505
- field_result_idx = 0
506
- i = 0
507
- s = results.size
508
- while i < s do
509
- result_h = results[i]
510
- if @object_is_authorized[i]
511
- field_result = @field_results[field_result_idx]
512
- field_result_idx += 1
513
- else
514
- field_result = nil
515
- end
516
- i += 1
517
- result_h[@key] = if field_result.nil?
518
- if return_type.non_null?
519
- add_non_null_error(false)
520
- else
521
- nil
522
- end
523
- elsif field_result.is_a?(GraphQL::Error)
524
- add_graphql_error(field_result)
525
- else
526
- # TODO `nil`s in [T!] types aren't handled
527
- return_type.coerce_result(field_result, ctx)
528
- end
529
- end
530
- end
531
- end
532
-
533
- def enqueue_next_steps
534
- if !@all_next_results.empty?
535
- @all_next_objects.compact!
536
-
537
- if @static_type.kind.abstract?
538
- next_objects_by_type = Hash.new { |h, obj_t| h[obj_t] = [] }.compare_by_identity
539
- next_results_by_type = Hash.new { |h, obj_t| h[obj_t] = [] }.compare_by_identity
540
-
541
- @all_next_objects.each_with_index do |next_object, i|
542
- result = @all_next_results[i]
543
- if (object_type = @runner.runtime_type_at[result])
544
- # OK
545
- else
546
- object_type = @runner.resolve_type(@static_type, next_object, @selections_step.query)
547
- @runner.runtime_type_at[result] = object_type
548
- end
549
- next_objects_by_type[object_type] << next_object
550
- next_results_by_type[object_type] << result
551
- end
552
-
553
- next_objects_by_type.each do |obj_type, next_objects|
554
- @runner.add_step(SelectionsStep.new(
555
- path: path,
556
- parent_type: obj_type,
557
- selections: @next_selections,
558
- objects: next_objects,
559
- results: next_results_by_type[obj_type],
560
- runner: @runner,
561
- query: @selections_step.query,
562
- ))
563
- end
564
- else
565
- @runner.add_step(SelectionsStep.new(
566
- path: path,
567
- parent_type: @static_type,
568
- selections: @next_selections,
569
- objects: @all_next_objects,
570
- results: @all_next_results,
571
- runner: @runner,
572
- query: @selections_step.query,
573
- ))
574
- end
575
- end
576
- end
577
-
578
- def authorized_finished(step)
579
- @pending_steps.delete(step)
580
- if @enqueued_authorization && @pending_steps.size == 0
581
- @runner.add_step(self)
582
- end
583
- end
584
-
585
- def add_non_null_error(is_from_array)
586
- err = InvalidNullError.new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path)
587
- @runner.schema.type_error(err, @selections_step.query.context)
588
- end
589
-
590
- private
591
-
592
- def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
593
- if field_result.nil?
594
- if is_nn
595
- graphql_result[key] = add_non_null_error(is_from_array)
596
- else
597
- graphql_result[key] = nil
598
- end
599
- elsif field_result.is_a?(GraphQL::Error)
600
- graphql_result[key] = add_graphql_error(field_result)
601
- elsif is_list
602
- if is_nn
603
- return_type = return_type.of_type
604
- end
605
- inner_type = return_type.of_type
606
- inner_type_nn = inner_type.non_null?
607
- inner_type_l = inner_type.list?
608
- list_result = graphql_result[key] = []
609
- i = 0
610
- s = field_result.size
611
- while i < s
612
- inner_f_r = field_result[i]
613
- build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
614
- i += 1
615
- end
616
- elsif @runner.resolves_lazies || (@runner.authorization && (@static_type.kind.object? ? @runner.authorizes?(@static_type, @selections_step.query.context) : (
617
- (runtime_type = (@runner.runtime_type_at[graphql_result] = @runner.resolve_type(@static_type, field_result, @selections_step.query))
618
- ) && @runner.authorizes?(runtime_type, @selections_step.query.context)
619
- )))
620
- obj_step = PrepareObjectStep.new(
621
- static_type: @static_type,
622
- object: field_result,
623
- runner: @runner,
624
- field_resolve_step: self,
625
- graphql_result: graphql_result,
626
- next_objects: @all_next_objects,
627
- next_results: @all_next_results,
628
- is_non_null: is_nn,
629
- key: key,
630
- is_from_array: is_from_array,
631
- )
632
- ps = @pending_steps ||= []
633
- ps << obj_step
634
- @runner.add_step(obj_step)
635
- else
636
- next_result_h = {}
637
- @all_next_results << next_result_h
638
- @all_next_objects << field_result
639
- @runner.static_type_at[next_result_h] = @static_type
640
- graphql_result[key] = next_result_h
641
- end
642
- end
643
-
644
- def resolve_batch(objects, context, args_hash)
645
- method_receiver = @field_definition.dynamic_introspection ? @field_definition.owner : @parent_type
646
- case @field_definition.execution_next_mode
647
- when :resolve_batch
648
- begin
649
- method_receiver.public_send(@field_definition.execution_next_mode_key, objects, context, **args_hash)
650
- rescue GraphQL::ExecutionError => exec_err
651
- Array.new(objects.size, exec_err)
652
- end
653
- when :resolve_static
654
- result = method_receiver.public_send(@field_definition.execution_next_mode_key, context, **args_hash)
655
- Array.new(objects.size, result)
656
- when :resolve_each
657
- objects.map do |o|
658
- method_receiver.public_send(@field_definition.execution_next_mode_key, o, context, **args_hash)
659
- rescue GraphQL::ExecutionError => err
660
- err
661
- end
662
- when :hash_key
663
- objects.map { |o| o[@field_definition.execution_next_mode_key] }
664
- when :direct_send
665
- if args_hash.empty?
666
- objects.map do |o|
667
- o.public_send(@field_definition.execution_next_mode_key)
668
- rescue GraphQL::ExecutionError => err
669
- err
670
- rescue StandardError => stderr
671
- begin
672
- @selections_step.query.handle_or_reraise(stderr)
673
- rescue GraphQL::ExecutionError => ex_err
674
- ex_err
675
- end
676
- end
677
- else
678
- objects.map { |o| o.public_send(@field_definition.execution_next_mode_key, **args_hash) }
679
- end
680
- when :dig
681
- objects.map { |o| o.dig(*@field_definition.execution_next_mode_key) }
682
- when :dataload
683
- if (k = @field_definition.execution_next_mode_key).is_a?(Class)
684
- context.dataload_all(k, objects)
685
- elsif (source_class = k[:with])
686
- if (batch_args = k[:by])
687
- context.dataload_all(source_class, *batch_args, objects)
688
- else
689
- context.dataload_all(source_class, objects)
690
- end
691
- elsif (model = k[:model])
692
- value_method = k[:using]
693
- values = objects.map(&value_method)
694
- context.dataload_all_records(model, values, find_by: k[:find_by])
695
- elsif (assoc = k[:association])
696
- if assoc == true
697
- assoc = @field_definition.original_name
698
- end
699
- context.dataload_all_associations(objects, assoc, scope: k[:scope])
700
- else
701
- raise ArgumentError, "Unexpected `dataload: ...` configuration: #{k.inspect}"
702
- end
703
- when :resolver_class
704
- results = Array.new(objects.size, nil)
705
- ps = @pending_steps ||= []
706
- objects.each_with_index do |o, idx|
707
- resolver_inst = @field_definition.resolver.new(object: o, context: context, field: @field_definition)
708
- ps << resolver_inst
709
- resolver_inst.field_resolve_step = self
710
- resolver_inst.prepared_arguments = args_hash
711
- resolver_inst.exec_result = results
712
- resolver_inst.exec_index = idx
713
- @runner.add_step(resolver_inst)
714
- resolver_inst
715
- end
716
- results
717
- when :resolve_legacy_instance_method
718
- @selections_step.graphql_objects.map do |obj_inst|
719
- if @field_definition.dynamic_introspection
720
- obj_inst = @owner.wrap(obj_inst, context)
721
- end
722
- obj_inst.public_send(@field_definition.execution_next_mode_key, **args_hash)
723
- rescue GraphQL::ExecutionError => exec_err
724
- exec_err
725
- end
726
- else
727
- raise "Batching execution for #{path} not implemented (execution_next_mode: #{@execution_next_mode.inspect}); provide `resolve_static:`, `resolve_batch:`, `hash_key:`, `method:`, or use a compatibility plug-in"
728
- end
729
- end
730
- end
731
-
732
- class RawValueFieldResolveStep < FieldResolveStep
733
- def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
734
- if field_result.is_a?(Interpreter::RawValue)
735
- graphql_result[key] = field_result.resolve
736
- else
737
- super
738
- end
739
- end
740
- end
741
- end
742
- end
743
- end