graphql 2.4.2 → 2.5.0

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 (166) 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 +118 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  10. data/lib/graphql/dashboard/installable.rb +22 -0
  11. data/lib/graphql/dashboard/limiters.rb +93 -0
  12. data/lib/graphql/dashboard/operation_store.rb +199 -0
  13. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  14. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  15. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  16. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  17. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  18. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  19. data/lib/graphql/dashboard/statics/icon.png +0 -0
  20. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  36. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  37. data/lib/graphql/dashboard.rb +158 -0
  38. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  39. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  40. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/source.rb +3 -3
  43. data/lib/graphql/dataloader.rb +43 -14
  44. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  45. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
  46. data/lib/graphql/execution/interpreter/runtime.rb +78 -41
  47. data/lib/graphql/execution/interpreter.rb +16 -7
  48. data/lib/graphql/execution/lookahead.rb +8 -1
  49. data/lib/graphql/execution/multiplex.rb +1 -5
  50. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  51. data/lib/graphql/invalid_name_error.rb +1 -1
  52. data/lib/graphql/invalid_null_error.rb +5 -15
  53. data/lib/graphql/language/cache.rb +13 -0
  54. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  55. data/lib/graphql/language/lexer.rb +11 -4
  56. data/lib/graphql/language/nodes.rb +3 -0
  57. data/lib/graphql/language/parser.rb +15 -8
  58. data/lib/graphql/language/printer.rb +8 -8
  59. data/lib/graphql/language/static_visitor.rb +37 -33
  60. data/lib/graphql/language/visitor.rb +59 -55
  61. data/lib/graphql/pagination/connection.rb +1 -1
  62. data/lib/graphql/query/context/scoped_context.rb +1 -1
  63. data/lib/graphql/query/context.rb +6 -5
  64. data/lib/graphql/query/variable_validation_error.rb +1 -1
  65. data/lib/graphql/query.rb +19 -23
  66. data/lib/graphql/railtie.rb +7 -0
  67. data/lib/graphql/schema/addition.rb +1 -1
  68. data/lib/graphql/schema/argument.rb +7 -8
  69. data/lib/graphql/schema/build_from_definition.rb +99 -53
  70. data/lib/graphql/schema/directive/flagged.rb +1 -1
  71. data/lib/graphql/schema/directive.rb +2 -2
  72. data/lib/graphql/schema/enum.rb +36 -1
  73. data/lib/graphql/schema/enum_value.rb +1 -1
  74. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  75. data/lib/graphql/schema/field.rb +12 -12
  76. data/lib/graphql/schema/field_extension.rb +1 -1
  77. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  78. data/lib/graphql/schema/input_object.rb +76 -39
  79. data/lib/graphql/schema/interface.rb +3 -2
  80. data/lib/graphql/schema/loader.rb +1 -1
  81. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  82. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  83. data/lib/graphql/schema/member/has_directives.rb +4 -4
  84. data/lib/graphql/schema/member/has_fields.rb +19 -1
  85. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  86. data/lib/graphql/schema/member/has_validators.rb +1 -1
  87. data/lib/graphql/schema/member/scoped.rb +1 -1
  88. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  89. data/lib/graphql/schema/member.rb +1 -0
  90. data/lib/graphql/schema/object.rb +25 -8
  91. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  92. data/lib/graphql/schema/resolver.rb +11 -10
  93. data/lib/graphql/schema/subscription.rb +52 -6
  94. data/lib/graphql/schema/union.rb +1 -1
  95. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  96. data/lib/graphql/schema/validator.rb +1 -1
  97. data/lib/graphql/schema/visibility/migration.rb +1 -0
  98. data/lib/graphql/schema/visibility/profile.rb +69 -237
  99. data/lib/graphql/schema/visibility/visit.rb +190 -0
  100. data/lib/graphql/schema/visibility.rb +184 -28
  101. data/lib/graphql/schema/warden.rb +18 -5
  102. data/lib/graphql/schema.rb +94 -45
  103. data/lib/graphql/static_validation/all_rules.rb +1 -1
  104. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  105. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  106. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  107. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  108. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  109. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  110. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  111. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  112. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  113. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  114. data/lib/graphql/static_validation/validation_context.rb +1 -0
  115. data/lib/graphql/static_validation/validator.rb +6 -1
  116. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  117. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  118. data/lib/graphql/subscriptions/event.rb +12 -1
  119. data/lib/graphql/subscriptions/serialize.rb +1 -1
  120. data/lib/graphql/subscriptions.rb +1 -1
  121. data/lib/graphql/testing/helpers.rb +7 -4
  122. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  123. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  124. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  125. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  126. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  127. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  128. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  129. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  130. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  131. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  132. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  133. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  134. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  135. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  136. data/lib/graphql/tracing/monitor_trace.rb +285 -0
  137. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  138. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  139. data/lib/graphql/tracing/notifications_trace.rb +182 -34
  140. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  141. data/lib/graphql/tracing/null_trace.rb +9 -0
  142. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  143. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  144. data/lib/graphql/tracing/perfetto_trace.rb +734 -0
  145. data/lib/graphql/tracing/platform_trace.rb +5 -0
  146. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  147. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  148. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  149. data/lib/graphql/tracing/scout_trace.rb +32 -55
  150. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  151. data/lib/graphql/tracing/sentry_trace.rb +63 -94
  152. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  153. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  154. data/lib/graphql/tracing/trace.rb +111 -1
  155. data/lib/graphql/tracing.rb +31 -30
  156. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  157. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  158. data/lib/graphql/types.rb +18 -11
  159. data/lib/graphql/version.rb +1 -1
  160. data/lib/graphql.rb +55 -47
  161. metadata +146 -11
  162. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  163. data/lib/graphql/backtrace/trace.rb +0 -93
  164. data/lib/graphql/backtrace/tracer.rb +0 -80
  165. data/lib/graphql/schema/null_mask.rb +0 -11
  166. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -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
@@ -74,7 +74,7 @@ module GraphQL
74
74
  runtime_object = root_type.wrap(query.root_value, context)
75
75
  runtime_object = schema.sync_lazy(runtime_object)
76
76
  is_eager = root_op_type == "mutation"
77
- @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
77
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
78
78
  st = get_current_runtime_state
79
79
  st.current_result = @response
80
80
 
@@ -85,7 +85,7 @@ module GraphQL
85
85
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
86
  each_gathered_selections(@response) do |selections, is_selection_array|
87
87
  if is_selection_array
88
- selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
88
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
89
89
  final_response = @response
90
90
  else
91
91
  selection_response = @response
@@ -142,7 +142,7 @@ module GraphQL
142
142
  end
143
143
  else
144
144
  # This is an InlineFragment or a FragmentSpread
145
- if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
145
+ if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
146
146
  next_selections = {}
147
147
  next_selections[:graphql_directives] = node.directives
148
148
  if selections_to_run
@@ -218,8 +218,10 @@ module GraphQL
218
218
  result_name, field_ast_nodes_or_ast_node, selections_result
219
219
  )
220
220
  finished_jobs += 1
221
- if target_result && finished_jobs == enqueued_jobs
222
- selections_result.merge_into(target_result)
221
+ if finished_jobs == enqueued_jobs
222
+ if target_result
223
+ selections_result.merge_into(target_result)
224
+ end
223
225
  end
224
226
  @dataloader.clear_cache
225
227
  }
@@ -229,8 +231,10 @@ module GraphQL
229
231
  result_name, field_ast_nodes_or_ast_node, selections_result
230
232
  )
231
233
  finished_jobs += 1
232
- if target_result && finished_jobs == enqueued_jobs
233
- selections_result.merge_into(target_result)
234
+ if finished_jobs == enqueued_jobs
235
+ if target_result
236
+ selections_result.merge_into(target_result)
237
+ end
234
238
  end
235
239
  }
236
240
  end
@@ -332,7 +336,7 @@ module GraphQL
332
336
  extra_args[extra] = field_defn.fetch_extra(extra, context)
333
337
  end
334
338
  end
335
- if extra_args.any?
339
+ if !extra_args.empty?
336
340
  resolved_arguments = resolved_arguments.merge_extras(extra_args)
337
341
  end
338
342
  resolved_arguments.keyword_arguments
@@ -361,7 +365,7 @@ module GraphQL
361
365
  end
362
366
 
363
367
  field_result = call_method_on_directives(:resolve, object, directives) do
364
- if directives.any?
368
+ if !directives.empty?
365
369
  # This might be executed in a different context; reset this info
366
370
  runtime_state = get_current_runtime_state
367
371
  runtime_state.current_field = field_defn
@@ -371,6 +375,7 @@ module GraphQL
371
375
  end
372
376
  # Actually call the field resolver and capture the result
373
377
  app_result = begin
378
+ @current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
374
379
  @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
375
380
  field_defn.resolve(object, kwarg_arguments, context)
376
381
  end
@@ -383,6 +388,7 @@ module GraphQL
383
388
  ex_err
384
389
  end
385
390
  end
391
+ @current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
386
392
  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|
387
393
  owner_type = selection_result.graphql_result_type
388
394
  return_type = field_defn.type
@@ -391,6 +397,8 @@ module GraphQL
391
397
  was_scoped = runtime_state.was_authorized_by_scope_items
392
398
  runtime_state.was_authorized_by_scope_items = nil
393
399
  continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
400
+ else
401
+ nil
394
402
  end
395
403
  end
396
404
  end
@@ -465,7 +473,7 @@ module GraphQL
465
473
  # When this comes from a list item, use the parent object:
466
474
  parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
467
475
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
468
- err = parent_type::InvalidNullError.new(parent_type, field, value)
476
+ err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
469
477
  schema.type_error(err, context)
470
478
  end
471
479
  else
@@ -525,7 +533,7 @@ module GraphQL
525
533
  end
526
534
  when Array
527
535
  # It's an array full of execution errors; add them all.
528
- if value.any? && value.all?(GraphQL::ExecutionError)
536
+ if !value.empty? && value.all?(GraphQL::ExecutionError)
529
537
  list_type_at_all = (field && (field.type.list?))
530
538
  if selection_result.nil? || !selection_result.graphql_dead
531
539
  value.each_with_index do |error, index|
@@ -574,12 +582,22 @@ module GraphQL
574
582
  r = begin
575
583
  current_type.coerce_result(value, context)
576
584
  rescue StandardError => err
577
- schema.handle_or_reraise(context, err)
585
+ query.handle_or_reraise(err)
578
586
  end
579
587
  set_result(selection_result, result_name, r, false, is_non_null)
580
588
  r
581
589
  when "UNION", "INTERFACE"
582
- resolved_type_or_lazy = resolve_type(current_type, value)
590
+ resolved_type_or_lazy = begin
591
+ resolve_type(current_type, value)
592
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
593
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
594
+ rescue StandardError => err
595
+ begin
596
+ query.handle_or_reraise(err)
597
+ rescue GraphQL::ExecutionError => ex_err
598
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
599
+ end
600
+ end
583
601
  after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
584
602
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
585
603
  resolved_type, resolved_value = resolved_type_result
@@ -609,11 +627,11 @@ module GraphQL
609
627
  after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
610
628
  continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
611
629
  if HALT != continue_value
612
- response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
630
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
613
631
  set_result(selection_result, result_name, response_hash, true, is_non_null)
614
632
  each_gathered_selections(response_hash) do |selections, is_selection_array|
615
633
  if is_selection_array
616
- this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
634
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
617
635
  final_result = response_hash
618
636
  else
619
637
  this_result = response_hash
@@ -634,35 +652,43 @@ module GraphQL
634
652
  # This is true for objects, unions, and interfaces
635
653
  use_dataloader_job = !inner_type.unwrap.kind.input?
636
654
  inner_type_non_null = inner_type.non_null?
637
- response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
655
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
638
656
  set_result(selection_result, result_name, response_list, true, is_non_null)
639
657
  idx = nil
640
658
  list_value = begin
641
- value.each do |inner_value|
642
- idx ||= 0
643
- this_idx = idx
644
- idx += 1
645
- if use_dataloader_job
646
- @dataloader.append_job do
659
+ begin
660
+ value.each do |inner_value|
661
+ idx ||= 0
662
+ this_idx = idx
663
+ idx += 1
664
+ if use_dataloader_job
665
+ @dataloader.append_job do
666
+ 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)
667
+ end
668
+ else
647
669
  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)
648
670
  end
649
- else
650
- 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)
651
671
  end
652
- end
653
672
 
654
- response_list
655
- rescue NoMethodError => err
656
- # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
657
- if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
658
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
659
- raise ListResultFailedError.new(value: value, field: field, path: current_path)
660
- else
661
- # This was some other NoMethodError -- let it bubble to reveal the real error.
662
- raise
673
+ response_list
674
+ rescue NoMethodError => err
675
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
676
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
677
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
678
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
679
+ else
680
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
681
+ raise
682
+ end
683
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
684
+ ex_err
685
+ rescue StandardError => err
686
+ begin
687
+ query.handle_or_reraise(err)
688
+ rescue GraphQL::ExecutionError => ex_err
689
+ ex_err
690
+ end
663
691
  end
664
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
665
- ex_err
666
692
  rescue StandardError => err
667
693
  begin
668
694
  query.handle_or_reraise(err)
@@ -736,7 +762,7 @@ module GraphQL
736
762
  end
737
763
 
738
764
  def get_current_runtime_state
739
- current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
765
+ current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
740
766
  current_state[@query] ||= CurrentState.new
741
767
  end
742
768
 
@@ -773,8 +799,10 @@ module GraphQL
773
799
  runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
774
800
  # Wrap the execution of _this_ method with tracing,
775
801
  # but don't wrap the continuation below
802
+ result = nil
776
803
  inner_obj = begin
777
- if trace
804
+ result = if trace
805
+ @current_trace.begin_execute_field(field, owner_object, arguments, query)
778
806
  @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
779
807
  schema.sync_lazy(lazy_obj)
780
808
  end
@@ -789,6 +817,10 @@ module GraphQL
789
817
  rescue GraphQL::ExecutionError => ex_err
790
818
  ex_err
791
819
  end
820
+ ensure
821
+ if trace
822
+ @current_trace.end_execute_field(field, owner_object, arguments, query, result)
823
+ end
792
824
  end
793
825
  yield(inner_obj, runtime_state)
794
826
  end
@@ -821,25 +853,30 @@ module GraphQL
821
853
  end
822
854
 
823
855
  def delete_all_interpreter_context
824
- per_query_state = Thread.current[:__graphql_runtime_info]
856
+ per_query_state = Fiber[:__graphql_runtime_info]
825
857
  if per_query_state
826
858
  per_query_state.delete(@query)
827
859
  if per_query_state.size == 0
828
- Thread.current[:__graphql_runtime_info] = nil
860
+ Fiber[:__graphql_runtime_info] = nil
829
861
  end
830
862
  end
831
863
  nil
832
864
  end
833
865
 
834
866
  def resolve_type(type, value)
867
+ @current_trace.begin_resolve_type(type, value, context)
835
868
  resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
836
869
  query.resolve_type(type, value)
837
870
  end
871
+ @current_trace.end_resolve_type(type, value, context, resolved_type)
838
872
 
839
873
  if lazy?(resolved_type)
840
874
  GraphQL::Execution::Lazy.new do
875
+ @current_trace.begin_resolve_type(type, value, context)
841
876
  @current_trace.resolve_type_lazy(query: query, type: type, object: value) do
842
- schema.sync_lazy(resolved_type)
877
+ rt = schema.sync_lazy(resolved_type)
878
+ @current_trace.end_resolve_type(type, value, context, rt)
879
+ rt
843
880
  end
844
881
  end
845
882
  else
@@ -23,7 +23,7 @@ module GraphQL
23
23
  # @return [Array<GraphQL::Query::Result>] One result per query
24
24
  def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
25
25
  queries = query_options.map do |opts|
26
- case opts
26
+ query = case opts
27
27
  when Hash
28
28
  schema.query_class.new(schema, nil, **opts)
29
29
  when GraphQL::Query
@@ -31,11 +31,15 @@ module GraphQL
31
31
  else
32
32
  raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
33
33
  end
34
+ query
34
35
  end
35
36
 
37
+ return GraphQL::EmptyObjects::EMPTY_ARRAY if queries.empty?
38
+
36
39
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
40
+ trace = multiplex.current_trace
37
41
  Fiber[:__graphql_current_multiplex] = multiplex
38
- multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
42
+ trace.execute_multiplex(multiplex: multiplex) do
39
43
  schema = multiplex.schema
40
44
  queries = multiplex.queries
41
45
  lazies_at_depth = Hash.new { |h, k| h[k] = [] }
@@ -44,7 +48,10 @@ module GraphQL
44
48
  multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
45
49
  end
46
50
 
51
+ trace.begin_analyze_multiplex(multiplex, multiplex_analyzers)
47
52
  schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
53
+ trace.end_analyze_multiplex(multiplex, multiplex_analyzers)
54
+
48
55
  begin
49
56
  # Since this is basically the batching context,
50
57
  # share it for a whole multiplex
@@ -53,11 +60,13 @@ module GraphQL
53
60
  results = []
54
61
  queries.each_with_index do |query, idx|
55
62
  if query.subscription? && !query.subscription_update?
56
- query.context.namespace(:subscriptions)[:events] = []
63
+ subs_namespace = query.context.namespace(:subscriptions)
64
+ subs_namespace[:events] = []
65
+ subs_namespace[:subscriptions] = {}
57
66
  end
58
67
  multiplex.dataloader.append_job {
59
68
  operation = query.selected_operation
60
- result = if operation.nil? || !query.valid? || query.context.errors.any?
69
+ result = if operation.nil? || !query.valid? || !query.context.errors.empty?
61
70
  NO_OPERATION
62
71
  else
63
72
  begin
@@ -100,12 +109,12 @@ module GraphQL
100
109
  # Then, find all errors and assign the result to the query object
101
110
  results.each_with_index do |data_result, idx|
102
111
  query = queries[idx]
103
- if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
112
+ if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
104
113
  schema.subscriptions.write_subscription(query, events)
105
114
  end
106
115
  # Assign the result so that it can be accessed in instrumentation
107
116
  query.result_values = if data_result.equal?(NO_OPERATION)
108
- if !query.valid? || query.context.errors.any?
117
+ if !query.valid? || !query.context.errors.empty?
109
118
  # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
110
119
  { "errors" => query.static_errors.map(&:to_h) }
111
120
  else
@@ -114,7 +123,7 @@ module GraphQL
114
123
  else
115
124
  result = {}
116
125
 
117
- if query.context.errors.any?
126
+ if !query.context.errors.empty?
118
127
  error_result = query.context.errors.map(&:to_h)
119
128
  result["errors"] = error_result
120
129
  end
@@ -56,7 +56,14 @@ module GraphQL
56
56
  else
57
57
  @arguments = if @field
58
58
  @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
- args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
59
+ case args
60
+ when Execution::Interpreter::Arguments
61
+ args.keyword_arguments
62
+ when GraphQL::ExecutionError
63
+ EmptyObjects::EMPTY_HASH
64
+ else
65
+ args
66
+ end
60
67
  end
61
68
  else
62
69
  nil
@@ -32,14 +32,10 @@ module GraphQL
32
32
  @queries = queries
33
33
  @queries.each { |q| q.multiplex = self }
34
34
  @context = context
35
- @current_trace = @context[:trace] || schema.new_trace(multiplex: self)
36
35
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
37
36
  @tracers = schema.tracers + (context[:tracers] || [])
38
- # Support `context: {backtrace: true}`
39
- if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
40
- @tracers << GraphQL::Backtrace::Tracer
41
- end
42
37
  @max_complexity = max_complexity
38
+ @current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
43
39
  end
44
40
  end
45
41
  end
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a __DirectiveLocation describes one such possible adjacencies."
8
8
 
9
9
  GraphQL::Schema::Directive::LOCATIONS.each do |location|
10
- value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
10
+ value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
11
11
  end
12
12
  introspection true
13
13
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- class InvalidNameError < GraphQL::ExecutionError
3
+ class InvalidNameError < GraphQL::Error
4
4
  attr_reader :name, :valid_regex
5
5
  def initialize(name, valid_regex)
6
6
  @name = name