graphql 2.5.11 → 2.5.23

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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  4. data/lib/graphql/dashboard/application_controller.rb +41 -0
  5. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  6. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  7. data/lib/graphql/dashboard/subscriptions.rb +2 -1
  8. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
  9. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
  10. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
  11. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
  12. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
  13. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
  14. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
  15. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
  16. data/lib/graphql/dashboard.rb +11 -73
  17. data/lib/graphql/dataloader/async_dataloader.rb +22 -11
  18. data/lib/graphql/dataloader/null_dataloader.rb +48 -10
  19. data/lib/graphql/dataloader.rb +75 -23
  20. data/lib/graphql/date_encoding_error.rb +1 -1
  21. data/lib/graphql/execution/interpreter/resolve.rb +7 -13
  22. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
  23. data/lib/graphql/execution/interpreter/runtime.rb +24 -18
  24. data/lib/graphql/execution/interpreter.rb +8 -22
  25. data/lib/graphql/execution/lazy.rb +1 -1
  26. data/lib/graphql/execution/multiplex.rb +1 -1
  27. data/lib/graphql/execution/next/field_resolve_step.rb +743 -0
  28. data/lib/graphql/execution/next/load_argument_step.rb +64 -0
  29. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  30. data/lib/graphql/execution/next/runner.rb +411 -0
  31. data/lib/graphql/execution/next/selections_step.rb +37 -0
  32. data/lib/graphql/execution/next.rb +72 -0
  33. data/lib/graphql/execution.rb +8 -4
  34. data/lib/graphql/execution_error.rb +17 -10
  35. data/lib/graphql/introspection/directive_type.rb +7 -3
  36. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  37. data/lib/graphql/introspection/entry_points.rb +11 -3
  38. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  39. data/lib/graphql/introspection/field_type.rb +13 -5
  40. data/lib/graphql/introspection/input_value_type.rb +21 -13
  41. data/lib/graphql/introspection/type_type.rb +64 -28
  42. data/lib/graphql/invalid_null_error.rb +11 -5
  43. data/lib/graphql/language/document_from_schema_definition.rb +2 -1
  44. data/lib/graphql/language.rb +21 -12
  45. data/lib/graphql/pagination/connection.rb +2 -0
  46. data/lib/graphql/pagination/connections.rb +32 -0
  47. data/lib/graphql/query/context.rb +4 -3
  48. data/lib/graphql/query/null_context.rb +9 -3
  49. data/lib/graphql/schema/argument.rb +12 -0
  50. data/lib/graphql/schema/build_from_definition.rb +10 -1
  51. data/lib/graphql/schema/directive.rb +22 -4
  52. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  53. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  54. data/lib/graphql/schema/field.rb +79 -48
  55. data/lib/graphql/schema/field_extension.rb +33 -0
  56. data/lib/graphql/schema/list.rb +1 -1
  57. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  58. data/lib/graphql/schema/member/has_arguments.rb +43 -14
  59. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  60. data/lib/graphql/schema/member/has_dataloader.rb +37 -0
  61. data/lib/graphql/schema/member/has_fields.rb +86 -5
  62. data/lib/graphql/schema/member.rb +5 -0
  63. data/lib/graphql/schema/non_null.rb +1 -1
  64. data/lib/graphql/schema/object.rb +1 -0
  65. data/lib/graphql/schema/resolver.rb +60 -1
  66. data/lib/graphql/schema/subscription.rb +0 -2
  67. data/lib/graphql/schema/validator/required_validator.rb +33 -2
  68. data/lib/graphql/schema/visibility/profile.rb +68 -49
  69. data/lib/graphql/schema/visibility.rb +3 -3
  70. data/lib/graphql/schema/wrapper.rb +7 -1
  71. data/lib/graphql/schema.rb +53 -10
  72. data/lib/graphql/static_validation/base_visitor.rb +90 -66
  73. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  74. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  75. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
  76. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
  77. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
  78. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
  79. data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
  80. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
  81. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  82. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
  83. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
  84. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
  85. data/lib/graphql/static_validation/validation_context.rb +1 -1
  86. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  87. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +25 -1
  88. data/lib/graphql/subscriptions/event.rb +1 -0
  89. data/lib/graphql/subscriptions.rb +21 -1
  90. data/lib/graphql/testing/helpers.rb +12 -9
  91. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  92. data/lib/graphql/testing.rb +1 -0
  93. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  94. data/lib/graphql/tracing/detailed_trace.rb +70 -7
  95. data/lib/graphql/tracing/perfetto_trace.rb +209 -79
  96. data/lib/graphql/tracing/sentry_trace.rb +3 -1
  97. data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
  98. data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
  99. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  100. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  101. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  102. data/lib/graphql/unauthorized_error.rb +9 -1
  103. data/lib/graphql/version.rb +1 -1
  104. data/lib/graphql.rb +8 -2
  105. metadata +17 -3
@@ -64,6 +64,7 @@ module GraphQL
64
64
  @nonblocking = nonblocking
65
65
  end
66
66
  @fiber_limit = fiber_limit
67
+ @lazies_at_depth = Hash.new { |h, k| h[k] = [] }
67
68
  end
68
69
 
69
70
  # @return [Integer, nil]
@@ -140,10 +141,10 @@ module GraphQL
140
141
  end
141
142
 
142
143
  # @api private Nothing to see here
143
- def append_job(&job)
144
+ def append_job(callable = nil, &job)
144
145
  # Given a block, queue it up to be worked through when `#run` is called.
145
- # (If the dataloader is already running, than a Fiber will pick this up later.)
146
- @pending_jobs.push(job)
146
+ # (If the dataloader is already running, then a Fiber will pick this up later.)
147
+ @pending_jobs.push(callable || job)
147
148
  nil
148
149
  end
149
150
 
@@ -160,6 +161,10 @@ module GraphQL
160
161
  def run_isolated
161
162
  prev_queue = @pending_jobs
162
163
  prev_pending_keys = {}
164
+ prev_lazies_at_depth = @lazies_at_depth
165
+ @lazies_at_depth = @lazies_at_depth.dup.clear
166
+ # Clear pending loads but keep already-cached records
167
+ # in case they are useful to the given block.
163
168
  @source_cache.each do |source_class, batched_sources|
164
169
  batched_sources.each do |batch_args, batched_source_instance|
165
170
  if batched_source_instance.pending?
@@ -179,6 +184,7 @@ module GraphQL
179
184
  res
180
185
  ensure
181
186
  @pending_jobs = prev_queue
187
+ @lazies_at_depth = prev_lazies_at_depth
182
188
  prev_pending_keys.each do |source_instance, pending|
183
189
  pending.each do |key, value|
184
190
  if !source_instance.results.key?(key)
@@ -188,7 +194,8 @@ module GraphQL
188
194
  end
189
195
  end
190
196
 
191
- def run
197
+ # @param trace_query_lazy [nil, Execution::Multiplex]
198
+ def run(trace_query_lazy: nil)
192
199
  trace = Fiber[:__graphql_current_multiplex]&.current_trace
193
200
  jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
194
201
  job_fibers = []
@@ -201,26 +208,13 @@ module GraphQL
201
208
  while first_pass || !job_fibers.empty?
202
209
  first_pass = false
203
210
 
204
- while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
205
- if f.alive?
206
- finished = run_fiber(f)
207
- if !finished
208
- next_job_fibers << f
209
- end
210
- end
211
- end
212
- join_queues(job_fibers, next_job_fibers)
213
-
214
- while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
215
- while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
216
- if f.alive?
217
- finished = run_fiber(f)
218
- if !finished
219
- next_source_fibers << f
220
- end
221
- end
211
+ run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
212
+
213
+ if !@lazies_at_depth.empty?
214
+ with_trace_query_lazy(trace_query_lazy) do
215
+ run_next_pending_lazies(job_fibers, trace)
216
+ run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
222
217
  end
223
- join_queues(source_fibers, next_source_fibers)
224
218
  end
225
219
  end
226
220
 
@@ -248,6 +242,11 @@ module GraphQL
248
242
  f.resume
249
243
  end
250
244
 
245
+ # @api private
246
+ def lazy_at_depth(depth, lazy)
247
+ @lazies_at_depth[depth] << lazy
248
+ end
249
+
251
250
  def spawn_fiber
252
251
  fiber_vars = get_fiber_variables
253
252
  Fiber.new(blocking: !@nonblocking) {
@@ -275,6 +274,59 @@ module GraphQL
275
274
 
276
275
  private
277
276
 
277
+ def run_next_pending_lazies(job_fibers, trace)
278
+ smallest_depth = nil
279
+ @lazies_at_depth.each_key do |depth_key|
280
+ smallest_depth ||= depth_key
281
+ if depth_key < smallest_depth
282
+ smallest_depth = depth_key
283
+ end
284
+ end
285
+
286
+ if smallest_depth
287
+ lazies = @lazies_at_depth.delete(smallest_depth)
288
+ if !lazies.empty?
289
+ lazies.each_with_index do |l, idx|
290
+ append_job { l.value }
291
+ end
292
+ job_fibers.unshift(spawn_job_fiber(trace))
293
+ end
294
+ end
295
+ end
296
+
297
+ def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
298
+ while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
299
+ if f.alive?
300
+ finished = run_fiber(f)
301
+ if !finished
302
+ next_job_fibers << f
303
+ end
304
+ end
305
+ end
306
+ join_queues(job_fibers, next_job_fibers)
307
+
308
+ while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
309
+ while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
310
+ if f.alive?
311
+ finished = run_fiber(f)
312
+ if !finished
313
+ next_source_fibers << f
314
+ end
315
+ end
316
+ end
317
+ join_queues(source_fibers, next_source_fibers)
318
+ end
319
+ end
320
+
321
+ def with_trace_query_lazy(multiplex_or_nil, &block)
322
+ if (multiplex = multiplex_or_nil)
323
+ query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
324
+ multiplex.current_trace.execute_query_lazy(query: query, multiplex: multiplex, &block)
325
+ else
326
+ yield
327
+ end
328
+ end
329
+
278
330
  def calculate_fiber_limit
279
331
  total_fiber_limit = @fiber_limit || Float::INFINITY
280
332
  if total_fiber_limit < 4
@@ -10,7 +10,7 @@ module GraphQL
10
10
 
11
11
  def initialize(value)
12
12
  @date_value = value
13
- super("Date cannot be parsed: #{value}. \nDate must be be able to be parsed as a Ruby Date object.")
13
+ super("Date cannot be parsed: #{value}. \nDate must be able to be parsed as a Ruby Date object.")
14
14
  end
15
15
  end
16
16
  end
@@ -6,12 +6,17 @@ module GraphQL
6
6
  module Resolve
7
7
  # Continue field results in `results` until there's nothing else to continue.
8
8
  # @return [void]
9
+ # @deprecated Call `dataloader.run` instead
9
10
  def self.resolve_all(results, dataloader)
11
+ warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
10
12
  dataloader.append_job { resolve(results, dataloader) }
11
13
  nil
12
14
  end
13
15
 
16
+ # @deprecated Call `dataloader.run` instead
14
17
  def self.resolve_each_depth(lazies_at_depth, dataloader)
18
+ warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
19
+
15
20
  smallest_depth = nil
16
21
  lazies_at_depth.each_key do |depth_key|
17
22
  smallest_depth ||= depth_key
@@ -34,20 +39,9 @@ module GraphQL
34
39
  nil
35
40
  end
36
41
 
37
- # After getting `results` back from an interpreter evaluation,
38
- # continue it until you get a response-ready Ruby value.
39
- #
40
- # `results` is one level of _depth_ of a query or multiplex.
41
- #
42
- # Resolve all lazy values in that depth before moving on
43
- # to the next level.
44
- #
45
- # It's assumed that the lazies will
46
- # return {Lazy} instances if there's more work to be done,
47
- # or return {Hash}/{Array} if the query should be continued.
48
- #
49
- # @return [void]
42
+ # @deprecated Call `dataloader.run` instead
50
43
  def self.resolve(results, dataloader)
44
+ warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
51
45
  # There might be pending jobs here that _will_ write lazies
52
46
  # into the result hash. We should run them out, so we
53
47
  # can be sure that all lazies will be present in the result hashes.
@@ -42,6 +42,14 @@ module GraphQL
42
42
  end
43
43
  end
44
44
 
45
+ def depth
46
+ @depth ||= if @graphql_parent
47
+ @graphql_parent.depth + 1
48
+ else
49
+ 1
50
+ end
51
+ end
52
+
45
53
  attr_accessor :graphql_dead
46
54
  attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent,
47
55
  :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
@@ -157,6 +165,11 @@ module GraphQL
157
165
  end
158
166
  end
159
167
  end
168
+
169
+ # hook for breadth-first implementations to signal when collecting results.
170
+ def collect_result(result_name, result_value)
171
+ false
172
+ end
160
173
  end
161
174
 
162
175
  class GraphQLResultArray
@@ -35,11 +35,10 @@ module GraphQL
35
35
  # @return [GraphQL::Query::Context]
36
36
  attr_reader :context
37
37
 
38
- def initialize(query:, lazies_at_depth:)
38
+ def initialize(query:)
39
39
  @query = query
40
40
  @current_trace = query.current_trace
41
41
  @dataloader = query.multiplex.dataloader
42
- @lazies_at_depth = lazies_at_depth
43
42
  @schema = query.schema
44
43
  @context = query.context
45
44
  @response = nil
@@ -365,6 +364,10 @@ module GraphQL
365
364
  else
366
365
  @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
367
366
  runtime_state = get_current_runtime_state # This might be in a different fiber
367
+ runtime_state.current_field = field_defn
368
+ runtime_state.current_arguments = resolved_arguments
369
+ runtime_state.current_result_name = result_name
370
+ runtime_state.current_result = selections_result
368
371
  evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
369
372
  end
370
373
  end
@@ -373,6 +376,8 @@ module GraphQL
373
376
  def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
374
377
  after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
375
378
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
379
+ next if selection_result.collect_result(result_name, resolved_arguments)
380
+
376
381
  return_type_non_null = field_defn.type.non_null?
377
382
  continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
378
383
  next
@@ -446,7 +451,7 @@ module GraphQL
446
451
  }
447
452
  end
448
453
 
449
- field_result = call_method_on_directives(:resolve, object, directives) do
454
+ call_method_on_directives(:resolve, object, directives) do
450
455
  if !directives.empty?
451
456
  # This might be executed in a different context; reset this info
452
457
  runtime_state = get_current_runtime_state
@@ -472,6 +477,8 @@ module GraphQL
472
477
  end
473
478
  @current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
474
479
  after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
480
+ next if selection_result.collect_result(result_name, inner_result)
481
+
475
482
  owner_type = selection_result.graphql_result_type
476
483
  return_type = field_defn.type
477
484
  continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
@@ -488,7 +495,7 @@ module GraphQL
488
495
  # all of its child fields before moving on to the next root mutation field.
489
496
  # (Subselections of this mutation will still be resolved level-by-level.)
490
497
  if selection_result.graphql_is_eager
491
- Interpreter::Resolve.resolve_all([field_result], @dataloader)
498
+ @dataloader.run
492
499
  end
493
500
  end
494
501
 
@@ -596,7 +603,7 @@ module GraphQL
596
603
  err
597
604
  end
598
605
  continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
599
- elsif GraphQL::Execution::SKIP == value
606
+ elsif value.is_a?(GraphQL::Execution::Skip)
600
607
  # It's possible a lazy was already written here
601
608
  case selection_result
602
609
  when GraphQLResultHash
@@ -667,7 +674,11 @@ module GraphQL
667
674
  rescue GraphQL::ExecutionError => ex_err
668
675
  return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
669
676
  rescue StandardError => err
670
- query.handle_or_reraise(err)
677
+ begin
678
+ query.handle_or_reraise(err)
679
+ rescue GraphQL::ExecutionError => ex_err
680
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
681
+ end
671
682
  end
672
683
  set_result(selection_result, result_name, r, false, is_non_null)
673
684
  r
@@ -750,7 +761,7 @@ module GraphQL
750
761
  idx += 1
751
762
  if use_dataloader_job
752
763
  @dataloader.append_job do
753
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
764
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, nil)
754
765
  end
755
766
  else
756
767
  resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
@@ -792,6 +803,7 @@ module GraphQL
792
803
  end
793
804
 
794
805
  def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
806
+ runtime_state ||= get_current_runtime_state
795
807
  runtime_state.current_result_name = this_idx
796
808
  runtime_state.current_result = response_list
797
809
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
@@ -879,7 +891,6 @@ module GraphQL
879
891
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
880
892
  def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
881
893
  if lazy?(lazy_obj)
882
- orig_result = result
883
894
  was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
884
895
  lazy = GraphQL::Execution::Lazy.new(field: field) do
885
896
  # This block might be called in a new fiber;
@@ -889,13 +900,13 @@ module GraphQL
889
900
  runtime_state.current_field = field
890
901
  runtime_state.current_arguments = arguments
891
902
  runtime_state.current_result_name = result_name
892
- runtime_state.current_result = orig_result
903
+ runtime_state.current_result = result
893
904
  runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
894
905
  # Wrap the execution of _this_ method with tracing,
895
906
  # but don't wrap the continuation below
896
- result = nil
907
+ sync_result = nil
897
908
  inner_obj = begin
898
- result = if trace
909
+ sync_result = if trace
899
910
  @current_trace.begin_execute_field(field, owner_object, arguments, query)
900
911
  @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
901
912
  schema.sync_lazy(lazy_obj)
@@ -913,7 +924,7 @@ module GraphQL
913
924
  end
914
925
  ensure
915
926
  if trace
916
- @current_trace.end_execute_field(field, owner_object, arguments, query, result)
927
+ @current_trace.end_execute_field(field, owner_object, arguments, query, sync_result)
917
928
  end
918
929
  end
919
930
  yield(inner_obj, runtime_state)
@@ -923,12 +934,7 @@ module GraphQL
923
934
  lazy.value
924
935
  else
925
936
  set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
926
- current_depth = 0
927
- while result
928
- current_depth += 1
929
- result = result.graphql_parent
930
- end
931
- @lazies_at_depth[current_depth] << lazy
937
+ @dataloader.lazy_at_depth(result.depth, lazy)
932
938
  lazy
933
939
  end
934
940
  else
@@ -42,7 +42,6 @@ module GraphQL
42
42
  trace.execute_multiplex(multiplex: multiplex) do
43
43
  schema = multiplex.schema
44
44
  queries = multiplex.queries
45
- lazies_at_depth = Hash.new { |h, k| h[k] = [] }
46
45
  multiplex_analyzers = schema.multiplex_analyzers
47
46
  if multiplex.max_complexity
48
47
  multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
@@ -59,11 +58,6 @@ module GraphQL
59
58
  # Do as much eager evaluation of the query as possible
60
59
  results = []
61
60
  queries.each_with_index do |query, idx|
62
- if query.subscription? && !query.subscription_update?
63
- subs_namespace = query.context.namespace(:subscriptions)
64
- subs_namespace[:events] = []
65
- subs_namespace[:subscriptions] = {}
66
- end
67
61
  multiplex.dataloader.append_job {
68
62
  operation = query.selected_operation
69
63
  result = if operation.nil? || !query.valid? || !query.context.errors.empty?
@@ -73,38 +67,27 @@ module GraphQL
73
67
  # Although queries in a multiplex _share_ an Interpreter instance,
74
68
  # they also have another item of state, which is private to that query
75
69
  # in particular, assign it here:
76
- runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
70
+ runtime = Runtime.new(query: query)
77
71
  query.context.namespace(:interpreter_runtime)[:runtime] = runtime
78
-
72
+ if query.subscription? && !query.subscription_update?
73
+ schema.subscriptions.initialize_subscriptions(query)
74
+ end
79
75
  query.current_trace.execute_query(query: query) do
80
76
  runtime.run_eager
81
77
  end
82
78
  rescue GraphQL::ExecutionError => err
83
79
  query.context.errors << err
84
- NO_OPERATION
85
80
  end
86
81
  end
87
82
  results[idx] = result
88
83
  }
89
84
  end
90
85
 
91
- multiplex.dataloader.run
92
-
93
- # Then, work through lazy results in a breadth-first way
94
- multiplex.dataloader.append_job {
95
- query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
96
- multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
97
- Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
98
- end
99
- }
100
- multiplex.dataloader.run
86
+ multiplex.dataloader.run(trace_query_lazy: multiplex)
101
87
 
102
88
  # Then, find all errors and assign the result to the query object
103
89
  results.each_with_index do |data_result, idx|
104
90
  query = queries[idx]
105
- if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
106
- schema.subscriptions.write_subscription(query, events)
107
- end
108
91
  # Assign the result so that it can be accessed in instrumentation
109
92
  query.result_values = if data_result.equal?(NO_OPERATION)
110
93
  if !query.valid? || !query.context.errors.empty?
@@ -114,6 +97,9 @@ module GraphQL
114
97
  data_result
115
98
  end
116
99
  else
100
+ if query.subscription?
101
+ schema.subscriptions.finish_subscriptions(query)
102
+ end
117
103
  result = {}
118
104
 
119
105
  if !query.context.errors.empty?
@@ -38,7 +38,7 @@ module GraphQL
38
38
  # (fewer clauses in a hot `case` block), but now it requires special handling here.
39
39
  # I think it's still worth it for the performance win, but if the number of special
40
40
  # cases grows, then maybe it's worth rethinking somehow.
41
- if @value.is_a?(StandardError) && @value != GraphQL::Execution::SKIP
41
+ if @value.is_a?(StandardError) && !@value.is_a?(GraphQL::Execution::Skip)
42
42
  raise @value
43
43
  else
44
44
  @value
@@ -33,7 +33,7 @@ module GraphQL
33
33
  @queries.each { |q| q.multiplex = self }
34
34
  @context = context
35
35
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
36
- @tracers = schema.tracers + (context[:tracers] || [])
36
+ @tracers = schema.tracers + (context[:tracers] || EmptyObjects::EMPTY_ARRAY)
37
37
  @max_complexity = max_complexity
38
38
  @current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
39
39
  @logger = nil