graphql 2.0.30 → 2.3.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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  7. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  8. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  9. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  10. data/lib/generators/graphql/templates/base_field.erb +2 -0
  11. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  13. data/lib/generators/graphql/templates/base_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  15. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  16. data/lib/generators/graphql/templates/base_union.erb +2 -0
  17. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  18. data/lib/generators/graphql/templates/loader.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation.erb +2 -0
  20. data/lib/generators/graphql/templates/node_type.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/schema.erb +5 -0
  23. data/lib/graphql/analysis/analyzer.rb +89 -0
  24. data/lib/graphql/analysis/field_usage.rb +82 -0
  25. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  26. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  27. data/lib/graphql/analysis/query_complexity.rb +183 -0
  28. data/lib/graphql/analysis/query_depth.rb +58 -0
  29. data/lib/graphql/analysis/visitor.rb +282 -0
  30. data/lib/graphql/analysis.rb +92 -1
  31. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  32. data/lib/graphql/backtrace/trace.rb +12 -15
  33. data/lib/graphql/coercion_error.rb +1 -9
  34. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  35. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  36. data/lib/graphql/dataloader/request.rb +5 -0
  37. data/lib/graphql/dataloader/source.rb +11 -3
  38. data/lib/graphql/dataloader.rb +112 -142
  39. data/lib/graphql/duration_encoding_error.rb +16 -0
  40. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  41. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  42. data/lib/graphql/execution/interpreter/runtime.rb +163 -365
  43. data/lib/graphql/execution/interpreter.rb +92 -158
  44. data/lib/graphql/execution/lookahead.rb +88 -21
  45. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  46. data/lib/graphql/introspection/entry_points.rb +11 -5
  47. data/lib/graphql/introspection/schema_type.rb +3 -1
  48. data/lib/graphql/language/block_string.rb +34 -18
  49. data/lib/graphql/language/definition_slice.rb +1 -1
  50. data/lib/graphql/language/document_from_schema_definition.rb +38 -38
  51. data/lib/graphql/language/lexer.rb +305 -193
  52. data/lib/graphql/language/nodes.rb +113 -66
  53. data/lib/graphql/language/parser.rb +787 -1986
  54. data/lib/graphql/language/printer.rb +303 -146
  55. data/lib/graphql/language/sanitized_printer.rb +20 -22
  56. data/lib/graphql/language/static_visitor.rb +167 -0
  57. data/lib/graphql/language/visitor.rb +20 -81
  58. data/lib/graphql/language.rb +61 -0
  59. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  60. data/lib/graphql/pagination/array_connection.rb +6 -6
  61. data/lib/graphql/pagination/connection.rb +28 -1
  62. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  63. data/lib/graphql/query/context/scoped_context.rb +101 -0
  64. data/lib/graphql/query/context.rb +66 -131
  65. data/lib/graphql/query/null_context.rb +4 -11
  66. data/lib/graphql/query/validation_pipeline.rb +4 -4
  67. data/lib/graphql/query/variables.rb +3 -3
  68. data/lib/graphql/query.rb +17 -26
  69. data/lib/graphql/railtie.rb +9 -6
  70. data/lib/graphql/rake_task.rb +3 -12
  71. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  72. data/lib/graphql/schema/addition.rb +21 -11
  73. data/lib/graphql/schema/argument.rb +43 -8
  74. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  75. data/lib/graphql/schema/build_from_definition.rb +9 -12
  76. data/lib/graphql/schema/directive/one_of.rb +12 -0
  77. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  78. data/lib/graphql/schema/directive.rb +3 -1
  79. data/lib/graphql/schema/enum.rb +3 -3
  80. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  81. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  82. data/lib/graphql/schema/field.rb +49 -35
  83. data/lib/graphql/schema/has_single_input_argument.rb +157 -0
  84. data/lib/graphql/schema/input_object.rb +4 -4
  85. data/lib/graphql/schema/interface.rb +10 -10
  86. data/lib/graphql/schema/introspection_system.rb +4 -2
  87. data/lib/graphql/schema/late_bound_type.rb +4 -0
  88. data/lib/graphql/schema/list.rb +2 -2
  89. data/lib/graphql/schema/loader.rb +2 -3
  90. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  91. data/lib/graphql/schema/member/has_arguments.rb +63 -73
  92. data/lib/graphql/schema/member/has_directives.rb +1 -1
  93. data/lib/graphql/schema/member/has_fields.rb +8 -5
  94. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  95. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  96. data/lib/graphql/schema/member/scoped.rb +19 -0
  97. data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
  98. data/lib/graphql/schema/member/validates_input.rb +3 -3
  99. data/lib/graphql/schema/mutation.rb +7 -0
  100. data/lib/graphql/schema/object.rb +8 -0
  101. data/lib/graphql/schema/printer.rb +8 -7
  102. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  103. data/lib/graphql/schema/resolver.rb +27 -13
  104. data/lib/graphql/schema/scalar.rb +3 -3
  105. data/lib/graphql/schema/subscription.rb +11 -4
  106. data/lib/graphql/schema/union.rb +1 -1
  107. data/lib/graphql/schema/unique_within_type.rb +1 -1
  108. data/lib/graphql/schema/warden.rb +96 -95
  109. data/lib/graphql/schema.rb +323 -102
  110. data/lib/graphql/static_validation/all_rules.rb +1 -1
  111. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  112. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  114. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  116. data/lib/graphql/static_validation/validation_context.rb +5 -5
  117. data/lib/graphql/static_validation/validator.rb +3 -0
  118. data/lib/graphql/static_validation.rb +0 -1
  119. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  120. data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
  121. data/lib/graphql/subscriptions/event.rb +8 -2
  122. data/lib/graphql/subscriptions/serialize.rb +2 -0
  123. data/lib/graphql/subscriptions.rb +15 -13
  124. data/lib/graphql/testing/helpers.rb +151 -0
  125. data/lib/graphql/testing.rb +2 -0
  126. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  127. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  128. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  129. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  130. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  131. data/lib/graphql/tracing/prometheus_trace.rb +9 -9
  132. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  133. data/lib/graphql/tracing/trace.rb +1 -0
  134. data/lib/graphql/tracing.rb +3 -1
  135. data/lib/graphql/type_kinds.rb +1 -1
  136. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  137. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  138. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  139. data/lib/graphql/types.rb +1 -0
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +13 -13
  142. data/readme.md +12 -2
  143. metadata +33 -26
  144. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  145. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  146. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  147. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  148. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  149. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  150. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  151. data/lib/graphql/analysis/ast.rb +0 -81
  152. data/lib/graphql/deprecation.rb +0 -9
  153. data/lib/graphql/filter.rb +0 -59
  154. data/lib/graphql/language/parser.y +0 -560
  155. data/lib/graphql/schema/base_64_bp.rb +0 -26
  156. data/lib/graphql/static_validation/type_stack.rb +0 -216
  157. 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
@@ -10,176 +11,19 @@ module GraphQL
10
11
  class Runtime
11
12
  class CurrentState
12
13
  def initialize
13
- @current_object = nil
14
14
  @current_field = nil
15
15
  @current_arguments = nil
16
16
  @current_result_name = nil
17
17
  @current_result = nil
18
+ @was_authorized_by_scope_items = nil
18
19
  end
19
20
 
20
- 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
- into_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
21
+ def current_object
22
+ @current_result.graphql_application_value
135
23
  end
136
- end
137
-
138
- class GraphQLResultArray
139
- include GraphQLResult
140
24
 
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
25
+ attr_accessor :current_result, :current_result_name,
26
+ :current_arguments, :current_field, :was_authorized_by_scope_items
183
27
  end
184
28
 
185
29
  # @return [GraphQL::Query]
@@ -198,7 +42,7 @@ module GraphQL
198
42
  @lazies_at_depth = lazies_at_depth
199
43
  @schema = query.schema
200
44
  @context = query.context
201
- @response = GraphQLResultHash.new(nil, nil, false)
45
+ @response = nil
202
46
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
203
47
  @runtime_directive_names = []
204
48
  noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
@@ -208,12 +52,6 @@ module GraphQL
208
52
  @runtime_directive_names << name
209
53
  end
210
54
  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
55
  # { Class => Boolean }
218
56
  @lazy_cache = {}
219
57
  @lazy_cache.compare_by_identity
@@ -227,16 +65,6 @@ module GraphQL
227
65
  "#<#{self.class.name} response=#{@response.inspect}>"
228
66
  end
229
67
 
230
- def tap_or_each(obj_or_array)
231
- if obj_or_array.is_a?(Array)
232
- obj_or_array.each do |item|
233
- yield(item, true)
234
- end
235
- else
236
- yield(obj_or_array, false)
237
- end
238
- end
239
-
240
68
  # This _begins_ the execution. Some deferred work
241
69
  # might be stored up in lazies.
242
70
  # @return [void]
@@ -244,28 +72,21 @@ module GraphQL
244
72
  root_operation = query.selected_operation
245
73
  root_op_type = root_operation.operation_type || "query"
246
74
  root_type = schema.root_type_for_operation(root_op_type)
247
- st = get_current_runtime_state
248
- st.current_object = query.root_value
249
- st.current_result = @response
250
75
  runtime_object = root_type.wrap(query.root_value, context)
251
76
  runtime_object = schema.sync_lazy(runtime_object)
77
+ is_eager = root_op_type == "mutation"
78
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
79
+ st = get_current_runtime_state
80
+ st.current_result = @response
252
81
 
253
82
  if runtime_object.nil?
254
83
  # Root .authorized? returned false.
255
84
  @response = nil
256
85
  else
257
86
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
258
- gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
259
- # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
260
- # require isolation during execution (because of runtime directives). In that case,
261
- # make a new, isolated result hash for writing the result into. (That isolated response
262
- # is eventually merged back into the main response)
263
- #
264
- # Otherwise, `gathered_selections` is a hash of selections which can be
265
- # directly evaluated and the results can be written right into the main response hash.
266
- tap_or_each(gathered_selections) do |selections, is_selection_array|
87
+ each_gathered_selections(@response) do |selections, is_selection_array|
267
88
  if is_selection_array
268
- selection_response = GraphQLResultHash.new(nil, nil, false)
89
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
269
90
  final_response = @response
270
91
  else
271
92
  selection_response = @response
@@ -273,32 +94,30 @@ module GraphQL
273
94
  end
274
95
 
275
96
  @dataloader.append_job {
276
- st = get_current_runtime_state
277
- st.current_object = query.root_value
278
- st.current_result = selection_response
279
- # This is a less-frequent case; use a fast check since it's often not there.
280
- if (directives = selections[:graphql_directives])
281
- selections.delete(:graphql_directives)
282
- end
283
- call_method_on_directives(:resolve, runtime_object, directives) do
284
- evaluate_selections(
285
- runtime_object,
286
- root_type,
287
- root_op_type == "mutation",
288
- selections,
289
- selection_response,
290
- final_response,
291
- nil,
292
- )
293
- end
97
+ evaluate_selections(
98
+ selections,
99
+ selection_response,
100
+ final_response,
101
+ nil,
102
+ )
294
103
  }
295
104
  end
296
105
  end
297
106
  end
298
- delete_all_interpreter_context
299
107
  nil
300
108
  end
301
109
 
110
+ def each_gathered_selections(response_hash)
111
+ gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections)
112
+ if gathered_selections.is_a?(Array)
113
+ gathered_selections.each do |item|
114
+ yield(item, true)
115
+ end
116
+ else
117
+ yield(gathered_selections, false)
118
+ end
119
+ end
120
+
302
121
  def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
303
122
  selections.each do |node|
304
123
  # Skip gathering this if the directive says so
@@ -311,7 +130,7 @@ module GraphQL
311
130
  selections = selections_by_name[response_key]
312
131
  # if there was already a selection of this field,
313
132
  # use an array to hold all selections,
314
- # otherise, use the single node to represent the selection
133
+ # otherwise, use the single node to represent the selection
315
134
  if selections
316
135
  # This field was already selected at least once,
317
136
  # add this node to the list of selections
@@ -344,17 +163,26 @@ module GraphQL
344
163
  type_defn = schema.get_type(node.type.name, context)
345
164
 
346
165
  if query.warden.possible_types(type_defn).include?(owner_type)
347
- gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
166
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
167
+ if !result.equal?(next_selections)
168
+ selections_to_run = result
169
+ end
348
170
  end
349
171
  else
350
172
  # it's an untyped fragment, definitely continue
351
- gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
173
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
174
+ if !result.equal?(next_selections)
175
+ selections_to_run = result
176
+ end
352
177
  end
353
178
  when GraphQL::Language::Nodes::FragmentSpread
354
179
  fragment_def = query.fragments[node.name]
355
180
  type_defn = query.get_type(fragment_def.type.name)
356
181
  if query.warden.possible_types(type_defn).include?(owner_type)
357
- gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
182
+ result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
183
+ if !result.equal?(next_selections)
184
+ selections_to_run = result
185
+ end
358
186
  end
359
187
  else
360
188
  raise "Invariant: unexpected selection class: #{node.class}"
@@ -367,38 +195,44 @@ module GraphQL
367
195
  NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
368
196
 
369
197
  # @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
-
376
- finished_jobs = 0
377
- enqueued_jobs = gathered_selections.size
378
- gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
379
- @dataloader.append_job {
380
- evaluate_selection(
381
- result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
382
- )
383
- finished_jobs += 1
384
- if target_result && finished_jobs == enqueued_jobs
385
- selections_result.merge_into(target_result)
198
+ def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
199
+ runtime_state ||= get_current_runtime_state
200
+ runtime_state.current_result_name = nil
201
+ runtime_state.current_result = selections_result
202
+ # This is a less-frequent case; use a fast check since it's often not there.
203
+ if (directives = gathered_selections[:graphql_directives])
204
+ gathered_selections.delete(:graphql_directives)
205
+ end
206
+
207
+ call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
208
+ finished_jobs = 0
209
+ enqueued_jobs = gathered_selections.size
210
+ gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
211
+ @dataloader.append_job {
212
+ evaluate_selection(
213
+ result_name, field_ast_nodes_or_ast_node, selections_result
214
+ )
215
+ finished_jobs += 1
216
+ if target_result && finished_jobs == enqueued_jobs
217
+ selections_result.merge_into(target_result)
218
+ end
219
+ }
220
+ # Field resolution may pause the fiber,
221
+ # so it wouldn't get to the `Resolve` call that happens below.
222
+ # So instead trigger a run from this outer context.
223
+ if selections_result.graphql_is_eager
224
+ @dataloader.clear_cache
225
+ @dataloader.run
226
+ @dataloader.clear_cache
386
227
  end
387
- }
388
- # Field resolution may pause the fiber,
389
- # so it wouldn't get to the `Resolve` call that happens below.
390
- # So instead trigger a run from this outer context.
391
- if is_eager_selection
392
- @dataloader.run
393
228
  end
229
+ selections_result
394
230
  end
395
-
396
- selections_result
397
231
  end
398
232
 
399
233
  # @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
401
- return if dead_result?(selections_result)
234
+ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
235
+ return if selections_result.graphql_dead
402
236
  # As a performance optimization, the hash key will be a `Node` if
403
237
  # there's only one selection of the field. But if there are multiple
404
238
  # selections of the field, it will be an Array of nodes
@@ -410,59 +244,42 @@ module GraphQL
410
244
  ast_node = field_ast_nodes_or_ast_node
411
245
  end
412
246
  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
428
-
429
- return_type = field_defn.type
247
+ owner_type = selections_result.graphql_result_type
248
+ field_defn = query.warden.get_field(owner_type, field_name)
430
249
 
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
250
  # 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
251
+ runtime_state = get_current_runtime_state
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
+ owner_object = selections_result.graphql_application_value
257
+ if field_defn.dynamic_introspection
442
258
  owner_object = field_defn.owner.wrap(owner_object, context)
443
259
  end
444
260
 
445
- total_args_count = field_defn.arguments(context).size
446
- if total_args_count == 0
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_object, result_name, selections_result, 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_object, result_name, selections_result, 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_object, result_name, selections_result, 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, object, result_name, selection_result, 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|
464
280
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
465
- continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
281
+ return_type_non_null = field_defn.type.non_null?
282
+ continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
466
283
  next
467
284
  end
468
285
 
@@ -500,7 +317,8 @@ module GraphQL
500
317
  # to the keyword args hash _before_ freezing everything.
501
318
  extra_args[:argument_details] = :__arguments_add_self
502
319
  when :parent
503
- extra_args[:parent] = parent_object
320
+ parent_result = selection_result.graphql_parent
321
+ extra_args[:parent] = parent_result&.graphql_application_value&.object
504
322
  else
505
323
  extra_args[extra] = field_defn.fetch_extra(extra, context)
506
324
  end
@@ -511,17 +329,15 @@ module GraphQL
511
329
  resolved_arguments.keyword_arguments
512
330
  end
513
331
 
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)
332
+ evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state)
515
333
  end
516
334
  end
517
335
 
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
336
+ def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
337
+ runtime_state.current_field = field_defn
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
@@ -536,6 +352,14 @@ module GraphQL
536
352
  end
537
353
 
538
354
  field_result = call_method_on_directives(:resolve, object, directives) do
355
+ if directives.any?
356
+ # This might be executed in a different context; reset this info
357
+ runtime_state = get_current_runtime_state
358
+ runtime_state.current_field = field_defn
359
+ runtime_state.current_arguments = resolved_arguments
360
+ runtime_state.current_result_name = result_name
361
+ runtime_state.current_result = selection_result
362
+ end
539
363
  # Actually call the field resolver and capture the result
540
364
  app_result = begin
541
365
  @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
@@ -550,32 +374,27 @@ module GraphQL
550
374
  ex_err
551
375
  end
552
376
  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|
554
- continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
377
+ 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|
378
+ owner_type = selection_result.graphql_result_type
379
+ return_type = field_defn.type
380
+ continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
555
381
  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)
382
+ was_scoped = runtime_state.was_authorized_by_scope_items
383
+ runtime_state.was_authorized_by_scope_items = nil
384
+ 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
385
  end
558
386
  end
559
387
  end
560
-
561
388
  # If this field is a root mutation field, immediately resolve
562
389
  # all of its child fields before moving on to the next root mutation field.
563
390
  # (Subselections of this mutation will still be resolved level-by-level.)
564
- if is_eager_field
391
+ if selection_result.graphql_is_eager
565
392
  Interpreter::Resolve.resolve_all([field_result], @dataloader)
566
- else
567
- # Return this from `after_lazy` because it might be another lazy that needs to be resolved
568
- field_result
569
393
  end
570
394
  end
571
395
 
572
-
573
- def dead_result?(selection_result)
574
- selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
575
- end
576
-
577
396
  def set_result(selection_result, result_name, value, is_child_result, is_non_null)
578
- if !dead_result?(selection_result)
397
+ if !selection_result.graphql_dead
579
398
  if value.nil? && is_non_null
580
399
  # This is an invalid nil that should be propagated
581
400
  # One caller of this method passes a block,
@@ -629,11 +448,13 @@ module GraphQL
629
448
  end
630
449
 
631
450
  HALT = Object.new
632
- def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
451
+ def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
633
452
  case value
634
453
  when nil
635
454
  if is_non_null
636
455
  set_result(selection_result, result_name, nil, false, is_non_null) do
456
+ # When this comes from a list item, use the parent object:
457
+ parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
637
458
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
638
459
  err = parent_type::InvalidNullError.new(parent_type, field, value)
639
460
  schema.type_error(err, context)
@@ -647,7 +468,7 @@ module GraphQL
647
468
  # to avoid the overhead of checking three different classes
648
469
  # every time.
649
470
  if value.is_a?(GraphQL::ExecutionError)
650
- if selection_result.nil? || !dead_result?(selection_result)
471
+ if selection_result.nil? || !selection_result.graphql_dead
651
472
  value.path ||= current_path
652
473
  value.ast_node ||= ast_node
653
474
  context.errors << value
@@ -665,7 +486,7 @@ module GraphQL
665
486
  rescue GraphQL::ExecutionError => err
666
487
  err
667
488
  end
668
- continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
489
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
669
490
  elsif value.is_a?(GraphQL::UnauthorizedError)
670
491
  # this hook might raise & crash, or it might return
671
492
  # a replacement value
@@ -674,7 +495,7 @@ module GraphQL
674
495
  rescue GraphQL::ExecutionError => err
675
496
  err
676
497
  end
677
- continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
498
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
678
499
  elsif GraphQL::Execution::SKIP == value
679
500
  # It's possible a lazy was already written here
680
501
  case selection_result
@@ -695,9 +516,9 @@ module GraphQL
695
516
  end
696
517
  when Array
697
518
  # It's an array full of execution errors; add them all.
698
- if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
519
+ if value.any? && value.all?(GraphQL::ExecutionError)
699
520
  list_type_at_all = (field && (field.type.list?))
700
- if selection_result.nil? || !dead_result?(selection_result)
521
+ if selection_result.nil? || !selection_result.graphql_dead
701
522
  value.each_with_index do |error, index|
702
523
  error.ast_node ||= ast_node
703
524
  error.path ||= current_path + (list_type_at_all ? [index] : [])
@@ -733,7 +554,7 @@ module GraphQL
733
554
  # Location information from `path` and `ast_node`.
734
555
  #
735
556
  # @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
557
+ 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
558
  if current_type.non_null?
738
559
  current_type = current_type.of_type
739
560
  is_non_null = true
@@ -750,7 +571,7 @@ module GraphQL
750
571
  r
751
572
  when "UNION", "INTERFACE"
752
573
  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|
574
+ 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
575
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
755
576
  resolved_type, resolved_value = resolved_type_result
756
577
  else
@@ -767,60 +588,35 @@ module GraphQL
767
588
  set_result(selection_result, result_name, nil, false, is_non_null)
768
589
  nil
769
590
  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)
591
+ 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
592
  end
772
593
  end
773
594
  when "OBJECT"
774
595
  object_proxy = begin
775
- current_type.wrap(value, context)
596
+ was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
776
597
  rescue GraphQL::ExecutionError => err
777
598
  err
778
599
  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|
780
- continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
600
+ 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|
601
+ continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
781
602
  if HALT != continue_value
782
- response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
603
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
783
604
  set_result(selection_result, result_name, response_hash, true, is_non_null)
784
- gathered_selections = gather_selections(continue_value, current_type, next_selections)
785
- # There are two possibilities for `gathered_selections`:
786
- # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
787
- # This case is handled below, and the result can be written right into the main `response_hash` above.
788
- # In this case, `gathered_selections` is a hash of selections.
789
- # 2. Some selections of this object have runtime directives that may or may not modify execution.
790
- # That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
791
- # eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
792
- # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
793
- tap_or_each(gathered_selections) do |selections, is_selection_array|
605
+ each_gathered_selections(response_hash) do |selections, is_selection_array|
794
606
  if is_selection_array
795
- this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
607
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
796
608
  final_result = response_hash
797
609
  else
798
610
  this_result = response_hash
799
611
  final_result = nil
800
612
  end
801
- # reset this mutable state
802
- # 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
613
 
808
- # This is a less-frequent case; use a fast check since it's often not there.
809
- if (directives = selections[:graphql_directives])
810
- selections.delete(:graphql_directives)
811
- end
812
- call_method_on_directives(:resolve, continue_value, directives) do
813
- evaluate_selections(
814
- continue_value,
815
- current_type,
816
- false,
817
- selections,
818
- this_result,
819
- final_result,
820
- owner_object.object,
821
- )
822
- this_result
823
- end
614
+ evaluate_selections(
615
+ selections,
616
+ this_result,
617
+ final_result,
618
+ runtime_state,
619
+ )
824
620
  end
825
621
  end
826
622
  end
@@ -829,7 +625,7 @@ module GraphQL
829
625
  # This is true for objects, unions, and interfaces
830
626
  use_dataloader_job = !inner_type.unwrap.kind.input?
831
627
  inner_type_non_null = inner_type.non_null?
832
- response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
628
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
833
629
  set_result(selection_result, result_name, response_list, true, is_non_null)
834
630
  idx = nil
835
631
  list_value = begin
@@ -839,10 +635,10 @@ module GraphQL
839
635
  idx += 1
840
636
  if use_dataloader_job
841
637
  @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)
638
+ 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)
843
639
  end
844
640
  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)
641
+ 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)
846
642
  end
847
643
  end
848
644
 
@@ -867,22 +663,21 @@ module GraphQL
867
663
  end
868
664
  # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
869
665
  error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
870
- continue_value(list_value, owner_type, field, error_is_non_null, ast_node, result_name, selection_result)
666
+ continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result)
871
667
  else
872
668
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
873
669
  end
874
670
  end
875
671
 
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
672
+ def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
673
+ runtime_state.current_result_name = this_idx
674
+ runtime_state.current_result = response_list
880
675
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
881
676
  # 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|
883
- continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
677
+ 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|
678
+ continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
884
679
  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)
680
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
886
681
  end
887
682
  end
888
683
  end
@@ -902,7 +697,6 @@ module GraphQL
902
697
  raw_dir_args = arguments(nil, dir_defn, dir_node)
903
698
  dir_args = continue_value(
904
699
  raw_dir_args, # value
905
- dir_defn, # parent_type
906
700
  nil, # field
907
701
  false, # is_non_null
908
702
  dir_node, # ast_node
@@ -959,16 +753,20 @@ module GraphQL
959
753
  # @param eager [Boolean] Set to `true` for mutation root fields only
960
754
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
961
755
  # @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)
756
+ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
963
757
  if lazy?(lazy_obj)
964
758
  orig_result = result
759
+ was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
965
760
  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
761
+ # This block might be called in a new fiber;
762
+ # In that case, this will initialize a new state
763
+ # to avoid conflicting with the parent fiber.
764
+ runtime_state = get_current_runtime_state
765
+ runtime_state.current_field = field
766
+ runtime_state.current_arguments = arguments
767
+ runtime_state.current_result_name = result_name
768
+ runtime_state.current_result = orig_result
769
+ runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
972
770
  # Wrap the execution of _this_ method with tracing,
973
771
  # but don't wrap the continuation below
974
772
  inner_obj = begin
@@ -988,7 +786,7 @@ module GraphQL
988
786
  ex_err
989
787
  end
990
788
  end
991
- yield(inner_obj)
789
+ yield(inner_obj, runtime_state)
992
790
  end
993
791
 
994
792
  if eager
@@ -1005,7 +803,7 @@ module GraphQL
1005
803
  end
1006
804
  else
1007
805
  # Don't need to reset state here because it _wasn't_ lazy.
1008
- yield(lazy_obj)
806
+ yield(lazy_obj, runtime_state)
1009
807
  end
1010
808
  end
1011
809