graphql 2.6.3 → 2.6.10
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/field_extractor.rb +17 -1
- data/lib/generators/graphql/templates/schema.erb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +2 -2
- data/lib/graphql/dashboard/application_controller.rb +5 -1
- data/lib/graphql/dashboard.rb +3 -0
- data/lib/graphql/dataloader/async_dataloader.rb +333 -68
- data/lib/graphql/dataloader/source.rb +32 -19
- data/lib/graphql/dataloader.rb +52 -36
- data/lib/graphql/execution/field_resolve_step.rb +79 -19
- data/lib/graphql/execution/finalize.rb +7 -8
- data/lib/graphql/execution/interpreter/runtime.rb +1 -8
- data/lib/graphql/execution/interpreter.rb +9 -1
- data/lib/graphql/execution/prepare_object_step.rb +8 -4
- data/lib/graphql/execution/runner.rb +17 -5
- data/lib/graphql/execution/selections_step.rb +10 -6
- data/lib/graphql/execution_error.rb +4 -0
- data/lib/graphql/float_decoding_error.rb +13 -0
- data/lib/graphql/float_encoding_error.rb +28 -0
- data/lib/graphql/language/block_string.rb +6 -11
- data/lib/graphql/language/cache.rb +84 -14
- data/lib/graphql/language/lexer.rb +15 -9
- data/lib/graphql/language/nodes.rb +2 -1
- data/lib/graphql/language.rb +3 -3
- data/lib/graphql/pagination/relation_connection.rb +10 -1
- data/lib/graphql/query/variable_validation_error.rb +16 -1
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/railtie.rb +2 -1
- data/lib/graphql/schema/build_from_definition.rb +11 -3
- data/lib/graphql/schema/directive.rb +3 -0
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/schema/wrapper.rb +4 -0
- data/lib/graphql/schema.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +66 -29
- data/lib/graphql/subscriptions/serialize.rb +10 -4
- data/lib/graphql/types/float.rb +18 -4
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +2 -0
- metadata +4 -58
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -58,7 +58,9 @@ module GraphQL
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
|
|
61
|
-
@source_cache = Hash.new { |h, k| h[k] = {} }
|
|
61
|
+
@source_cache = Hash.new { |h, k| h[k] = {} }.compare_by_identity
|
|
62
|
+
@pending_source_set = Set.new.compare_by_identity
|
|
63
|
+
@pending_sources = []
|
|
62
64
|
@pending_jobs = []
|
|
63
65
|
if !nonblocking.nil?
|
|
64
66
|
@nonblocking = nonblocking
|
|
@@ -108,7 +110,7 @@ module GraphQL
|
|
|
108
110
|
# @param batch_parameters [Array<Object>]
|
|
109
111
|
# @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
|
|
110
112
|
# and cached for the lifetime of this {Multiplex}.
|
|
111
|
-
if (RUBY_ENGINE == "ruby" &&
|
|
113
|
+
if (RUBY_ENGINE == "ruby" && RUBY_VERSION < "3") || RUBY_ENGINE == "truffleruby" # truffle-ruby wasn't doing well with the implementation below
|
|
112
114
|
def with(source_class, *batch_args)
|
|
113
115
|
batch_key = source_class.batch_key_for(*batch_args)
|
|
114
116
|
@source_cache[source_class][batch_key] ||= begin
|
|
@@ -148,6 +150,14 @@ module GraphQL
|
|
|
148
150
|
nil
|
|
149
151
|
end
|
|
150
152
|
|
|
153
|
+
# @api private
|
|
154
|
+
def queue_pending_source(source)
|
|
155
|
+
if @pending_source_set.add?(source)
|
|
156
|
+
@pending_sources << source
|
|
157
|
+
end
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
160
|
+
|
|
151
161
|
# Clear any already-loaded objects from {Source} caches
|
|
152
162
|
# @return [void]
|
|
153
163
|
def clear_cache
|
|
@@ -187,9 +197,10 @@ module GraphQL
|
|
|
187
197
|
@lazies_at_depth = prev_lazies_at_depth
|
|
188
198
|
prev_pending_keys.each do |source_instance, pending|
|
|
189
199
|
pending.each do |key, value|
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
next if source_instance.results.key?(key)
|
|
201
|
+
|
|
202
|
+
queue_pending_source(source_instance) if source_instance.pending.empty?
|
|
203
|
+
source_instance.pending[key] = value
|
|
193
204
|
end
|
|
194
205
|
end
|
|
195
206
|
end
|
|
@@ -212,8 +223,10 @@ module GraphQL
|
|
|
212
223
|
|
|
213
224
|
if !@lazies_at_depth.empty?
|
|
214
225
|
with_trace_query_lazy(trace_query_lazy) do
|
|
215
|
-
|
|
216
|
-
|
|
226
|
+
if enqueue_next_pending_lazies(@lazies_at_depth)
|
|
227
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
228
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
229
|
+
end
|
|
217
230
|
end
|
|
218
231
|
end
|
|
219
232
|
end
|
|
@@ -274,24 +287,19 @@ module GraphQL
|
|
|
274
287
|
|
|
275
288
|
private
|
|
276
289
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if depth_key < smallest_depth
|
|
282
|
-
smallest_depth = depth_key
|
|
283
|
-
end
|
|
284
|
-
end
|
|
290
|
+
# Returns true if anything was actually enqueued
|
|
291
|
+
def enqueue_next_pending_lazies(lazies_at_depth)
|
|
292
|
+
smallest_depth = lazies_at_depth.each_key.min
|
|
293
|
+
return false if smallest_depth.nil?
|
|
285
294
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
end
|
|
292
|
-
job_fibers.unshift(spawn_job_fiber(trace))
|
|
293
|
-
end
|
|
295
|
+
lazies = lazies_at_depth.delete(smallest_depth)
|
|
296
|
+
return false if lazies.empty?
|
|
297
|
+
|
|
298
|
+
lazies.each do |lazy|
|
|
299
|
+
append_job { lazy.value }
|
|
294
300
|
end
|
|
301
|
+
|
|
302
|
+
true
|
|
295
303
|
end
|
|
296
304
|
|
|
297
305
|
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
@@ -305,7 +313,7 @@ module GraphQL
|
|
|
305
313
|
end
|
|
306
314
|
join_queues(job_fibers, next_job_fibers)
|
|
307
315
|
|
|
308
|
-
while (!source_fibers.empty? ||
|
|
316
|
+
while (!source_fibers.empty? || !@pending_sources.empty?)
|
|
309
317
|
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
310
318
|
if f.alive?
|
|
311
319
|
finished = run_fiber(f)
|
|
@@ -356,21 +364,29 @@ module GraphQL
|
|
|
356
364
|
end
|
|
357
365
|
end
|
|
358
366
|
|
|
359
|
-
def
|
|
360
|
-
pending_sources =
|
|
361
|
-
@
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
367
|
+
def drain_pending_sources
|
|
368
|
+
pending_sources = @pending_sources
|
|
369
|
+
@pending_sources = []
|
|
370
|
+
@pending_source_set.clear
|
|
371
|
+
|
|
372
|
+
pending_sources.select!(&:pending?)
|
|
373
|
+
pending_sources.empty? ? nil : pending_sources
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def dequeue_pending_source
|
|
377
|
+
while (source = @pending_sources.shift)
|
|
378
|
+
@pending_source_set.delete(source)
|
|
379
|
+
return source if source.pending?
|
|
368
380
|
end
|
|
381
|
+
end
|
|
369
382
|
|
|
370
|
-
|
|
383
|
+
def spawn_source_fiber(trace)
|
|
384
|
+
if !@pending_sources.empty?
|
|
371
385
|
spawn_fiber do
|
|
372
|
-
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
373
|
-
|
|
386
|
+
trace&.dataloader_spawn_source_fiber(@pending_sources)
|
|
387
|
+
# This will find sources which were enqueued during `#fetch`:
|
|
388
|
+
while (source = dequeue_pending_source)
|
|
389
|
+
next if !source.pending?
|
|
374
390
|
Fiber[:__graphql_current_dataloader_source] = source
|
|
375
391
|
trace&.begin_dataloader_source(source)
|
|
376
392
|
source.run_pending_keys
|
|
@@ -49,11 +49,12 @@ module GraphQL
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def value
|
|
52
|
+
return nil if @selections_step.killed
|
|
52
53
|
query = @selections_step.query
|
|
53
54
|
set_current_field
|
|
54
|
-
query.current_trace.begin_execute_field(@field_definition, @
|
|
55
|
+
query.current_trace.begin_execute_field(@field_definition, @field_results, @arguments, query)
|
|
55
56
|
sync(@field_results)
|
|
56
|
-
query.current_trace.end_execute_field(@field_definition, @
|
|
57
|
+
query.current_trace.end_execute_field(@field_definition, @field_results, @arguments, query, @field_results)
|
|
57
58
|
@runner.add_step(self)
|
|
58
59
|
true
|
|
59
60
|
ensure
|
|
@@ -79,7 +80,9 @@ module GraphQL
|
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
def call
|
|
83
|
+
return nil if @selections_step.killed
|
|
82
84
|
set_current_field if @field_definition
|
|
85
|
+
|
|
83
86
|
if @enqueued_authorization
|
|
84
87
|
enqueue_next_steps
|
|
85
88
|
elsif @finish_extension_idx
|
|
@@ -102,24 +105,64 @@ module GraphQL
|
|
|
102
105
|
set_current_field(nil)
|
|
103
106
|
end
|
|
104
107
|
|
|
105
|
-
def add_graphql_error(err)
|
|
108
|
+
def add_graphql_error(result, key, err, return_type: @field_definition.type)
|
|
106
109
|
err.path = path
|
|
107
110
|
if err.ast_node.nil?
|
|
108
111
|
err.ast_nodes = ast_nodes
|
|
109
112
|
end
|
|
110
|
-
@selections_step.query
|
|
113
|
+
@runner.add_finalizer(@selections_step.query, result, key, err)
|
|
114
|
+
if !err.is_a?(GraphQL::Execution::Skip)
|
|
115
|
+
field_type = return_type
|
|
116
|
+
should_propagate_null = field_type.non_null?
|
|
117
|
+
while (should_propagate_null == false && field_type.kind.wraps?)
|
|
118
|
+
field_type = field_type.of_type
|
|
119
|
+
should_propagate_null = field_type.non_null?
|
|
120
|
+
end
|
|
121
|
+
if should_propagate_null
|
|
122
|
+
propagate_nulls
|
|
123
|
+
end
|
|
124
|
+
end
|
|
111
125
|
err
|
|
112
126
|
end
|
|
113
127
|
|
|
128
|
+
def propagate_nulls
|
|
129
|
+
propagating_null = true
|
|
130
|
+
highest_nulled_depth = path.size
|
|
131
|
+
highest_list_depth = nil
|
|
132
|
+
current_field_step = self
|
|
133
|
+
while current_field_step && current_field_step.field_definition
|
|
134
|
+
return_type = current_field_step.field_definition.type
|
|
135
|
+
if propagating_null && return_type.non_null?
|
|
136
|
+
highest_nulled_depth = current_field_step.path.size
|
|
137
|
+
else
|
|
138
|
+
propagating_null = false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if return_type.list?
|
|
142
|
+
highest_list_depth = current_field_step.path.size
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
current_field_step = current_field_step.selections_step.field_resolve_step
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if highest_list_depth.nil? || highest_nulled_depth <= highest_list_depth
|
|
149
|
+
kill_field_step = self
|
|
150
|
+
while kill_field_step && kill_field_step.field_definition && highest_nulled_depth <= kill_field_step.path.size
|
|
151
|
+
kill_field_step.selections_step.killed = true
|
|
152
|
+
kill_field_step = kill_field_step.selections_step.field_resolve_step
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
114
157
|
def build_errors_result(errors, single_error)
|
|
115
158
|
first_error = errors.nil? ? single_error : errors.pop
|
|
116
|
-
@field_results =
|
|
159
|
+
@field_results = [first_error]
|
|
160
|
+
@results = [@selections_step.results.first]
|
|
117
161
|
if errors
|
|
118
162
|
errors.each do |e|
|
|
119
|
-
add_graphql_error(e)
|
|
163
|
+
add_graphql_error(@results.first, key, e)
|
|
120
164
|
end
|
|
121
165
|
end
|
|
122
|
-
@results ||= @selections_step.results
|
|
123
166
|
build_results
|
|
124
167
|
end
|
|
125
168
|
|
|
@@ -226,7 +269,7 @@ module GraphQL
|
|
|
226
269
|
authorized_results << @results[i]
|
|
227
270
|
end
|
|
228
271
|
rescue GraphQL::ExecutionError => exec_err
|
|
229
|
-
add_graphql_error(exec_err)
|
|
272
|
+
add_graphql_error(@results[i], key, exec_err)
|
|
230
273
|
end
|
|
231
274
|
end
|
|
232
275
|
i += 1
|
|
@@ -244,7 +287,7 @@ module GraphQL
|
|
|
244
287
|
@was_scoped = true
|
|
245
288
|
end
|
|
246
289
|
|
|
247
|
-
query.current_trace.begin_execute_field(@field_definition, @arguments,
|
|
290
|
+
query.current_trace.begin_execute_field(@field_definition, authorized_objects, @arguments, query)
|
|
248
291
|
|
|
249
292
|
if @runner.uses_runtime_directives
|
|
250
293
|
if @ast_nodes.nil? || @ast_nodes.size == 1
|
|
@@ -319,7 +362,7 @@ module GraphQL
|
|
|
319
362
|
@field_results = resolve_batch(authorized_objects, ctx, @arguments)
|
|
320
363
|
end
|
|
321
364
|
|
|
322
|
-
query.current_trace.end_execute_field(@field_definition, @arguments,
|
|
365
|
+
query.current_trace.end_execute_field(@field_definition, authorized_objects, @arguments, query, @field_results)
|
|
323
366
|
|
|
324
367
|
if any_lazy_results?
|
|
325
368
|
@runner.dataloader.lazy_at_depth(path.size, self)
|
|
@@ -331,12 +374,12 @@ module GraphQL
|
|
|
331
374
|
end
|
|
332
375
|
end
|
|
333
376
|
rescue GraphQL::ExecutionError => err
|
|
334
|
-
|
|
377
|
+
build_errors_result(nil, err)
|
|
335
378
|
rescue StandardError => stderr
|
|
336
379
|
begin
|
|
337
380
|
@selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
338
381
|
rescue GraphQL::ExecutionError => err
|
|
339
|
-
add_graphql_error(err)
|
|
382
|
+
add_graphql_error(@results[0], key, err)
|
|
340
383
|
end
|
|
341
384
|
end
|
|
342
385
|
|
|
@@ -458,13 +501,13 @@ module GraphQL
|
|
|
458
501
|
end
|
|
459
502
|
|
|
460
503
|
def finish_leaf_result(result_h, key, field_result, return_type, ctx)
|
|
461
|
-
final_field_result = build_leaf_result(field_result, return_type, ctx, false)
|
|
504
|
+
final_field_result = build_leaf_result(result_h, key, field_result, return_type, ctx, false)
|
|
462
505
|
|
|
463
506
|
@directive_finalizers&.each { |f| @runner.add_finalizer(ctx.query, result_h, key, f) }
|
|
464
507
|
result_h[@key] = final_field_result
|
|
465
508
|
end
|
|
466
509
|
|
|
467
|
-
def build_leaf_result(field_result, return_type, ctx, is_from_array)
|
|
510
|
+
def build_leaf_result(result_h, result_key, field_result, return_type, ctx, is_from_array)
|
|
468
511
|
if field_result.nil?
|
|
469
512
|
if return_type.non_null?
|
|
470
513
|
add_non_null_error(is_from_array)
|
|
@@ -473,7 +516,7 @@ module GraphQL
|
|
|
473
516
|
end
|
|
474
517
|
elsif field_result.is_a?(Finalizer)
|
|
475
518
|
if field_result.is_a?(GraphQL::RuntimeError)
|
|
476
|
-
add_graphql_error(field_result)
|
|
519
|
+
add_graphql_error(result_h, result_key, field_result, return_type: return_type)
|
|
477
520
|
else
|
|
478
521
|
field_result.path = path
|
|
479
522
|
@runner.add_finalizer(ctx.query, result_h, key, field_result)
|
|
@@ -484,7 +527,11 @@ module GraphQL
|
|
|
484
527
|
end
|
|
485
528
|
|
|
486
529
|
inner_type = return_type.of_type
|
|
487
|
-
|
|
530
|
+
result_a = Array.new(field_result.size)
|
|
531
|
+
field_result.each_with_index do |item, idx|
|
|
532
|
+
result_a[idx] = build_leaf_result(result_a, idx, item, inner_type, ctx, true)
|
|
533
|
+
end
|
|
534
|
+
result_a
|
|
488
535
|
else
|
|
489
536
|
return_type.coerce_result(field_result, ctx)
|
|
490
537
|
end
|
|
@@ -526,6 +573,7 @@ module GraphQL
|
|
|
526
573
|
query.current_trace.objects(obj_type, next_objects, ctx)
|
|
527
574
|
@runner.add_step(SelectionsStep.new(
|
|
528
575
|
path: path,
|
|
576
|
+
field_resolve_step: self,
|
|
529
577
|
parent_type: obj_type,
|
|
530
578
|
selections: @next_selections,
|
|
531
579
|
objects: next_objects,
|
|
@@ -538,6 +586,7 @@ module GraphQL
|
|
|
538
586
|
query.current_trace.objects(@static_type, @all_next_objects, ctx)
|
|
539
587
|
@runner.add_step(SelectionsStep.new(
|
|
540
588
|
path: path,
|
|
589
|
+
field_resolve_step: self,
|
|
541
590
|
parent_type: @static_type,
|
|
542
591
|
selections: @next_selections,
|
|
543
592
|
objects: @all_next_objects,
|
|
@@ -558,7 +607,11 @@ module GraphQL
|
|
|
558
607
|
|
|
559
608
|
def add_non_null_error(is_from_array)
|
|
560
609
|
err = @parent_type::InvalidNullError.new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path)
|
|
561
|
-
@runner.schema.type_error(err, @selections_step.query.context)
|
|
610
|
+
nn_result = @runner.schema.type_error(err, @selections_step.query.context)
|
|
611
|
+
if nn_result.nil?
|
|
612
|
+
propagate_nulls
|
|
613
|
+
end
|
|
614
|
+
nn_result
|
|
562
615
|
end
|
|
563
616
|
|
|
564
617
|
def set_current_field(new_value = @field_definition)
|
|
@@ -576,7 +629,7 @@ module GraphQL
|
|
|
576
629
|
end
|
|
577
630
|
elsif field_result.is_a?(Finalizer)
|
|
578
631
|
graphql_result[key] = if field_result.is_a?(GraphQL::RuntimeError)
|
|
579
|
-
add_graphql_error(field_result)
|
|
632
|
+
add_graphql_error(graphql_result, key, field_result)
|
|
580
633
|
else
|
|
581
634
|
field_result.path = path
|
|
582
635
|
@runner.add_finalizer(@selections_step.query, graphql_result, key, field_result)
|
|
@@ -691,7 +744,8 @@ module GraphQL
|
|
|
691
744
|
when :dig
|
|
692
745
|
objects.map { |o| o.dig(*@field_definition.execution_mode_key) }
|
|
693
746
|
when :dataload
|
|
694
|
-
|
|
747
|
+
k = @field_definition.execution_mode_key
|
|
748
|
+
results = if k.is_a?(Class)
|
|
695
749
|
context.dataload_all(k, objects)
|
|
696
750
|
elsif (source_class = k[:with])
|
|
697
751
|
if (batch_args = k[:by])
|
|
@@ -711,6 +765,12 @@ module GraphQL
|
|
|
711
765
|
else
|
|
712
766
|
raise ArgumentError, "Unexpected `dataload: ...` configuration: #{k.inspect}"
|
|
713
767
|
end
|
|
768
|
+
method = k.is_a?(Hash) ? k[:method] : nil
|
|
769
|
+
if method
|
|
770
|
+
results.map { |r| r&.public_send(method) }
|
|
771
|
+
else
|
|
772
|
+
results
|
|
773
|
+
end
|
|
714
774
|
when :resolver_class
|
|
715
775
|
results = Array.new(objects.size, nil)
|
|
716
776
|
ps = @pending_steps ||= []
|
|
@@ -33,14 +33,15 @@ module GraphQL
|
|
|
33
33
|
|
|
34
34
|
targets.each_with_index do |target, idx|
|
|
35
35
|
if target.is_a?(Hash)
|
|
36
|
-
|
|
36
|
+
value_at_key = target[key]
|
|
37
|
+
if value_at_key.equal?(err)
|
|
37
38
|
tf = @finalizers[target] ||= {}.compare_by_identity
|
|
38
39
|
tf[key] = err
|
|
39
40
|
@finalizers_count += 1
|
|
40
|
-
elsif
|
|
41
|
-
|
|
41
|
+
elsif value_at_key.is_a?(Array)
|
|
42
|
+
value_at_key.each_with_index do |el, idx|
|
|
42
43
|
if el.equal?(err)
|
|
43
|
-
tf = @finalizers[
|
|
44
|
+
tf = @finalizers[value_at_key] ||= {}.compare_by_identity
|
|
44
45
|
tf[idx] = err
|
|
45
46
|
@finalizers_count += 1
|
|
46
47
|
end
|
|
@@ -150,7 +151,7 @@ module GraphQL
|
|
|
150
151
|
when Language::Nodes::InlineFragment
|
|
151
152
|
static_type_at_result = @static_type_at[result_h]
|
|
152
153
|
if static_type_at_result && (
|
|
153
|
-
|
|
154
|
+
(t = ast_selection.type).nil? ||
|
|
154
155
|
@runner.type_condition_applies?(@query.context, static_type_at_result, t.name)
|
|
155
156
|
)
|
|
156
157
|
result_h = check_object_result(result_h, parent_type, ast_selection.selections)
|
|
@@ -170,9 +171,7 @@ module GraphQL
|
|
|
170
171
|
end
|
|
171
172
|
|
|
172
173
|
def check_list_result(result_arr, inner_type, ast_selections)
|
|
173
|
-
inner_type_non_null =
|
|
174
|
-
if inner_type.non_null?
|
|
175
|
-
inner_type_non_null = true
|
|
174
|
+
if (inner_type_non_null = inner_type.non_null?)
|
|
176
175
|
inner_type = inner_type.of_type
|
|
177
176
|
end
|
|
178
177
|
|
|
@@ -492,12 +492,6 @@ module GraphQL
|
|
|
492
492
|
end
|
|
493
493
|
end
|
|
494
494
|
end
|
|
495
|
-
# If this field is a root mutation field, immediately resolve
|
|
496
|
-
# all of its child fields before moving on to the next root mutation field.
|
|
497
|
-
# (Subselections of this mutation will still be resolved level-by-level.)
|
|
498
|
-
if selection_result.graphql_is_eager
|
|
499
|
-
@dataloader.run
|
|
500
|
-
end
|
|
501
495
|
end
|
|
502
496
|
|
|
503
497
|
def set_result(selection_result, result_name, value, is_child_result, is_non_null)
|
|
@@ -771,8 +765,7 @@ module GraphQL
|
|
|
771
765
|
|
|
772
766
|
response_list
|
|
773
767
|
rescue NoMethodError => err
|
|
774
|
-
|
|
775
|
-
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
|
768
|
+
if err.name == :each && err.receiver == value
|
|
776
769
|
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
|
777
770
|
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
|
778
771
|
else
|
|
@@ -22,6 +22,7 @@ module GraphQL
|
|
|
22
22
|
# @param max_complexity [Integer, nil]
|
|
23
23
|
# @return [Array<GraphQL::Query::Result>] One result per query
|
|
24
24
|
def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
25
|
+
previous_multiplex = Fiber[:__graphql_current_multiplex]
|
|
25
26
|
queries = query_options.map do |opts|
|
|
26
27
|
query = case opts
|
|
27
28
|
when Hash
|
|
@@ -119,13 +120,18 @@ module GraphQL
|
|
|
119
120
|
end
|
|
120
121
|
|
|
121
122
|
results
|
|
123
|
+
rescue SystemStackError => err
|
|
124
|
+
queries.map do |query|
|
|
125
|
+
schema.query_stack_error(query, err)
|
|
126
|
+
query.result_values ||= { "errors" => query.context.errors.map(&:to_h) }
|
|
127
|
+
query.result
|
|
128
|
+
end
|
|
122
129
|
rescue Exception
|
|
123
130
|
# TODO rescue at a higher level so it will catch errors in analysis, too
|
|
124
131
|
# Assign values here so that the query's `@executed` becomes true
|
|
125
132
|
queries.map { |q| q.result_values ||= {} }
|
|
126
133
|
raise
|
|
127
134
|
ensure
|
|
128
|
-
Fiber[:__graphql_current_multiplex] = nil
|
|
129
135
|
queries.map { |query|
|
|
130
136
|
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
|
131
137
|
if runtime
|
|
@@ -134,6 +140,8 @@ module GraphQL
|
|
|
134
140
|
}
|
|
135
141
|
end
|
|
136
142
|
end
|
|
143
|
+
ensure
|
|
144
|
+
Fiber[:__graphql_current_multiplex] = previous_multiplex
|
|
137
145
|
end
|
|
138
146
|
end
|
|
139
147
|
|
|
@@ -73,6 +73,10 @@ module GraphQL
|
|
|
73
73
|
@field_resolve_step.set_current_field(nil)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
def add_field_error(err)
|
|
77
|
+
@field_resolve_step.add_graphql_error(@graphql_result, @key, err)
|
|
78
|
+
end
|
|
79
|
+
|
|
76
80
|
def authorize
|
|
77
81
|
if @field_resolve_step.was_scoped && !@resolved_type.reauthorize_scoped_objects
|
|
78
82
|
@authorized_value = @object
|
|
@@ -96,13 +100,13 @@ module GraphQL
|
|
|
96
100
|
create_result
|
|
97
101
|
end
|
|
98
102
|
rescue GraphQL::RuntimeError => err
|
|
99
|
-
@graphql_result[@key] =
|
|
103
|
+
@graphql_result[@key] = add_field_error(err)
|
|
100
104
|
rescue StandardError => err
|
|
101
105
|
query ||= @field_resolve_step.selections_step.query
|
|
102
106
|
begin
|
|
103
107
|
query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
|
|
104
108
|
rescue GraphQL::RuntimeError => err
|
|
105
|
-
@graphql_result[@key] =
|
|
109
|
+
@graphql_result[@key] = add_field_error(err)
|
|
106
110
|
end
|
|
107
111
|
end
|
|
108
112
|
|
|
@@ -120,13 +124,13 @@ module GraphQL
|
|
|
120
124
|
elsif @is_non_null
|
|
121
125
|
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
122
126
|
else
|
|
123
|
-
@graphql_result[@key] =
|
|
127
|
+
@graphql_result[@key] = add_field_error(@authorization_error)
|
|
124
128
|
end
|
|
125
129
|
rescue GraphQL::RuntimeError => err
|
|
126
130
|
if @is_non_null
|
|
127
131
|
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
128
132
|
else
|
|
129
|
-
@graphql_result[@key] =
|
|
133
|
+
@graphql_result[@key] = add_field_error(err)
|
|
130
134
|
end
|
|
131
135
|
end
|
|
132
136
|
end
|
|
@@ -5,7 +5,6 @@ module GraphQL
|
|
|
5
5
|
def initialize(multiplex)
|
|
6
6
|
@multiplex = multiplex
|
|
7
7
|
@schema = multiplex.schema
|
|
8
|
-
@steps_queue = []
|
|
9
8
|
@runtime_type_at = {}.compare_by_identity
|
|
10
9
|
@static_type_at = {}.compare_by_identity
|
|
11
10
|
@finalizers = nil
|
|
@@ -53,7 +52,7 @@ module GraphQL
|
|
|
53
52
|
@dataloader.append_job(step)
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
attr_reader :
|
|
55
|
+
attr_reader :schema, :variables, :dataloader, :resolves_lazies, :authorizes, :static_type_at, :runtime_type_at, :finalizers, :input_values
|
|
57
56
|
|
|
58
57
|
# @return [void]
|
|
59
58
|
def add_finalizer(query, result_value, key, finalizer)
|
|
@@ -73,6 +72,7 @@ module GraphQL
|
|
|
73
72
|
end
|
|
74
73
|
|
|
75
74
|
def execute
|
|
75
|
+
previous_multiplex = Fiber[:__graphql_current_multiplex]
|
|
76
76
|
Fiber[:__graphql_current_multiplex] = @multiplex
|
|
77
77
|
isolated_steps = [[]]
|
|
78
78
|
trace = @multiplex.current_trace
|
|
@@ -156,8 +156,14 @@ module GraphQL
|
|
|
156
156
|
query.result
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
|
+
rescue SystemStackError => err
|
|
160
|
+
@multiplex.queries.map do |query|
|
|
161
|
+
@schema.query_stack_error(query, err)
|
|
162
|
+
query.result_values ||= { "errors" => query.context.errors.map(&:to_h) }
|
|
163
|
+
query.result
|
|
164
|
+
end
|
|
159
165
|
ensure
|
|
160
|
-
Fiber[:__graphql_current_multiplex] =
|
|
166
|
+
Fiber[:__graphql_current_multiplex] = previous_multiplex
|
|
161
167
|
end
|
|
162
168
|
|
|
163
169
|
def gather_selections(type_defn, ast_selections, selections_step, query, all_selections, prototype_result, into:)
|
|
@@ -281,8 +287,8 @@ module GraphQL
|
|
|
281
287
|
end
|
|
282
288
|
result = dir_defn.resolve_operation(selected_operation, query, objects, dir_args, query.context)
|
|
283
289
|
if result.is_a?(Finalizer)
|
|
284
|
-
result.path =
|
|
285
|
-
add_finalizer(query,
|
|
290
|
+
result.path = beginning_path
|
|
291
|
+
add_finalizer(query, data, nil, result)
|
|
286
292
|
if result.is_a?(HaltExecution)
|
|
287
293
|
continue_execution = false
|
|
288
294
|
break
|
|
@@ -298,6 +304,7 @@ module GraphQL
|
|
|
298
304
|
if query.query?
|
|
299
305
|
isolated_steps[0] << SelectionsStep.new(
|
|
300
306
|
parent_type: root_type,
|
|
307
|
+
field_resolve_step: nil,
|
|
301
308
|
selections: selected_operation.selections,
|
|
302
309
|
objects: objects,
|
|
303
310
|
results: [data],
|
|
@@ -317,6 +324,7 @@ module GraphQL
|
|
|
317
324
|
isolated_steps << [SelectionsStep.new(
|
|
318
325
|
clobber: false, # `data` is being shared among several selections steps
|
|
319
326
|
parent_type: root_type,
|
|
327
|
+
field_resolve_step: field_resolve_step,
|
|
320
328
|
selections: field_resolve_step.ast_nodes || Array(field_resolve_step.ast_node),
|
|
321
329
|
objects: objects,
|
|
322
330
|
results: [data],
|
|
@@ -332,6 +340,7 @@ module GraphQL
|
|
|
332
340
|
end
|
|
333
341
|
isolated_steps[0] << SelectionsStep.new(
|
|
334
342
|
parent_type: root_type,
|
|
343
|
+
field_resolve_step: nil,
|
|
335
344
|
selections: selected_operation.selections,
|
|
336
345
|
objects: objects,
|
|
337
346
|
results: [data],
|
|
@@ -355,6 +364,7 @@ module GraphQL
|
|
|
355
364
|
results << { "data" => data }
|
|
356
365
|
isolated_steps[0] << SelectionsStep.new(
|
|
357
366
|
parent_type: resolved_type,
|
|
367
|
+
field_resolve_step: nil,
|
|
358
368
|
selections: selected_operation.selections,
|
|
359
369
|
objects: objects,
|
|
360
370
|
results: [data],
|
|
@@ -372,6 +382,7 @@ module GraphQL
|
|
|
372
382
|
results << { "data" => list_result }
|
|
373
383
|
isolated_steps[0] << SelectionsStep.new(
|
|
374
384
|
parent_type: inner_type,
|
|
385
|
+
field_resolve_step: nil,
|
|
375
386
|
selections: selected_operation.selections,
|
|
376
387
|
objects: root_value,
|
|
377
388
|
results: list_result,
|
|
@@ -420,6 +431,7 @@ module GraphQL
|
|
|
420
431
|
selections = partial.ast_nodes
|
|
421
432
|
dummy_ss = SelectionsStep.new(
|
|
422
433
|
parent_type: nil,
|
|
434
|
+
field_resolve_step: nil,
|
|
423
435
|
selections: selections,
|
|
424
436
|
objects: nil,
|
|
425
437
|
results: nil,
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
module GraphQL
|
|
3
3
|
module Execution
|
|
4
4
|
class SelectionsStep
|
|
5
|
-
def initialize(parent_type:, selections:, objects:, results:, runner:, query:, path:, clobber: true)
|
|
5
|
+
def initialize(parent_type:, field_resolve_step:, selections:, objects:, results:, runner:, query:, path:, clobber: true)
|
|
6
6
|
@path = path
|
|
7
|
+
@field_resolve_step = field_resolve_step
|
|
7
8
|
@parent_type = parent_type
|
|
8
9
|
@selections = selections
|
|
9
10
|
@runner = runner
|
|
@@ -12,10 +13,13 @@ module GraphQL
|
|
|
12
13
|
@query = query
|
|
13
14
|
@graphql_objects = nil
|
|
14
15
|
@all_selections = nil
|
|
16
|
+
@killed = false
|
|
15
17
|
@clobber = clobber
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
attr_reader :path, :query, :objects, :results
|
|
20
|
+
attr_reader :path, :query, :objects, :results, :field_resolve_step
|
|
21
|
+
|
|
22
|
+
attr_accessor :killed
|
|
19
23
|
|
|
20
24
|
def graphql_objects
|
|
21
25
|
@graphql_objects ||= @objects.map do |obj|
|
|
@@ -79,12 +83,12 @@ module GraphQL
|
|
|
79
83
|
end
|
|
80
84
|
end
|
|
81
85
|
|
|
82
|
-
continue_selections.each do |frs|
|
|
83
|
-
@runner.add_step(frs)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
86
|
i += 2
|
|
87
87
|
end
|
|
88
|
+
|
|
89
|
+
continue_selections.each do |frs|
|
|
90
|
+
@runner.add_step(frs)
|
|
91
|
+
end
|
|
88
92
|
end
|
|
89
93
|
end
|
|
90
94
|
end
|
|
@@ -26,6 +26,10 @@ module GraphQL
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def finalize_graphql_result(query, result_data, key)
|
|
29
|
+
add_this_error = !query.context.errors.any? { |e| e.eql?(self) }
|
|
30
|
+
if add_this_error
|
|
31
|
+
query.context.add_error(self)
|
|
32
|
+
end
|
|
29
33
|
if ast_node.is_a?(GraphQL::Language::Nodes::Directive)
|
|
30
34
|
# This is for backwards compatibility ... what does the spec say?
|
|
31
35
|
result_data.delete(key)
|