graphql 2.4.3 → 2.4.13

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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +38 -41
  4. data/lib/graphql/analysis.rb +15 -12
  5. data/lib/graphql/autoload.rb +38 -0
  6. data/lib/graphql/backtrace/table.rb +95 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  10. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  11. data/lib/graphql/dashboard/statics/dashboard.css +3 -0
  12. data/lib/graphql/dashboard/statics/dashboard.js +78 -0
  13. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  14. data/lib/graphql/dashboard/statics/icon.png +0 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +63 -0
  17. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +60 -0
  18. data/lib/graphql/dashboard.rb +142 -0
  19. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  20. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  21. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  22. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  23. data/lib/graphql/dataloader/source.rb +3 -3
  24. data/lib/graphql/dataloader.rb +43 -14
  25. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  26. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
  27. data/lib/graphql/execution/interpreter/runtime.rb +67 -40
  28. data/lib/graphql/execution/interpreter.rb +16 -6
  29. data/lib/graphql/execution/multiplex.rb +0 -4
  30. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  31. data/lib/graphql/invalid_name_error.rb +1 -1
  32. data/lib/graphql/invalid_null_error.rb +5 -15
  33. data/lib/graphql/language/cache.rb +13 -0
  34. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  35. data/lib/graphql/language/lexer.rb +11 -4
  36. data/lib/graphql/language/nodes.rb +3 -0
  37. data/lib/graphql/language/parser.rb +2 -2
  38. data/lib/graphql/language/printer.rb +8 -8
  39. data/lib/graphql/language/static_visitor.rb +37 -33
  40. data/lib/graphql/language/visitor.rb +59 -55
  41. data/lib/graphql/pagination/connection.rb +1 -1
  42. data/lib/graphql/query/context/scoped_context.rb +1 -1
  43. data/lib/graphql/query/context.rb +6 -5
  44. data/lib/graphql/query/variable_validation_error.rb +1 -1
  45. data/lib/graphql/query.rb +20 -22
  46. data/lib/graphql/railtie.rb +7 -0
  47. data/lib/graphql/schema/addition.rb +1 -1
  48. data/lib/graphql/schema/argument.rb +3 -5
  49. data/lib/graphql/schema/build_from_definition.rb +8 -7
  50. data/lib/graphql/schema/directive/flagged.rb +1 -1
  51. data/lib/graphql/schema/directive.rb +2 -2
  52. data/lib/graphql/schema/enum.rb +36 -1
  53. data/lib/graphql/schema/enum_value.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +12 -12
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  58. data/lib/graphql/schema/input_object.rb +70 -34
  59. data/lib/graphql/schema/interface.rb +3 -2
  60. data/lib/graphql/schema/loader.rb +1 -1
  61. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  62. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  63. data/lib/graphql/schema/member/has_directives.rb +4 -4
  64. data/lib/graphql/schema/member/has_fields.rb +19 -1
  65. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  66. data/lib/graphql/schema/member/has_validators.rb +1 -1
  67. data/lib/graphql/schema/member/scoped.rb +1 -1
  68. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  69. data/lib/graphql/schema/member.rb +1 -0
  70. data/lib/graphql/schema/object.rb +25 -8
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +11 -10
  73. data/lib/graphql/schema/subscription.rb +52 -6
  74. data/lib/graphql/schema/union.rb +1 -1
  75. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  76. data/lib/graphql/schema/validator.rb +1 -1
  77. data/lib/graphql/schema/visibility/migration.rb +1 -0
  78. data/lib/graphql/schema/visibility/profile.rb +69 -237
  79. data/lib/graphql/schema/visibility/visit.rb +190 -0
  80. data/lib/graphql/schema/visibility.rb +169 -28
  81. data/lib/graphql/schema/warden.rb +18 -5
  82. data/lib/graphql/schema.rb +90 -43
  83. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  84. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  85. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  86. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  87. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  88. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  89. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  90. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  91. data/lib/graphql/static_validation/validation_context.rb +1 -0
  92. data/lib/graphql/static_validation/validator.rb +6 -1
  93. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  94. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  95. data/lib/graphql/subscriptions/event.rb +12 -1
  96. data/lib/graphql/subscriptions/serialize.rb +1 -1
  97. data/lib/graphql/subscriptions.rb +1 -1
  98. data/lib/graphql/testing/helpers.rb +2 -2
  99. data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -3
  100. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  101. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  102. data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
  103. data/lib/graphql/tracing/appsignal_trace.rb +12 -0
  104. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  105. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  106. data/lib/graphql/tracing/data_dog_trace.rb +11 -0
  107. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  108. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  109. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  110. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  111. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  112. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  113. data/lib/graphql/tracing/new_relic_trace.rb +164 -41
  114. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  115. data/lib/graphql/tracing/notifications_trace.rb +4 -0
  116. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  117. data/lib/graphql/tracing/null_trace.rb +9 -0
  118. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  119. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  120. data/lib/graphql/tracing/perfetto_trace.rb +737 -0
  121. data/lib/graphql/tracing/platform_trace.rb +5 -0
  122. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  123. data/lib/graphql/tracing/prometheus_trace.rb +31 -0
  124. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  125. data/lib/graphql/tracing/scout_trace.rb +11 -0
  126. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  127. data/lib/graphql/tracing/sentry_trace.rb +11 -0
  128. data/lib/graphql/tracing/statsd_trace.rb +15 -0
  129. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  130. data/lib/graphql/tracing/trace.rb +128 -1
  131. data/lib/graphql/tracing.rb +30 -30
  132. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  133. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  134. data/lib/graphql/types.rb +18 -11
  135. data/lib/graphql/version.rb +1 -1
  136. data/lib/graphql.rb +55 -47
  137. metadata +152 -10
  138. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  139. data/lib/graphql/backtrace/trace.rb +0 -93
  140. data/lib/graphql/backtrace/tracer.rb +0 -80
  141. data/lib/graphql/schema/null_mask.rb +0 -11
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/dataloader/source"
3
+ require "graphql/dataloader/active_record_source"
4
+
5
+ module GraphQL
6
+ class Dataloader
7
+ class ActiveRecordAssociationSource < GraphQL::Dataloader::Source
8
+ RECORD_SOURCE_CLASS = ActiveRecordSource
9
+
10
+ def initialize(association, scope = nil)
11
+ @association = association
12
+ @scope = scope
13
+ end
14
+
15
+ def load(record)
16
+ if (assoc = record.association(@association)).loaded?
17
+ assoc.target
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def fetch(records)
24
+ record_classes = Set.new.compare_by_identity
25
+ associated_classes = Set.new.compare_by_identity
26
+ records.each do |record|
27
+ if record_classes.add?(record.class)
28
+ reflection = record.class.reflect_on_association(@association)
29
+ if !reflection.polymorphic? && reflection.klass
30
+ associated_classes.add(reflection.klass)
31
+ end
32
+ end
33
+ end
34
+
35
+ available_records = []
36
+ associated_classes.each do |assoc_class|
37
+ already_loaded_records = dataloader.with(RECORD_SOURCE_CLASS, assoc_class).results.values
38
+ available_records.concat(already_loaded_records)
39
+ end
40
+
41
+ ::ActiveRecord::Associations::Preloader.new(records: records, associations: @association, available_records: available_records, scope: @scope).call
42
+
43
+ loaded_associated_records = records.map { |r| r.public_send(@association) }
44
+ records_by_model = {}
45
+ loaded_associated_records.each do |record|
46
+ if record
47
+ updates = records_by_model[record.class] ||= {}
48
+ updates[record.id] = record
49
+ end
50
+ end
51
+
52
+ if @scope.nil?
53
+ # Don't cache records loaded via scope because they might have reduced `SELECT`s
54
+ # Could check .select_values here?
55
+ records_by_model.each do |model_class, updates|
56
+ dataloader.with(RECORD_SOURCE_CLASS, model_class).merge(updates)
57
+ end
58
+ end
59
+
60
+ loaded_associated_records
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/dataloader/source"
3
+
4
+ module GraphQL
5
+ class Dataloader
6
+ class ActiveRecordSource < GraphQL::Dataloader::Source
7
+ def initialize(model_class, find_by: model_class.primary_key)
8
+ @model_class = model_class
9
+ @find_by = find_by
10
+ @type_for_column = @model_class.type_for_attribute(@find_by)
11
+ end
12
+
13
+ def load(requested_key)
14
+ casted_key = @type_for_column.cast(requested_key)
15
+ super(casted_key)
16
+ end
17
+
18
+ def fetch(record_ids)
19
+ records = @model_class.where(@find_by => record_ids)
20
+ record_lookup = {}
21
+ records.each { |r| record_lookup[r.public_send(@find_by)] = r }
22
+ record_ids.map { |id| record_lookup[id] }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,16 +2,20 @@
2
2
  module GraphQL
3
3
  class Dataloader
4
4
  class AsyncDataloader < Dataloader
5
- def yield
6
- if (condition = Thread.current[:graphql_dataloader_next_tick])
5
+ def yield(source = Fiber[:__graphql_current_dataloader_source])
6
+ trace = Fiber[:__graphql_current_multiplex]&.current_trace
7
+ trace&.dataloader_fiber_yield(source)
8
+ if (condition = Fiber[:graphql_dataloader_next_tick])
7
9
  condition.wait
8
10
  else
9
11
  Fiber.yield
10
12
  end
13
+ trace&.dataloader_fiber_resume(source)
11
14
  nil
12
15
  end
13
16
 
14
17
  def run
18
+ trace = Fiber[:__graphql_current_multiplex]&.current_trace
15
19
  jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
16
20
  job_fibers = []
17
21
  next_job_fibers = []
@@ -20,11 +24,12 @@ module GraphQL
20
24
  first_pass = true
21
25
  sources_condition = Async::Condition.new
22
26
  manager = spawn_fiber do
23
- while first_pass || job_fibers.any?
27
+ trace&.begin_dataloader(self)
28
+ while first_pass || !job_fibers.empty?
24
29
  first_pass = false
25
30
  fiber_vars = get_fiber_variables
26
31
 
27
- while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_fiber)))
32
+ while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
28
33
  if f.alive?
29
34
  finished = run_fiber(f)
30
35
  if !finished
@@ -37,8 +42,8 @@ module GraphQL
37
42
 
38
43
  Sync do |root_task|
39
44
  set_fiber_variables(fiber_vars)
40
- while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
41
- while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition))))
45
+ while !source_tasks.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
46
+ while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition, trace))))
42
47
  if task.alive?
43
48
  root_task.yield # give the source task a chance to run
44
49
  next_source_tasks << task
@@ -50,6 +55,7 @@ module GraphQL
50
55
  end
51
56
  end
52
57
  end
58
+ trace&.end_dataloader(self)
53
59
  end
54
60
 
55
61
  manager.resume
@@ -63,7 +69,7 @@ module GraphQL
63
69
 
64
70
  private
65
71
 
66
- def spawn_source_task(parent_task, condition)
72
+ def spawn_source_task(parent_task, condition, trace)
67
73
  pending_sources = nil
68
74
  @source_cache.each_value do |source_by_batch_params|
69
75
  source_by_batch_params.each_value do |source|
@@ -77,10 +83,16 @@ module GraphQL
77
83
  if pending_sources
78
84
  fiber_vars = get_fiber_variables
79
85
  parent_task.async do
86
+ trace&.dataloader_spawn_source_fiber(pending_sources)
80
87
  set_fiber_variables(fiber_vars)
81
- Thread.current[:graphql_dataloader_next_tick] = condition
82
- pending_sources.each(&:run_pending_keys)
88
+ Fiber[:graphql_dataloader_next_tick] = condition
89
+ pending_sources.each do |s|
90
+ trace&.begin_dataloader_source(s)
91
+ s.run_pending_keys
92
+ trace&.end_dataloader_source(s)
93
+ end
83
94
  cleanup_fiber
95
+ trace&.dataloader_fiber_exit
84
96
  end
85
97
  end
86
98
  end
@@ -11,7 +11,7 @@ module GraphQL
11
11
  # executed synchronously.
12
12
  def run; end
13
13
  def run_isolated; yield; end
14
- def yield
14
+ def yield(_source)
15
15
  raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
16
16
  end
17
17
 
@@ -73,7 +73,7 @@ module GraphQL
73
73
  end
74
74
  }
75
75
 
76
- if pending_keys.any?
76
+ if !pending_keys.empty?
77
77
  sync(pending_keys)
78
78
  end
79
79
 
@@ -93,14 +93,14 @@ module GraphQL
93
93
  # Then run the batch and update the cache.
94
94
  # @return [void]
95
95
  def sync(pending_result_keys)
96
- @dataloader.yield
96
+ @dataloader.yield(self)
97
97
  iterations = 0
98
98
  while pending_result_keys.any? { |key| !@results.key?(key) }
99
99
  iterations += 1
100
100
  if iterations > MAX_ITERATIONS
101
101
  raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
102
102
  end
103
- @dataloader.yield
103
+ @dataloader.yield(self)
104
104
  end
105
105
  nil
106
106
  end
@@ -4,6 +4,8 @@ require "graphql/dataloader/null_dataloader"
4
4
  require "graphql/dataloader/request"
5
5
  require "graphql/dataloader/request_all"
6
6
  require "graphql/dataloader/source"
7
+ require "graphql/dataloader/active_record_association_source"
8
+ require "graphql/dataloader/active_record_source"
7
9
 
8
10
  module GraphQL
9
11
  # This plugin supports Fiber-based concurrency, along with {GraphQL::Dataloader::Source}.
@@ -78,10 +80,7 @@ module GraphQL
78
80
  def get_fiber_variables
79
81
  fiber_vars = {}
80
82
  Thread.current.keys.each do |fiber_var_key|
81
- # This variable should be fresh in each new fiber
82
- if fiber_var_key != :__graphql_runtime_info
83
- fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
84
- end
83
+ fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
85
84
  end
86
85
  fiber_vars
87
86
  end
@@ -132,8 +131,11 @@ module GraphQL
132
131
  # Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
133
132
  #
134
133
  # @return [void]
135
- def yield
134
+ def yield(source = Fiber[:__graphql_current_dataloader_source])
135
+ trace = Fiber[:__graphql_current_multiplex]&.current_trace
136
+ trace&.dataloader_fiber_yield(source)
136
137
  Fiber.yield
138
+ trace&.dataloader_fiber_resume(source)
137
139
  nil
138
140
  end
139
141
 
@@ -187,6 +189,7 @@ module GraphQL
187
189
  end
188
190
 
189
191
  def run
192
+ trace = Fiber[:__graphql_current_multiplex]&.current_trace
190
193
  jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
191
194
  job_fibers = []
192
195
  next_job_fibers = []
@@ -194,10 +197,11 @@ module GraphQL
194
197
  next_source_fibers = []
195
198
  first_pass = true
196
199
  manager = spawn_fiber do
197
- while first_pass || job_fibers.any?
200
+ trace&.begin_dataloader(self)
201
+ while first_pass || !job_fibers.empty?
198
202
  first_pass = false
199
203
 
200
- while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber)))
204
+ while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
201
205
  if f.alive?
202
206
  finished = run_fiber(f)
203
207
  if !finished
@@ -207,8 +211,8 @@ module GraphQL
207
211
  end
208
212
  join_queues(job_fibers, next_job_fibers)
209
213
 
210
- while (source_fibers.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
211
- 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))
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)))
212
216
  if f.alive?
213
217
  finished = run_fiber(f)
214
218
  if !finished
@@ -219,6 +223,8 @@ module GraphQL
219
223
  join_queues(source_fibers, next_source_fibers)
220
224
  end
221
225
  end
226
+
227
+ trace&.end_dataloader(self)
222
228
  end
223
229
 
224
230
  run_fiber(manager)
@@ -227,12 +233,13 @@ module GraphQL
227
233
  raise "Invariant: Manager fiber didn't terminate properly."
228
234
  end
229
235
 
230
- if job_fibers.any?
236
+ if !job_fibers.empty?
231
237
  raise "Invariant: job fibers should have exited but #{job_fibers.size} remained"
232
238
  end
233
- if source_fibers.any?
239
+ if !source_fibers.empty?
234
240
  raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
235
241
  end
242
+
236
243
  rescue UncaughtThrowError => e
237
244
  throw e.tag, e.value
238
245
  end
@@ -250,6 +257,22 @@ module GraphQL
250
257
  }
251
258
  end
252
259
 
260
+ # Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
261
+ # These will be used by {Dataloader::ActiveRecordSource}, {Dataloader::ActiveRecordAssociationSource} and their helper
262
+ # methods, `dataload_record` and `dataload_association`.
263
+ # @param records [Array<ActiveRecord::Base>] Already-loaded records to warm the cache with
264
+ # @param index_by [Symbol] The attribute to use as the cache key. (Should match `find_by:` when using {ActiveRecordSource})
265
+ # @return [void]
266
+ def merge_records(records, index_by: :id)
267
+ records_by_class = Hash.new { |h, k| h[k] = {} }
268
+ records.each do |r|
269
+ records_by_class[r.class][r.public_send(index_by)] = r
270
+ end
271
+ records_by_class.each do |r_class, records|
272
+ with(ActiveRecordSource, r_class).merge(records)
273
+ end
274
+ end
275
+
253
276
  private
254
277
 
255
278
  def calculate_fiber_limit
@@ -269,17 +292,19 @@ module GraphQL
269
292
  new_queue.clear
270
293
  end
271
294
 
272
- def spawn_job_fiber
273
- if @pending_jobs.any?
295
+ def spawn_job_fiber(trace)
296
+ if !@pending_jobs.empty?
274
297
  spawn_fiber do
298
+ trace&.dataloader_spawn_execution_fiber(@pending_jobs)
275
299
  while job = @pending_jobs.shift
276
300
  job.call
277
301
  end
302
+ trace&.dataloader_fiber_exit
278
303
  end
279
304
  end
280
305
  end
281
306
 
282
- def spawn_source_fiber
307
+ def spawn_source_fiber(trace)
283
308
  pending_sources = nil
284
309
  @source_cache.each_value do |source_by_batch_params|
285
310
  source_by_batch_params.each_value do |source|
@@ -292,10 +317,14 @@ module GraphQL
292
317
 
293
318
  if pending_sources
294
319
  spawn_fiber do
320
+ trace&.dataloader_spawn_source_fiber(pending_sources)
295
321
  pending_sources.each do |source|
296
322
  Fiber[:__graphql_current_dataloader_source] = source
323
+ trace&.begin_dataloader_source(source)
297
324
  source.run_pending_keys
325
+ trace&.end_dataloader_source(source)
298
326
  end
327
+ trace&.dataloader_fiber_exit
299
328
  end
300
329
  end
301
330
  end
@@ -22,7 +22,7 @@ module GraphQL
22
22
 
23
23
  if smallest_depth
24
24
  lazies = lazies_at_depth.delete(smallest_depth)
25
- if lazies.any?
25
+ if !lazies.empty?
26
26
  dataloader.append_job {
27
27
  lazies.each(&:value) # resolve these Lazy instances
28
28
  }
@@ -55,7 +55,7 @@ module GraphQL
55
55
  # these approaches.
56
56
  dataloader.run
57
57
  next_results = []
58
- while results.any?
58
+ while !results.empty?
59
59
  result_value = results.shift
60
60
  if result_value.is_a?(Runtime::GraphQLResultHash) || result_value.is_a?(Hash)
61
61
  results.concat(result_value.values)
@@ -81,7 +81,7 @@ module GraphQL
81
81
  end
82
82
  end
83
83
 
84
- if next_results.any?
84
+ if !next_results.empty?
85
85
  # Any pending data loader jobs may populate the
86
86
  # resutl arrays or result hashes accumulated in
87
87
  # `next_results``. Run those **to completion**
@@ -5,7 +5,10 @@ module GraphQL
5
5
  class Interpreter
6
6
  class Runtime
7
7
  module GraphQLResult
8
- def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager)
8
+ def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager, ast_node, graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
9
+ @ast_node = ast_node
10
+ @graphql_arguments = graphql_arguments
11
+ @graphql_field = graphql_field
9
12
  @graphql_parent = parent_result
10
13
  @graphql_application_value = application_value
11
14
  @graphql_result_type = result_type
@@ -31,14 +34,14 @@ module GraphQL
31
34
 
32
35
  attr_accessor :graphql_dead
33
36
  attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent,
34
- :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager
37
+ :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
35
38
 
36
39
  # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
37
40
  attr_accessor :graphql_result_data
38
41
  end
39
42
 
40
43
  class GraphQLResultHash
41
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
44
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
42
45
  super
43
46
  @graphql_result_data = {}
44
47
  end
@@ -126,7 +129,7 @@ module GraphQL
126
129
  class GraphQLResultArray
127
130
  include GraphQLResult
128
131
 
129
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
132
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
130
133
  super
131
134
  @graphql_result_data = []
132
135
  end
@@ -168,6 +171,10 @@ module GraphQL
168
171
  def values
169
172
  (@graphql_metadata || @graphql_result_data)
170
173
  end
174
+
175
+ def [](idx)
176
+ (@graphql_metadata || @graphql_result_data)[idx]
177
+ end
171
178
  end
172
179
  end
173
180
  end