graphql 2.3.3 → 2.3.5

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
  4. data/lib/graphql/dataloader/async_dataloader.rb +1 -0
  5. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  6. data/lib/graphql/dataloader.rb +6 -3
  7. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +7 -4
  8. data/lib/graphql/execution/interpreter/runtime.rb +52 -63
  9. data/lib/graphql/execution/interpreter.rb +1 -1
  10. data/lib/graphql/language/nodes.rb +60 -26
  11. data/lib/graphql/language/parser.rb +56 -15
  12. data/lib/graphql/query.rb +2 -2
  13. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  14. data/lib/graphql/schema/addition.rb +21 -11
  15. data/lib/graphql/schema/has_single_input_argument.rb +1 -0
  16. data/lib/graphql/schema/input_object.rb +1 -0
  17. data/lib/graphql/schema/introspection_system.rb +2 -2
  18. data/lib/graphql/schema/late_bound_type.rb +4 -0
  19. data/lib/graphql/schema/list.rb +2 -2
  20. data/lib/graphql/schema/member/has_arguments.rb +2 -35
  21. data/lib/graphql/schema/member/has_directives.rb +1 -1
  22. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  23. data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
  24. data/lib/graphql/schema/warden.rb +2 -3
  25. data/lib/graphql/schema.rb +20 -20
  26. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  27. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  28. data/lib/graphql/tracing/prometheus_trace.rb +8 -8
  29. data/lib/graphql/tracing/sentry_trace.rb +10 -10
  30. data/lib/graphql/type_kinds.rb +1 -1
  31. data/lib/graphql/version.rb +1 -1
  32. data/lib/graphql.rb +0 -8
  33. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1511256e7812b5c2de2a01ffa44f8a77f2e8959837344eaf1c8780d0b1bf700
4
- data.tar.gz: 87a0734b541d8f2cbbd3fb293a63663cbc57c61e6ab39242449596c6ca759442
3
+ metadata.gz: e5f20ae656aec8f5ad77a6edd73103d2f5a25511ae3c9b515c5b0c58ecc91cac
4
+ data.tar.gz: 6b51f66b0f2188c292b34c61e367de84cb37a7894e536b5b8105a95ec60b9c64
5
5
  SHA512:
6
- metadata.gz: fff5ef36d8e8dff310cac664178ebf0e902cd067a893fedd164902625751fe457a070f1daf2a2251d0dd2ec57fb16daccc81b54267048bbb71c027caf7905100
7
- data.tar.gz: 5fc4e6c4a44398e9f77d99c5224d22128a9c80e64a17a156500337b4d658270da9aa7af71f8d06cc065939cc42b41de3e07d66cb7622704ad179424c312dd7a0
6
+ metadata.gz: 96bd289b9d880438ed2c7610b3a7008cb793fd4551191fcf084c0f947c3da195e07f07346e0b2c9b88177fdbeed098591508879756d7f5919c53c9576f5548e8
7
+ data.tar.gz: ce92d82eee23a710b66f3d29bdc29690b2e368f42ddfd60b159708dac0b018024a87bbf6069d8274878e98e0821f223b0cd2fafba41c23afab86dc7faa97328c
@@ -9,7 +9,7 @@ module Graphql
9
9
  class MutationRootGenerator < Rails::Generators::Base
10
10
  include Core
11
11
 
12
- desc "Create mutation base type, mutation root tipe, and adds the latter to the schema"
12
+ desc "Create mutation base type, mutation root type, and adds the latter to the schema"
13
13
  source_root File.expand_path('../templates', __FILE__)
14
14
 
15
15
  class_option :schema,
@@ -31,4 +31,4 @@ module Graphql
31
31
  end
32
32
  end
33
33
  end
34
- end
34
+ end
@@ -12,7 +12,7 @@ module GraphQL
12
12
  @complexities_on_type_by_query = {}
13
13
  end
14
14
 
15
- # Overide this method to use the complexity result
15
+ # Override this method to use the complexity result
16
16
  def result
17
17
  max_possible_complexity
18
18
  end
@@ -77,6 +77,7 @@ module GraphQL
77
77
  set_fiber_variables(fiber_vars)
78
78
  Thread.current[:graphql_dataloader_next_tick] = condition
79
79
  pending_sources.each(&:run_pending_keys)
80
+ cleanup_fiber
80
81
  end
81
82
  end
82
83
  end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # simple internal code while adding the option to add Dataloader.
9
9
  class NullDataloader < Dataloader
10
10
  # These are all no-ops because code was
11
- # executed sychronously.
11
+ # executed synchronously.
12
12
  def run; end
13
13
  def run_isolated; yield; end
14
14
  def yield
@@ -88,6 +88,11 @@ module GraphQL
88
88
  nil
89
89
  end
90
90
 
91
+ # This method is called when Dataloader is finished using a fiber.
92
+ # Use it to perform any cleanup, such as releasing database connections (if required manually)
93
+ def cleanup_fiber
94
+ end
95
+
91
96
  # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
92
97
  #
93
98
  # @param source_class [Class<GraphQL::Dataloader::Source]
@@ -231,9 +236,7 @@ module GraphQL
231
236
  Fiber.new(blocking: !@nonblocking) {
232
237
  set_fiber_variables(fiber_vars)
233
238
  yield
234
- # With `.transfer`, you have to explicitly pass back to the parent --
235
- # if the fiber is allowed to terminate normally, control is passed to the main fiber instead.
236
- true
239
+ cleanup_fiber
237
240
  }
238
241
  end
239
242
 
@@ -5,7 +5,7 @@ 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)
8
+ def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager)
9
9
  @graphql_parent = parent_result
10
10
  @graphql_application_value = application_value
11
11
  @graphql_result_type = result_type
@@ -16,6 +16,8 @@ module GraphQL
16
16
  @graphql_is_non_null_in_parent = is_non_null_in_parent
17
17
  # Jump through some hoops to avoid creating this duplicate storage if at all possible.
18
18
  @graphql_metadata = nil
19
+ @graphql_selections = selections
20
+ @graphql_is_eager = is_eager
19
21
  end
20
22
 
21
23
  def path
@@ -28,14 +30,15 @@ module GraphQL
28
30
  end
29
31
 
30
32
  attr_accessor :graphql_dead
31
- attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent, :graphql_application_value, :graphql_result_type
33
+ 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
32
35
 
33
36
  # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
34
37
  attr_accessor :graphql_result_data
35
38
  end
36
39
 
37
40
  class GraphQLResultHash
38
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent)
41
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
39
42
  super
40
43
  @graphql_result_data = {}
41
44
  end
@@ -123,7 +126,7 @@ module GraphQL
123
126
  class GraphQLResultArray
124
127
  include GraphQLResult
125
128
 
126
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent)
129
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
127
130
  super
128
131
  @graphql_result_data = []
129
132
  end
@@ -65,16 +65,6 @@ module GraphQL
65
65
  "#<#{self.class.name} response=#{@response.inspect}>"
66
66
  end
67
67
 
68
- def tap_or_each(obj_or_array)
69
- if obj_or_array.is_a?(Array)
70
- obj_or_array.each do |item|
71
- yield(item, true)
72
- end
73
- else
74
- yield(obj_or_array, false)
75
- end
76
- end
77
-
78
68
  # This _begins_ the execution. Some deferred work
79
69
  # might be stored up in lazies.
80
70
  # @return [void]
@@ -84,7 +74,8 @@ module GraphQL
84
74
  root_type = schema.root_type_for_operation(root_op_type)
85
75
  runtime_object = root_type.wrap(query.root_value, context)
86
76
  runtime_object = schema.sync_lazy(runtime_object)
87
- @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false)
77
+ is_eager = root_op_type == "mutation"
78
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
88
79
  st = get_current_runtime_state
89
80
  st.current_result = @response
90
81
 
@@ -93,17 +84,9 @@ module GraphQL
93
84
  @response = nil
94
85
  else
95
86
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
96
- gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
97
- # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
98
- # require isolation during execution (because of runtime directives). In that case,
99
- # make a new, isolated result hash for writing the result into. (That isolated response
100
- # is eventually merged back into the main response)
101
- #
102
- # Otherwise, `gathered_selections` is a hash of selections which can be
103
- # directly evaluated and the results can be written right into the main response hash.
104
- tap_or_each(gathered_selections) do |selections, is_selection_array|
87
+ each_gathered_selections(@response) do |selections, is_selection_array|
105
88
  if is_selection_array
106
- selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false)
89
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
107
90
  final_response = @response
108
91
  else
109
92
  selection_response = @response
@@ -112,12 +95,10 @@ module GraphQL
112
95
 
113
96
  @dataloader.append_job {
114
97
  evaluate_selections(
115
- root_op_type == "mutation",
116
98
  selections,
117
99
  selection_response,
118
100
  final_response,
119
101
  nil,
120
- nil,
121
102
  )
122
103
  }
123
104
  end
@@ -126,8 +107,18 @@ module GraphQL
126
107
  nil
127
108
  end
128
109
 
129
- def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
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
130
120
 
121
+ def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
131
122
  selections.each do |node|
132
123
  # Skip gathering this if the directive says so
133
124
  if !directives_include?(node, owner_object, owner_type)
@@ -139,7 +130,7 @@ module GraphQL
139
130
  selections = selections_by_name[response_key]
140
131
  # if there was already a selection of this field,
141
132
  # use an array to hold all selections,
142
- # otherise, use the single node to represent the selection
133
+ # otherwise, use the single node to represent the selection
143
134
  if selections
144
135
  # This field was already selected at least once,
145
136
  # add this node to the list of selections
@@ -172,17 +163,26 @@ module GraphQL
172
163
  type_defn = schema.get_type(node.type.name, context)
173
164
 
174
165
  if query.warden.possible_types(type_defn).include?(owner_type)
175
- 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
176
170
  end
177
171
  else
178
172
  # it's an untyped fragment, definitely continue
179
- 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
180
177
  end
181
178
  when GraphQL::Language::Nodes::FragmentSpread
182
179
  fragment_def = query.fragments[node.name]
183
180
  type_defn = query.get_type(fragment_def.type.name)
184
181
  if query.warden.possible_types(type_defn).include?(owner_type)
185
- 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
186
186
  end
187
187
  else
188
188
  raise "Invariant: unexpected selection class: #{node.class}"
@@ -195,7 +195,7 @@ module GraphQL
195
195
  NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
196
196
 
197
197
  # @return [void]
198
- def evaluate_selections(is_eager_selection, gathered_selections, selections_result, target_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
198
+ def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
199
199
  runtime_state ||= get_current_runtime_state
200
200
  runtime_state.current_result_name = nil
201
201
  runtime_state.current_result = selections_result
@@ -210,7 +210,7 @@ module GraphQL
210
210
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
211
211
  @dataloader.append_job {
212
212
  evaluate_selection(
213
- result_name, field_ast_nodes_or_ast_node, is_eager_selection, selections_result, parent_object
213
+ result_name, field_ast_nodes_or_ast_node, selections_result
214
214
  )
215
215
  finished_jobs += 1
216
216
  if target_result && finished_jobs == enqueued_jobs
@@ -220,7 +220,7 @@ module GraphQL
220
220
  # Field resolution may pause the fiber,
221
221
  # so it wouldn't get to the `Resolve` call that happens below.
222
222
  # So instead trigger a run from this outer context.
223
- if is_eager_selection
223
+ if selections_result.graphql_is_eager
224
224
  @dataloader.clear_cache
225
225
  @dataloader.run
226
226
  @dataloader.clear_cache
@@ -231,7 +231,7 @@ module GraphQL
231
231
  end
232
232
 
233
233
  # @return [void]
234
- def evaluate_selection(result_name, field_ast_nodes_or_ast_node, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
234
+ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
235
235
  return if selections_result.graphql_dead
236
236
  # As a performance optimization, the hash key will be a `Node` if
237
237
  # there's only one selection of the field. But if there are multiple
@@ -258,28 +258,27 @@ module GraphQL
258
258
  owner_object = field_defn.owner.wrap(owner_object, context)
259
259
  end
260
260
 
261
- return_type = field_defn.type
262
261
  if !field_defn.any_arguments?
263
262
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
264
263
  if field_defn.extras.size == 0
265
264
  evaluate_selection_with_resolved_keyword_args(
266
- NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type.non_null?, runtime_state
265
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state
267
266
  )
268
267
  else
269
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
268
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
270
269
  end
271
270
  else
272
271
  @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
273
272
  runtime_state = get_current_runtime_state # This might be in a different fiber
274
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
273
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
275
274
  end
276
275
  end
277
276
  end
278
277
 
279
- def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, runtime_state) # rubocop:disable Metrics/ParameterLists
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
280
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|
281
- return_type_non_null = return_type.non_null?
282
280
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
281
+ return_type_non_null = field_defn.type.non_null?
283
282
  continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
284
283
  next
285
284
  end
@@ -318,7 +317,8 @@ module GraphQL
318
317
  # to the keyword args hash _before_ freezing everything.
319
318
  extra_args[:argument_details] = :__arguments_add_self
320
319
  when :parent
321
- extra_args[:parent] = parent_object
320
+ parent_result = selection_result.graphql_parent
321
+ extra_args[:parent] = parent_result&.graphql_application_value&.object
322
322
  else
323
323
  extra_args[extra] = field_defn.fetch_extra(extra, context)
324
324
  end
@@ -329,11 +329,11 @@ module GraphQL
329
329
  resolved_arguments.keyword_arguments
330
330
  end
331
331
 
332
- evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state)
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)
333
333
  end
334
334
  end
335
335
 
336
- def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state) # rubocop:disable Metrics/ParameterLists
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
337
  runtime_state.current_field = field_defn
338
338
  runtime_state.current_arguments = resolved_arguments
339
339
  runtime_state.current_result_name = result_name
@@ -376,7 +376,8 @@ module GraphQL
376
376
  end
377
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
378
  owner_type = selection_result.graphql_result_type
379
- continue_value = continue_value(inner_result, field_defn, return_type_non_null, ast_node, result_name, selection_result)
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)
380
381
  if HALT != continue_value
381
382
  was_scoped = runtime_state.was_authorized_by_scope_items
382
383
  runtime_state.was_authorized_by_scope_items = nil
@@ -387,7 +388,7 @@ module GraphQL
387
388
  # If this field is a root mutation field, immediately resolve
388
389
  # all of its child fields before moving on to the next root mutation field.
389
390
  # (Subselections of this mutation will still be resolved level-by-level.)
390
- if is_eager_field
391
+ if selection_result.graphql_is_eager
391
392
  Interpreter::Resolve.resolve_all([field_result], @dataloader)
392
393
  end
393
394
  end
@@ -599,21 +600,11 @@ module GraphQL
599
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|
600
601
  continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
601
602
  if HALT != continue_value
602
- response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
603
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
603
604
  set_result(selection_result, result_name, response_hash, true, is_non_null)
604
-
605
- gathered_selections = gather_selections(continue_value, current_type, next_selections)
606
- # There are two possibilities for `gathered_selections`:
607
- # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
608
- # This case is handled below, and the result can be written right into the main `response_hash` above.
609
- # In this case, `gathered_selections` is a hash of selections.
610
- # 2. Some selections of this object have runtime directives that may or may not modify execution.
611
- # That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
612
- # eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
613
- # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
614
- tap_or_each(gathered_selections) do |selections, is_selection_array|
605
+ each_gathered_selections(response_hash) do |selections, is_selection_array|
615
606
  if is_selection_array
616
- this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
607
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
617
608
  final_result = response_hash
618
609
  else
619
610
  this_result = response_hash
@@ -621,11 +612,9 @@ module GraphQL
621
612
  end
622
613
 
623
614
  evaluate_selections(
624
- false,
625
615
  selections,
626
616
  this_result,
627
617
  final_result,
628
- owner_object.object,
629
618
  runtime_state,
630
619
  )
631
620
  end
@@ -636,7 +625,7 @@ module GraphQL
636
625
  # This is true for objects, unions, and interfaces
637
626
  use_dataloader_job = !inner_type.unwrap.kind.input?
638
627
  inner_type_non_null = inner_type.non_null?
639
- response_list = GraphQLResultArray.new(result_name, current_type, response_list, selection_result, is_non_null)
628
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
640
629
  set_result(selection_result, result_name, response_list, true, is_non_null)
641
630
  idx = nil
642
631
  list_value = begin
@@ -646,10 +635,10 @@ module GraphQL
646
635
  idx += 1
647
636
  if use_dataloader_job
648
637
  @dataloader.append_job do
649
- 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)
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)
650
639
  end
651
640
  else
652
- 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)
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)
653
642
  end
654
643
  end
655
644
 
@@ -680,7 +669,7 @@ module GraphQL
680
669
  end
681
670
  end
682
671
 
683
- 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
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
684
673
  runtime_state.current_result_name = this_idx
685
674
  runtime_state.current_result = response_list
686
675
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
@@ -688,7 +677,7 @@ module GraphQL
688
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|
689
678
  continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
690
679
  if HALT != continue_value
691
- 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)
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)
692
681
  end
693
682
  end
694
683
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
  # @param queries [Array<GraphQL::Query, Hash>]
21
21
  # @param context [Hash]
22
22
  # @param max_complexity [Integer, nil]
23
- # @return [Array<Hash>] One result per query
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
26
  case opts
@@ -20,6 +20,15 @@ module GraphQL
20
20
  @definition_line = definition_line
21
21
  super(**_rest)
22
22
  end
23
+
24
+ def marshal_dump
25
+ super << @definition_line
26
+ end
27
+
28
+ def marshal_load(values)
29
+ @definition_line = values.pop
30
+ super
31
+ end
23
32
  end
24
33
 
25
34
  attr_reader :filename
@@ -265,6 +274,8 @@ module GraphQL
265
274
  ]
266
275
 
267
276
  def generate_initialize
277
+ return if method_defined?(:marshal_load, false) # checking for `:initialize` doesn't work right
278
+
268
279
  scalar_method_names = @scalar_methods
269
280
  # TODO: These probably should be scalar methods, but `types` returns an array
270
281
  [:types, :description].each do |extra_method|
@@ -273,18 +284,20 @@ module GraphQL
273
284
  end
274
285
  end
275
286
 
276
- all_method_names = scalar_method_names + @children_methods.keys
287
+ children_method_names = @children_methods.keys
288
+
289
+ all_method_names = scalar_method_names + children_method_names
277
290
  if all_method_names.include?(:alias)
278
291
  # Rather than complicating this special case,
279
292
  # let it be overridden (in field)
280
293
  return
281
294
  else
282
295
  arguments = scalar_method_names.map { |m| "#{m}: nil"} +
283
- @children_methods.keys.map { |m| "#{m}: NO_CHILDREN" } +
296
+ children_method_names.map { |m| "#{m}: NO_CHILDREN" } +
284
297
  DEFAULT_INITIALIZE_OPTIONS
285
298
 
286
299
  assignments = scalar_method_names.map { |m| "@#{m} = #{m}"} +
287
- @children_methods.keys.map { |m| "@#{m} = #{m}.freeze" }
300
+ children_method_names.map { |m| "@#{m} = #{m}.freeze" }
288
301
 
289
302
  if name.end_with?("Definition") && name != "FragmentDefinition"
290
303
  arguments << "definition_pos: nil"
@@ -292,7 +305,7 @@ module GraphQL
292
305
  end
293
306
 
294
307
  keywords = scalar_method_names.map { |m| "#{m}: #{m}"} +
295
- @children_methods.keys.map { |m| "#{m}: #{m}" }
308
+ children_method_names.map { |m| "#{m}: #{m}" }
296
309
 
297
310
  module_eval <<-RUBY, __FILE__, __LINE__
298
311
  def initialize(#{arguments.join(", ")})
@@ -304,9 +317,21 @@ module GraphQL
304
317
  #{assignments.join("\n")}
305
318
  end
306
319
 
307
- def self.from_a(filename, line, col, #{(scalar_method_names + @children_methods.keys).join(", ")})
320
+ def self.from_a(filename, line, col, #{all_method_names.join(", ")})
308
321
  self.new(filename: filename, line: line, col: col, #{keywords.join(", ")})
309
322
  end
323
+
324
+ def marshal_dump
325
+ [
326
+ line, col, # use methods here to force them to be calculated
327
+ @filename,
328
+ #{all_method_names.map { |n| "@#{n}," }.join}
329
+ ]
330
+ end
331
+
332
+ def marshal_load(values)
333
+ @line, @col, @filename #{all_method_names.map { |n| ", @#{n}"}.join} = values
334
+ end
310
335
  RUBY
311
336
  end
312
337
  end
@@ -369,16 +394,6 @@ module GraphQL
369
394
 
370
395
  # A single selection in a GraphQL query.
371
396
  class Field < AbstractNode
372
- scalar_methods :name, :alias
373
- children_methods({
374
- arguments: GraphQL::Language::Nodes::Argument,
375
- selections: GraphQL::Language::Nodes::Field,
376
- directives: GraphQL::Language::Nodes::Directive,
377
- })
378
-
379
- # @!attribute selections
380
- # @return [Array<Nodes::Field>] Selections on this object (or empty array if this is a scalar field)
381
-
382
397
  def initialize(name: nil, arguments: NONE, directives: NONE, selections: NONE, field_alias: nil, line: nil, col: nil, pos: nil, filename: nil, source: nil)
383
398
  @name = name
384
399
  @arguments = arguments || NONE
@@ -397,24 +412,27 @@ module GraphQL
397
412
  self.new(filename: filename, line: line, col: col, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selections)
398
413
  end
399
414
 
400
- # Override this because default is `:fields`
401
- self.children_method_name = :selections
402
- end
415
+ def marshal_dump
416
+ [line, col, @filename, @name, @arguments, @directives, @selections, @alias]
417
+ end
403
418
 
404
- # A reusable fragment, defined at document-level.
405
- class FragmentDefinition < AbstractNode
406
- scalar_methods :name, :type
419
+ def marshal_load(values)
420
+ @line, @col, @filename, @name, @arguments, @directives, @selections, @alias = values
421
+ end
422
+
423
+ scalar_methods :name, :alias
407
424
  children_methods({
425
+ arguments: GraphQL::Language::Nodes::Argument,
408
426
  selections: GraphQL::Language::Nodes::Field,
409
427
  directives: GraphQL::Language::Nodes::Directive,
410
428
  })
411
429
 
412
- self.children_method_name = :definitions
413
- # @!attribute name
414
- # @return [String] the identifier for this fragment, which may be applied with `...#{name}`
430
+ # Override this because default is `:fields`
431
+ self.children_method_name = :selections
432
+ end
415
433
 
416
- # @!attribute type
417
- # @return [String] the type condition for this fragment (name of type which it may apply to)
434
+ # A reusable fragment, defined at document-level.
435
+ class FragmentDefinition < AbstractNode
418
436
  def initialize(name: nil, type: nil, directives: NONE, selections: NONE, filename: nil, pos: nil, source: nil, line: nil, col: nil)
419
437
  @name = name
420
438
  @type = type
@@ -430,6 +448,22 @@ module GraphQL
430
448
  def self.from_a(filename, line, col, name, type, directives, selections)
431
449
  self.new(filename: filename, line: line, col: col, name: name, type: type, directives: directives, selections: selections)
432
450
  end
451
+
452
+ def marshal_dump
453
+ [line, col, @filename, @name, @type, @directives, @selections]
454
+ end
455
+
456
+ def marshal_load(values)
457
+ @line, @col, @filename, @name, @type, @directives, @selections = values
458
+ end
459
+
460
+ scalar_methods :name, :type
461
+ children_methods({
462
+ selections: GraphQL::Language::Nodes::Field,
463
+ directives: GraphQL::Language::Nodes::Directive,
464
+ })
465
+
466
+ self.children_method_name = :definitions
433
467
  end
434
468
 
435
469
  # Application of a named fragment in a selection