graphql 2.0.27 → 2.2.6

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/install_generator.rb +3 -0
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  7. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  8. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  9. data/lib/generators/graphql/templates/base_field.erb +2 -0
  10. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  12. data/lib/generators/graphql/templates/base_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  14. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  15. data/lib/generators/graphql/templates/base_union.erb +2 -0
  16. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/node_type.erb +2 -0
  20. data/lib/generators/graphql/templates/query_type.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  23. data/lib/graphql/analysis/ast/field_usage.rb +32 -7
  24. data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
  25. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  26. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  27. data/lib/graphql/analysis/ast.rb +21 -11
  28. data/lib/graphql/backtrace/trace.rb +12 -15
  29. data/lib/graphql/coercion_error.rb +1 -9
  30. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  31. data/lib/graphql/dataloader/source.rb +11 -3
  32. data/lib/graphql/dataloader.rb +109 -142
  33. data/lib/graphql/duration_encoding_error.rb +16 -0
  34. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  35. data/lib/graphql/execution/interpreter/runtime.rb +70 -248
  36. data/lib/graphql/execution/interpreter.rb +91 -157
  37. data/lib/graphql/execution/lookahead.rb +88 -21
  38. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  39. data/lib/graphql/introspection/entry_points.rb +11 -5
  40. data/lib/graphql/introspection/schema_type.rb +3 -1
  41. data/lib/graphql/language/block_string.rb +34 -18
  42. data/lib/graphql/language/definition_slice.rb +1 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +37 -37
  44. data/lib/graphql/language/lexer.rb +271 -177
  45. data/lib/graphql/language/nodes.rb +74 -56
  46. data/lib/graphql/language/parser.rb +697 -1986
  47. data/lib/graphql/language/printer.rb +299 -146
  48. data/lib/graphql/language/sanitized_printer.rb +20 -22
  49. data/lib/graphql/language/static_visitor.rb +167 -0
  50. data/lib/graphql/language/visitor.rb +20 -81
  51. data/lib/graphql/language.rb +1 -0
  52. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  53. data/lib/graphql/pagination/array_connection.rb +3 -3
  54. data/lib/graphql/pagination/connection.rb +28 -1
  55. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  56. data/lib/graphql/pagination/relation_connection.rb +3 -3
  57. data/lib/graphql/query/context/scoped_context.rb +101 -0
  58. data/lib/graphql/query/context.rb +36 -98
  59. data/lib/graphql/query/null_context.rb +4 -11
  60. data/lib/graphql/query/validation_pipeline.rb +2 -2
  61. data/lib/graphql/query/variables.rb +3 -3
  62. data/lib/graphql/query.rb +13 -22
  63. data/lib/graphql/railtie.rb +9 -6
  64. data/lib/graphql/rake_task.rb +3 -12
  65. data/lib/graphql/schema/argument.rb +6 -1
  66. data/lib/graphql/schema/build_from_definition.rb +0 -11
  67. data/lib/graphql/schema/directive/one_of.rb +12 -0
  68. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  69. data/lib/graphql/schema/directive.rb +1 -1
  70. data/lib/graphql/schema/enum.rb +3 -3
  71. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  72. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  73. data/lib/graphql/schema/field.rb +8 -5
  74. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  75. data/lib/graphql/schema/input_object.rb +2 -2
  76. data/lib/graphql/schema/interface.rb +10 -10
  77. data/lib/graphql/schema/introspection_system.rb +2 -0
  78. data/lib/graphql/schema/loader.rb +0 -2
  79. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  81. data/lib/graphql/schema/member/has_fields.rb +8 -5
  82. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  83. data/lib/graphql/schema/member/scoped.rb +19 -0
  84. data/lib/graphql/schema/member/validates_input.rb +3 -3
  85. data/lib/graphql/schema/object.rb +8 -0
  86. data/lib/graphql/schema/printer.rb +8 -7
  87. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  88. data/lib/graphql/schema/resolver.rb +7 -3
  89. data/lib/graphql/schema/scalar.rb +3 -3
  90. data/lib/graphql/schema/subscription.rb +11 -4
  91. data/lib/graphql/schema/union.rb +1 -1
  92. data/lib/graphql/schema/warden.rb +96 -94
  93. data/lib/graphql/schema.rb +219 -72
  94. data/lib/graphql/static_validation/all_rules.rb +1 -1
  95. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  96. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  97. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  98. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  99. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/validation_context.rb +5 -5
  101. data/lib/graphql/static_validation/validator.rb +3 -0
  102. data/lib/graphql/static_validation.rb +0 -1
  103. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  104. data/lib/graphql/subscriptions/event.rb +8 -2
  105. data/lib/graphql/subscriptions.rb +14 -12
  106. data/lib/graphql/testing/helpers.rb +125 -0
  107. data/lib/graphql/testing.rb +2 -0
  108. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  109. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  110. data/lib/graphql/tracing/data_dog_trace.rb +21 -34
  111. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  112. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  113. data/lib/graphql/tracing/platform_tracing.rb +2 -0
  114. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  115. data/lib/graphql/tracing/sentry_trace.rb +94 -0
  116. data/lib/graphql/tracing/trace.rb +1 -0
  117. data/lib/graphql/tracing.rb +3 -1
  118. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  119. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  120. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  121. data/lib/graphql/types.rb +1 -0
  122. data/lib/graphql/version.rb +1 -1
  123. data/lib/graphql.rb +3 -3
  124. data/readme.md +12 -2
  125. metadata +33 -25
  126. data/lib/graphql/deprecation.rb +0 -9
  127. data/lib/graphql/filter.rb +0 -59
  128. data/lib/graphql/language/parser.y +0 -560
  129. data/lib/graphql/static_validation/type_stack.rb +0 -216
  130. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/execution/interpreter/runtime/graphql_result"
2
3
 
3
4
  module GraphQL
4
5
  module Execution
@@ -15,171 +16,11 @@ module GraphQL
15
16
  @current_arguments = nil
16
17
  @current_result_name = nil
17
18
  @current_result = nil
19
+ @was_authorized_by_scope_items = nil
18
20
  end
19
21
 
20
22
  attr_accessor :current_result, :current_result_name,
21
- :current_arguments, :current_field, :current_object
22
- end
23
-
24
- module GraphQLResult
25
- def initialize(result_name, parent_result, is_non_null_in_parent)
26
- @graphql_parent = parent_result
27
- if parent_result && parent_result.graphql_dead
28
- @graphql_dead = true
29
- end
30
- @graphql_result_name = result_name
31
- @graphql_is_non_null_in_parent = is_non_null_in_parent
32
- # Jump through some hoops to avoid creating this duplicate storage if at all possible.
33
- @graphql_metadata = nil
34
- end
35
-
36
- def path
37
- @path ||= build_path([])
38
- end
39
-
40
- def build_path(path_array)
41
- graphql_result_name && path_array.unshift(graphql_result_name)
42
- @graphql_parent ? @graphql_parent.build_path(path_array) : path_array
43
- end
44
-
45
- attr_accessor :graphql_dead
46
- attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
47
-
48
- # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
49
- attr_accessor :graphql_result_data
50
- end
51
-
52
- class GraphQLResultHash
53
- def initialize(_result_name, _parent_result, _is_non_null_in_parent)
54
- super
55
- @graphql_result_data = {}
56
- end
57
-
58
- include GraphQLResult
59
-
60
- attr_accessor :graphql_merged_into
61
-
62
- def set_leaf(key, value)
63
- # This is a hack.
64
- # Basically, this object is merged into the root-level result at some point.
65
- # But the problem is, some lazies are created whose closures retain reference to _this_
66
- # object. When those lazies are resolved, they cause an update to this object.
67
- #
68
- # In order to return a proper top-level result, we have to update that top-level result object.
69
- # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
70
- # Yowza.
71
- if (t = @graphql_merged_into)
72
- t.set_leaf(key, value)
73
- end
74
-
75
- @graphql_result_data[key] = value
76
- # keep this up-to-date if it's been initialized
77
- @graphql_metadata && @graphql_metadata[key] = value
78
-
79
- value
80
- end
81
-
82
- def set_child_result(key, value)
83
- if (t = @graphql_merged_into)
84
- t.set_child_result(key, value)
85
- end
86
- @graphql_result_data[key] = value.graphql_result_data
87
- # If we encounter some part of this response that requires metadata tracking,
88
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
89
- (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
90
- value
91
- end
92
-
93
- def delete(key)
94
- @graphql_metadata && @graphql_metadata.delete(key)
95
- @graphql_result_data.delete(key)
96
- end
97
-
98
- def each
99
- (@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
100
- end
101
-
102
- def values
103
- (@graphql_metadata || @graphql_result_data).values
104
- end
105
-
106
- def key?(k)
107
- @graphql_result_data.key?(k)
108
- end
109
-
110
- def [](k)
111
- (@graphql_metadata || @graphql_result_data)[k]
112
- end
113
-
114
- def merge_into(into_result)
115
- self.each do |key, value|
116
- case value
117
- when GraphQLResultHash
118
- next_into = into_result[key]
119
- if next_into
120
- value.merge_into(next_into)
121
- else
122
- into_result.set_child_result(key, value)
123
- end
124
- when GraphQLResultArray
125
- # There's no special handling of arrays because currently, there's no way to split the execution
126
- # of a list over several concurrent flows.
127
- next_result.set_child_result(key, value)
128
- else
129
- # We have to assume that, since this passed the `fields_will_merge` selection,
130
- # that the old and new values are the same.
131
- into_result.set_leaf(key, value)
132
- end
133
- end
134
- @graphql_merged_into = into_result
135
- end
136
- end
137
-
138
- class GraphQLResultArray
139
- include GraphQLResult
140
-
141
- def initialize(_result_name, _parent_result, _is_non_null_in_parent)
142
- super
143
- @graphql_result_data = []
144
- end
145
-
146
- def graphql_skip_at(index)
147
- # Mark this index as dead. It's tricky because some indices may already be storing
148
- # `Lazy`s. So the runtime is still holding indexes _before_ skipping,
149
- # this object has to coordinate incoming writes to account for any already-skipped indices.
150
- @skip_indices ||= []
151
- @skip_indices << index
152
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
153
- delete_at_index = index - offset_by
154
- @graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
155
- @graphql_result_data.delete_at(delete_at_index)
156
- end
157
-
158
- def set_leaf(idx, value)
159
- if @skip_indices
160
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
161
- idx -= offset_by
162
- end
163
- @graphql_result_data[idx] = value
164
- @graphql_metadata && @graphql_metadata[idx] = value
165
- value
166
- end
167
-
168
- def set_child_result(idx, value)
169
- if @skip_indices
170
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
171
- idx -= offset_by
172
- end
173
- @graphql_result_data[idx] = value.graphql_result_data
174
- # If we encounter some part of this response that requires metadata tracking,
175
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
176
- (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
177
- value
178
- end
179
-
180
- def values
181
- (@graphql_metadata || @graphql_result_data)
182
- end
23
+ :current_arguments, :current_field, :current_object, :was_authorized_by_scope_items
183
24
  end
184
25
 
185
26
  # @return [GraphQL::Query]
@@ -208,12 +49,6 @@ module GraphQL
208
49
  @runtime_directive_names << name
209
50
  end
210
51
  end
211
- # A cache of { Class => { String => Schema::Field } }
212
- # Which assumes that MyObject.get_field("myField") will return the same field
213
- # during the lifetime of a query
214
- @fields_cache = Hash.new { |h, k| h[k] = {} }
215
- # this can by by-identity since owners are the same object, but not the sub-hash, which uses strings.
216
- @fields_cache.compare_by_identity
217
52
  # { Class => Boolean }
218
53
  @lazy_cache = {}
219
54
  @lazy_cache.compare_by_identity
@@ -244,6 +79,7 @@ module GraphQL
244
79
  root_operation = query.selected_operation
245
80
  root_op_type = root_operation.operation_type || "query"
246
81
  root_type = schema.root_type_for_operation(root_op_type)
82
+
247
83
  st = get_current_runtime_state
248
84
  st.current_object = query.root_value
249
85
  st.current_result = @response
@@ -275,6 +111,7 @@ module GraphQL
275
111
  @dataloader.append_job {
276
112
  st = get_current_runtime_state
277
113
  st.current_object = query.root_value
114
+ st.current_result_name = nil
278
115
  st.current_result = selection_response
279
116
  # This is a less-frequent case; use a fast check since it's often not there.
280
117
  if (directives = selections[:graphql_directives])
@@ -289,17 +126,18 @@ module GraphQL
289
126
  selection_response,
290
127
  final_response,
291
128
  nil,
129
+ st,
292
130
  )
293
131
  end
294
132
  }
295
133
  end
296
134
  end
297
135
  end
298
- delete_all_interpreter_context
299
136
  nil
300
137
  end
301
138
 
302
139
  def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
140
+
303
141
  selections.each do |node|
304
142
  # Skip gathering this if the directive says so
305
143
  if !directives_include?(node, owner_object, owner_type)
@@ -367,18 +205,14 @@ module GraphQL
367
205
  NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
368
206
 
369
207
  # @return [void]
370
- def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
371
- st = get_current_runtime_state
372
- st.current_object = owner_object
373
- st.current_result_name = nil
374
- st.current_result = selections_result
375
-
208
+ def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
376
209
  finished_jobs = 0
377
210
  enqueued_jobs = gathered_selections.size
378
211
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
379
212
  @dataloader.append_job {
213
+ runtime_state = get_current_runtime_state
380
214
  evaluate_selection(
381
- result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
215
+ result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object, runtime_state
382
216
  )
383
217
  finished_jobs += 1
384
218
  if target_result && finished_jobs == enqueued_jobs
@@ -389,7 +223,9 @@ module GraphQL
389
223
  # so it wouldn't get to the `Resolve` call that happens below.
390
224
  # So instead trigger a run from this outer context.
391
225
  if is_eager_selection
226
+ @dataloader.clear_cache
392
227
  @dataloader.run
228
+ @dataloader.clear_cache
393
229
  end
394
230
  end
395
231
 
@@ -397,7 +233,7 @@ module GraphQL
397
233
  end
398
234
 
399
235
  # @return [void]
400
- def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
236
+ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
401
237
  return if dead_result?(selections_result)
402
238
  # As a performance optimization, the hash key will be a `Node` if
403
239
  # there's only one selection of the field. But if there are multiple
@@ -410,57 +246,38 @@ module GraphQL
410
246
  ast_node = field_ast_nodes_or_ast_node
411
247
  end
412
248
  field_name = ast_node.name
413
- # This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
414
- # because of how `is_introspection` is used to call `.authorized_new` later on.
415
- field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
416
- is_introspection = false
417
- if field_defn.nil?
418
- field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
419
- is_introspection = true
420
- entry_point_field
421
- elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
422
- is_introspection = true
423
- dynamic_field
424
- else
425
- raise "Invariant: no field for #{owner_type}.#{field_name}"
426
- end
427
- end
249
+ field_defn = query.warden.get_field(owner_type, field_name)
428
250
 
429
- return_type = field_defn.type
430
-
431
- # This seems janky, but we need to know
432
- # the field's return type at this path in order
433
- # to propagate `null`
434
- return_type_non_null = return_type.non_null?
435
251
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
436
- st = get_current_runtime_state
437
- st.current_field = field_defn
438
- st.current_result = selections_result
439
- st.current_result_name = result_name
252
+ runtime_state.current_field = field_defn
253
+ runtime_state.current_result = selections_result
254
+ runtime_state.current_result_name = result_name
440
255
 
441
- if is_introspection
256
+ if field_defn.dynamic_introspection
442
257
  owner_object = field_defn.owner.wrap(owner_object, context)
443
258
  end
444
259
 
445
- total_args_count = field_defn.arguments(context).size
446
- if total_args_count == 0
260
+ return_type = field_defn.type
261
+ if !field_defn.any_arguments?
447
262
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
448
263
  if field_defn.extras.size == 0
449
264
  evaluate_selection_with_resolved_keyword_args(
450
- NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
265
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type.non_null?, runtime_state
451
266
  )
452
267
  else
453
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
268
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
454
269
  end
455
270
  else
456
271
  @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
457
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
272
+ runtime_state = get_current_runtime_state # This might be in a different fiber
273
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
458
274
  end
459
275
  end
460
276
  end
461
277
 
462
- def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
463
- after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
278
+ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, runtime_state) # rubocop:disable Metrics/ParameterLists
279
+ 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|
280
+ return_type_non_null = return_type.non_null?
464
281
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
465
282
  continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
466
283
  next
@@ -511,17 +328,16 @@ module GraphQL
511
328
  resolved_arguments.keyword_arguments
512
329
  end
513
330
 
514
- evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
331
+ evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state)
515
332
  end
516
333
  end
517
334
 
518
- def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
519
- st = get_current_runtime_state
520
- st.current_field = field_defn
521
- st.current_object = object
522
- st.current_arguments = resolved_arguments
523
- st.current_result_name = result_name
524
- st.current_result = selection_result
335
+ def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state) # rubocop:disable Metrics/ParameterLists
336
+ runtime_state.current_field = field_defn
337
+ runtime_state.current_object = object
338
+ runtime_state.current_arguments = resolved_arguments
339
+ runtime_state.current_result_name = result_name
340
+ runtime_state.current_result = selection_result
525
341
  # Optimize for the case that field is selected only once
526
342
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
527
343
  next_selections = ast_node.selections
@@ -550,10 +366,12 @@ module GraphQL
550
366
  ex_err
551
367
  end
552
368
  end
553
- after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
369
+ 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|
554
370
  continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
555
371
  if HALT != continue_value
556
- continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
372
+ was_scoped = runtime_state.was_authorized_by_scope_items
373
+ runtime_state.was_authorized_by_scope_items = nil
374
+ 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)
557
375
  end
558
376
  end
559
377
  end
@@ -695,7 +513,7 @@ module GraphQL
695
513
  end
696
514
  when Array
697
515
  # It's an array full of execution errors; add them all.
698
- if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
516
+ if value.any? && value.all?(GraphQL::ExecutionError)
699
517
  list_type_at_all = (field && (field.type.list?))
700
518
  if selection_result.nil? || !dead_result?(selection_result)
701
519
  value.each_with_index do |error, index|
@@ -733,7 +551,7 @@ module GraphQL
733
551
  # Location information from `path` and `ast_node`.
734
552
  #
735
553
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
736
- def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
554
+ def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
737
555
  if current_type.non_null?
738
556
  current_type = current_type.of_type
739
557
  is_non_null = true
@@ -750,7 +568,7 @@ module GraphQL
750
568
  r
751
569
  when "UNION", "INTERFACE"
752
570
  resolved_type_or_lazy = resolve_type(current_type, value)
753
- 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) do |resolved_type_result|
571
+ 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|
754
572
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
755
573
  resolved_type, resolved_value = resolved_type_result
756
574
  else
@@ -767,20 +585,21 @@ module GraphQL
767
585
  set_result(selection_result, result_name, nil, false, is_non_null)
768
586
  nil
769
587
  else
770
- continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
588
+ continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state)
771
589
  end
772
590
  end
773
591
  when "OBJECT"
774
592
  object_proxy = begin
775
- current_type.wrap(value, context)
593
+ was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
776
594
  rescue GraphQL::ExecutionError => err
777
595
  err
778
596
  end
779
- 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) do |inner_object|
597
+ 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|
780
598
  continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
781
599
  if HALT != continue_value
782
600
  response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
783
601
  set_result(selection_result, result_name, response_hash, true, is_non_null)
602
+
784
603
  gathered_selections = gather_selections(continue_value, current_type, next_selections)
785
604
  # There are two possibilities for `gathered_selections`:
786
605
  # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
@@ -800,11 +619,9 @@ module GraphQL
800
619
  end
801
620
  # reset this mutable state
802
621
  # Unset `result_name` here because it's already included in the new response hash
803
- st = get_current_runtime_state
804
- st.current_object = continue_value
805
- st.current_result_name = nil
806
- st.current_result = this_result
807
-
622
+ runtime_state.current_object = continue_value
623
+ runtime_state.current_result_name = nil
624
+ runtime_state.current_result = this_result
808
625
  # This is a less-frequent case; use a fast check since it's often not there.
809
626
  if (directives = selections[:graphql_directives])
810
627
  selections.delete(:graphql_directives)
@@ -818,6 +635,7 @@ module GraphQL
818
635
  this_result,
819
636
  final_result,
820
637
  owner_object.object,
638
+ runtime_state,
821
639
  )
822
640
  this_result
823
641
  end
@@ -839,10 +657,10 @@ module GraphQL
839
657
  idx += 1
840
658
  if use_dataloader_job
841
659
  @dataloader.append_job do
842
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
660
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state)
843
661
  end
844
662
  else
845
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
663
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state)
846
664
  end
847
665
  end
848
666
 
@@ -873,16 +691,15 @@ module GraphQL
873
691
  end
874
692
  end
875
693
 
876
- def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
877
- st = get_current_runtime_state
878
- st.current_result_name = this_idx
879
- st.current_result = response_list
694
+ def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
695
+ runtime_state.current_result_name = this_idx
696
+ runtime_state.current_result = response_list
880
697
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
881
698
  # This will update `response_list` with the lazy
882
- after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
699
+ after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
883
700
  continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
884
701
  if HALT != continue_value
885
- continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
702
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
886
703
  end
887
704
  end
888
705
  end
@@ -959,16 +776,21 @@ module GraphQL
959
776
  # @param eager [Boolean] Set to `true` for mutation root fields only
960
777
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
961
778
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
962
- def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
779
+ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
963
780
  if lazy?(lazy_obj)
964
781
  orig_result = result
782
+ was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
965
783
  lazy = GraphQL::Execution::Lazy.new(field: field) do
966
- st = get_current_runtime_state
967
- st.current_object = owner_object
968
- st.current_field = field
969
- st.current_arguments = arguments
970
- st.current_result_name = result_name
971
- st.current_result = orig_result
784
+ # This block might be called in a new fiber;
785
+ # In that case, this will initialize a new state
786
+ # to avoid conflicting with the parent fiber.
787
+ runtime_state = get_current_runtime_state
788
+ runtime_state.current_object = owner_object
789
+ runtime_state.current_field = field
790
+ runtime_state.current_arguments = arguments
791
+ runtime_state.current_result_name = result_name
792
+ runtime_state.current_result = orig_result
793
+ runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
972
794
  # Wrap the execution of _this_ method with tracing,
973
795
  # but don't wrap the continuation below
974
796
  inner_obj = begin
@@ -988,7 +810,7 @@ module GraphQL
988
810
  ex_err
989
811
  end
990
812
  end
991
- yield(inner_obj)
813
+ yield(inner_obj, runtime_state)
992
814
  end
993
815
 
994
816
  if eager
@@ -1005,7 +827,7 @@ module GraphQL
1005
827
  end
1006
828
  else
1007
829
  # Don't need to reset state here because it _wasn't_ lazy.
1008
- yield(lazy_obj)
830
+ yield(lazy_obj, runtime_state)
1009
831
  end
1010
832
  end
1011
833