graphql 2.0.17 → 2.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast.rb +2 -2
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime.rb +96 -88
- data/lib/graphql/execution/interpreter.rb +8 -13
- data/lib/graphql/execution/lazy.rb +2 -4
- data/lib/graphql/execution/multiplex.rb +2 -1
- data/lib/graphql/language/lexer.rb +216 -1505
- data/lib/graphql/language/lexer.ri +744 -0
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +39 -38
- data/lib/graphql/language/parser.y +38 -37
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/query/context.rb +57 -27
- data/lib/graphql/query.rb +15 -2
- data/lib/graphql/schema/field.rb +31 -19
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_fields.rb +6 -1
- data/lib/graphql/schema/object.rb +2 -4
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/timeout.rb +24 -28
- data/lib/graphql/schema/warden.rb +8 -1
- data/lib/graphql/schema.rb +42 -0
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +231 -0
- data/lib/graphql/tracing/appsignal_trace.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +148 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +41 -0
- data/lib/graphql/tracing/platform_trace.rb +107 -0
- data/lib/graphql/tracing/platform_tracing.rb +15 -3
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing.rb +136 -39
- data/lib/graphql/type_kinds.rb +6 -3
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -7
- metadata +31 -7
- data/lib/graphql/language/lexer.rl +0 -280
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e3f276db828fe3908e759e87f8cc5d33ae761c3ea80bd53b92b16a971ef507a
|
4
|
+
data.tar.gz: fc59e806f45125da3065ed4d7758170f88231377664b9a130e0896d6b2790fa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 580e967d89228c0174072f5857cb646ea7495ee5a00587249ffc3034ffe9b536f751c09b8112e443b0c93efcd868db01c584497364897d23c7e798ee3f496d64
|
7
|
+
data.tar.gz: 43db761134f0ea83311f6a8b62b3b34b9dccdc335869c9067f43dcc5b9fc7f4023b77b8bb70cf203435ac25711d16d0ce5aa620478d351950d0db9f2e7bee9c7
|
data/lib/graphql/analysis/ast.rb
CHANGED
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
def analyze_multiplex(multiplex, analyzers)
|
22
22
|
multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
|
23
23
|
|
24
|
-
multiplex.
|
24
|
+
multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
|
25
25
|
query_results = multiplex.queries.map do |query|
|
26
26
|
if query.valid?
|
27
27
|
analyze_query(
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
# @param analyzers [Array<GraphQL::Analysis::AST::Analyzer>]
|
49
49
|
# @return [Array<Any>] Results from those analyzers
|
50
50
|
def analyze_query(query, analyzers, multiplex_analyzers: [])
|
51
|
-
query.
|
51
|
+
query.current_trace.analyze_query(query: query) do
|
52
52
|
query_analyzers = analyzers
|
53
53
|
.map { |analyzer| analyzer.new(query) }
|
54
54
|
.select { |analyzer| analyzer.analyze? }
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
when "execute_field", "execute_field_lazy"
|
26
26
|
query = metadata[:query]
|
27
27
|
multiplex = query.multiplex
|
28
|
-
push_key =
|
28
|
+
push_key = query.context[:current_path]
|
29
29
|
parent_frame = multiplex.context[:graphql_backtrace_contexts][push_key[0..-2]]
|
30
30
|
|
31
31
|
if parent_frame.is_a?(GraphQL::Query)
|
@@ -11,6 +11,25 @@ module GraphQL
|
|
11
11
|
nil
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.resolve_each_depth(lazies_at_depth, dataloader)
|
15
|
+
depths = lazies_at_depth.keys
|
16
|
+
depths.sort!
|
17
|
+
next_depth = depths.first
|
18
|
+
if next_depth
|
19
|
+
lazies = lazies_at_depth[next_depth]
|
20
|
+
lazies_at_depth.delete(next_depth)
|
21
|
+
if lazies.any?
|
22
|
+
dataloader.append_job {
|
23
|
+
lazies.each(&:value) # resolve these Lazy instances
|
24
|
+
}
|
25
|
+
# Run lazies _and_ dataloader, see if more are enqueued
|
26
|
+
dataloader.run
|
27
|
+
resolve_each_depth(lazies_at_depth, dataloader)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
14
33
|
# After getting `results` back from an interpreter evaluation,
|
15
34
|
# continue it until you get a response-ready Ruby value.
|
16
35
|
#
|
@@ -20,6 +20,15 @@ module GraphQL
|
|
20
20
|
@graphql_metadata = nil
|
21
21
|
end
|
22
22
|
|
23
|
+
def path
|
24
|
+
@path ||= build_path([])
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_path(path_array)
|
28
|
+
graphql_result_name && path_array.unshift(graphql_result_name)
|
29
|
+
@graphql_parent ? @graphql_parent.build_path(path_array) : path_array
|
30
|
+
end
|
31
|
+
|
23
32
|
attr_accessor :graphql_dead
|
24
33
|
attr_reader :graphql_parent, :graphql_result_name
|
25
34
|
|
@@ -157,9 +166,10 @@ module GraphQL
|
|
157
166
|
info
|
158
167
|
end
|
159
168
|
|
160
|
-
def initialize(query:)
|
169
|
+
def initialize(query:, lazies_at_depth:)
|
161
170
|
@query = query
|
162
171
|
@dataloader = query.multiplex.dataloader
|
172
|
+
@lazies_at_depth = lazies_at_depth
|
163
173
|
@schema = query.schema
|
164
174
|
@context = query.context
|
165
175
|
@multiplex_context = query.multiplex.context
|
@@ -208,8 +218,7 @@ module GraphQL
|
|
208
218
|
root_operation = query.selected_operation
|
209
219
|
root_op_type = root_operation.operation_type || "query"
|
210
220
|
root_type = schema.root_type_for_operation(root_op_type)
|
211
|
-
|
212
|
-
set_all_interpreter_context(query.root_value, nil, nil, path)
|
221
|
+
set_all_interpreter_context(query.root_value, nil, nil, nil, @response)
|
213
222
|
object_proxy = authorized_new(root_type, query.root_value, context)
|
214
223
|
object_proxy = schema.sync_lazy(object_proxy)
|
215
224
|
|
@@ -236,10 +245,9 @@ module GraphQL
|
|
236
245
|
end
|
237
246
|
|
238
247
|
@dataloader.append_job {
|
239
|
-
set_all_interpreter_context(query.root_value, nil, nil,
|
248
|
+
set_all_interpreter_context(query.root_value, nil, nil, nil, selection_response)
|
240
249
|
call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
|
241
250
|
evaluate_selections(
|
242
|
-
path,
|
243
251
|
object_proxy,
|
244
252
|
root_type,
|
245
253
|
root_op_type == "mutation",
|
@@ -253,10 +261,7 @@ module GraphQL
|
|
253
261
|
end
|
254
262
|
end
|
255
263
|
end
|
256
|
-
|
257
|
-
delete_interpreter_context(:current_field)
|
258
|
-
delete_interpreter_context(:current_object)
|
259
|
-
delete_interpreter_context(:current_arguments)
|
264
|
+
delete_all_interpreter_context
|
260
265
|
nil
|
261
266
|
end
|
262
267
|
|
@@ -358,15 +363,15 @@ module GraphQL
|
|
358
363
|
NO_ARGS = {}.freeze
|
359
364
|
|
360
365
|
# @return [void]
|
361
|
-
def evaluate_selections(
|
362
|
-
set_all_interpreter_context(owner_object, nil, nil,
|
366
|
+
def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
367
|
+
set_all_interpreter_context(owner_object, nil, nil, nil, selections_result)
|
363
368
|
|
364
369
|
finished_jobs = 0
|
365
370
|
enqueued_jobs = gathered_selections.size
|
366
371
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
367
372
|
@dataloader.append_job {
|
368
373
|
evaluate_selection(
|
369
|
-
|
374
|
+
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
|
370
375
|
)
|
371
376
|
finished_jobs += 1
|
372
377
|
if target_result && finished_jobs == enqueued_jobs
|
@@ -378,10 +383,8 @@ module GraphQL
|
|
378
383
|
selections_result
|
379
384
|
end
|
380
385
|
|
381
|
-
attr_reader :progress_path
|
382
|
-
|
383
386
|
# @return [void]
|
384
|
-
def evaluate_selection(
|
387
|
+
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
|
385
388
|
return if dead_result?(selections_result)
|
386
389
|
# As a performance optimization, the hash key will be a `Node` if
|
387
390
|
# there's only one selection of the field. But if there are multiple
|
@@ -412,9 +415,6 @@ module GraphQL
|
|
412
415
|
|
413
416
|
return_type = field_defn.type
|
414
417
|
|
415
|
-
next_path = path + [result_name]
|
416
|
-
next_path.freeze
|
417
|
-
|
418
418
|
# This seems janky, but we need to know
|
419
419
|
# the field's return type at this path in order
|
420
420
|
# to propagate `null`
|
@@ -422,7 +422,7 @@ module GraphQL
|
|
422
422
|
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
423
423
|
end
|
424
424
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
425
|
-
set_all_interpreter_context(nil, field_defn, nil,
|
425
|
+
set_all_interpreter_context(nil, field_defn, nil, result_name, selections_result)
|
426
426
|
object = owner_object
|
427
427
|
|
428
428
|
if is_introspection
|
@@ -432,19 +432,19 @@ module GraphQL
|
|
432
432
|
total_args_count = field_defn.arguments(context).size
|
433
433
|
if total_args_count == 0
|
434
434
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
435
|
-
evaluate_selection_with_args(resolved_arguments, field_defn,
|
435
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
|
436
436
|
else
|
437
437
|
# TODO remove all arguments(...) usages?
|
438
438
|
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
439
|
-
evaluate_selection_with_args(resolved_arguments, field_defn,
|
439
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
|
440
440
|
end
|
441
441
|
end
|
442
442
|
end
|
443
443
|
|
444
|
-
def evaluate_selection_with_args(arguments, field_defn,
|
445
|
-
after_lazy(arguments, owner: owner_type, field: field_defn,
|
444
|
+
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) # rubocop:disable Metrics/ParameterLists
|
445
|
+
after_lazy(arguments, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
446
446
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
447
|
-
continue_value(
|
447
|
+
continue_value(resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
448
448
|
next
|
449
449
|
end
|
450
450
|
|
@@ -460,9 +460,9 @@ module GraphQL
|
|
460
460
|
when :ast_node
|
461
461
|
extra_args[:ast_node] = ast_node
|
462
462
|
when :execution_errors
|
463
|
-
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node,
|
463
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path)
|
464
464
|
when :path
|
465
|
-
extra_args[:path] =
|
465
|
+
extra_args[:path] = current_path
|
466
466
|
when :lookahead
|
467
467
|
if !field_ast_nodes
|
468
468
|
field_ast_nodes = [ast_node]
|
@@ -489,7 +489,7 @@ module GraphQL
|
|
489
489
|
resolved_arguments.keyword_arguments
|
490
490
|
end
|
491
491
|
|
492
|
-
set_all_interpreter_context(nil, nil, resolved_arguments,
|
492
|
+
set_all_interpreter_context(nil, nil, resolved_arguments, result_name, selection_result)
|
493
493
|
|
494
494
|
# Optimize for the case that field is selected only once
|
495
495
|
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
@@ -507,7 +507,7 @@ module GraphQL
|
|
507
507
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
508
508
|
# Actually call the field resolver and capture the result
|
509
509
|
app_result = begin
|
510
|
-
query.
|
510
|
+
query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
511
511
|
field_defn.resolve(object, kwarg_arguments, context)
|
512
512
|
end
|
513
513
|
rescue GraphQL::ExecutionError => err
|
@@ -519,10 +519,10 @@ module GraphQL
|
|
519
519
|
ex_err
|
520
520
|
end
|
521
521
|
end
|
522
|
-
after_lazy(app_result, owner: owner_type, field: field_defn,
|
523
|
-
continue_value = continue_value(
|
522
|
+
after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
|
523
|
+
continue_value = continue_value(inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
524
524
|
if HALT != continue_value
|
525
|
-
continue_field(
|
525
|
+
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
526
526
|
end
|
527
527
|
end
|
528
528
|
end
|
@@ -587,8 +587,30 @@ module GraphQL
|
|
587
587
|
end
|
588
588
|
end
|
589
589
|
|
590
|
+
def current_path
|
591
|
+
ti = thread_info
|
592
|
+
path = ti &&
|
593
|
+
(result = ti[:current_result]) &&
|
594
|
+
(result.path)
|
595
|
+
if path && (rn = ti[:current_result_name])
|
596
|
+
path = path.dup
|
597
|
+
path.push(rn)
|
598
|
+
end
|
599
|
+
path
|
600
|
+
end
|
601
|
+
|
602
|
+
def current_depth
|
603
|
+
ti = thread_info
|
604
|
+
depth = 1
|
605
|
+
result = ti[:current_result]
|
606
|
+
while (result = result.graphql_parent)
|
607
|
+
depth += 1
|
608
|
+
end
|
609
|
+
depth
|
610
|
+
end
|
611
|
+
|
590
612
|
HALT = Object.new
|
591
|
-
def continue_value(
|
613
|
+
def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
592
614
|
case value
|
593
615
|
when nil
|
594
616
|
if is_non_null
|
@@ -607,7 +629,7 @@ module GraphQL
|
|
607
629
|
# every time.
|
608
630
|
if value.is_a?(GraphQL::ExecutionError)
|
609
631
|
if selection_result.nil? || !dead_result?(selection_result)
|
610
|
-
value.path ||=
|
632
|
+
value.path ||= current_path
|
611
633
|
value.ast_node ||= ast_node
|
612
634
|
context.errors << value
|
613
635
|
if selection_result
|
@@ -624,7 +646,7 @@ module GraphQL
|
|
624
646
|
rescue GraphQL::ExecutionError => err
|
625
647
|
err
|
626
648
|
end
|
627
|
-
continue_value(
|
649
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
628
650
|
elsif value.is_a?(GraphQL::UnauthorizedError)
|
629
651
|
# this hook might raise & crash, or it might return
|
630
652
|
# a replacement value
|
@@ -633,7 +655,7 @@ module GraphQL
|
|
633
655
|
rescue GraphQL::ExecutionError => err
|
634
656
|
err
|
635
657
|
end
|
636
|
-
continue_value(
|
658
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
637
659
|
elsif GraphQL::Execution::SKIP == value
|
638
660
|
# It's possible a lazy was already written here
|
639
661
|
case selection_result
|
@@ -659,7 +681,7 @@ module GraphQL
|
|
659
681
|
if selection_result.nil? || !dead_result?(selection_result)
|
660
682
|
value.each_with_index do |error, index|
|
661
683
|
error.ast_node ||= ast_node
|
662
|
-
error.path ||=
|
684
|
+
error.path ||= current_path + (list_type_at_all ? [index] : [])
|
663
685
|
context.errors << error
|
664
686
|
end
|
665
687
|
if selection_result
|
@@ -692,7 +714,7 @@ module GraphQL
|
|
692
714
|
# Location information from `path` and `ast_node`.
|
693
715
|
#
|
694
716
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
695
|
-
def continue_field(
|
717
|
+
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
|
696
718
|
if current_type.non_null?
|
697
719
|
current_type = current_type.of_type
|
698
720
|
is_non_null = true
|
@@ -708,8 +730,8 @@ module GraphQL
|
|
708
730
|
set_result(selection_result, result_name, r)
|
709
731
|
r
|
710
732
|
when "UNION", "INTERFACE"
|
711
|
-
resolved_type_or_lazy = resolve_type(current_type, value
|
712
|
-
after_lazy(resolved_type_or_lazy, owner: current_type,
|
733
|
+
resolved_type_or_lazy = resolve_type(current_type, value)
|
734
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, 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|
|
713
735
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
714
736
|
resolved_type, resolved_value = resolved_type_result
|
715
737
|
else
|
@@ -726,7 +748,7 @@ module GraphQL
|
|
726
748
|
set_result(selection_result, result_name, nil)
|
727
749
|
nil
|
728
750
|
else
|
729
|
-
continue_field(
|
751
|
+
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
730
752
|
end
|
731
753
|
end
|
732
754
|
when "OBJECT"
|
@@ -735,8 +757,8 @@ module GraphQL
|
|
735
757
|
rescue GraphQL::ExecutionError => err
|
736
758
|
err
|
737
759
|
end
|
738
|
-
after_lazy(object_proxy, owner: current_type,
|
739
|
-
continue_value = continue_value(
|
760
|
+
after_lazy(object_proxy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
|
761
|
+
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
740
762
|
if HALT != continue_value
|
741
763
|
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
742
764
|
set_result(selection_result, result_name, response_hash)
|
@@ -757,10 +779,10 @@ module GraphQL
|
|
757
779
|
this_result = response_hash
|
758
780
|
final_result = nil
|
759
781
|
end
|
760
|
-
|
782
|
+
# Don't pass `result_name` here because it's already included in the new response hash
|
783
|
+
set_all_interpreter_context(continue_value, nil, nil, nil, this_result) # reset this mutable state
|
761
784
|
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
762
785
|
evaluate_selections(
|
763
|
-
path,
|
764
786
|
continue_value,
|
765
787
|
current_type,
|
766
788
|
false,
|
@@ -781,40 +803,27 @@ module GraphQL
|
|
781
803
|
response_list = GraphQLResultArray.new(result_name, selection_result)
|
782
804
|
response_list.graphql_non_null_list_items = inner_type.non_null?
|
783
805
|
set_result(selection_result, result_name, response_list)
|
784
|
-
result_was_set = false
|
785
806
|
idx = 0
|
786
807
|
list_value = begin
|
787
808
|
value.each do |inner_value|
|
788
809
|
break if dead_result?(response_list)
|
789
|
-
if !result_was_set
|
790
|
-
# Don't set the result unless `.each` is successful
|
791
|
-
set_result(selection_result, result_name, response_list)
|
792
|
-
result_was_set = true
|
793
|
-
end
|
794
|
-
next_path = path + [idx]
|
795
810
|
this_idx = idx
|
796
|
-
next_path.freeze
|
797
811
|
idx += 1
|
798
812
|
if use_dataloader_job
|
799
813
|
@dataloader.append_job do
|
800
|
-
resolve_list_item(inner_value, inner_type,
|
814
|
+
resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
801
815
|
end
|
802
816
|
else
|
803
|
-
resolve_list_item(inner_value, inner_type,
|
817
|
+
resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
804
818
|
end
|
805
819
|
end
|
806
|
-
# Maybe the list was empty and the block was never called.
|
807
|
-
if !result_was_set
|
808
|
-
set_result(selection_result, result_name, response_list)
|
809
|
-
result_was_set = true
|
810
|
-
end
|
811
820
|
|
812
821
|
response_list
|
813
822
|
rescue NoMethodError => err
|
814
823
|
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
815
824
|
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
816
825
|
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
817
|
-
raise ListResultFailedError.new(value: value, field: field, path:
|
826
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
818
827
|
else
|
819
828
|
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
820
829
|
raise
|
@@ -829,20 +838,20 @@ module GraphQL
|
|
829
838
|
end
|
830
839
|
end
|
831
840
|
|
832
|
-
continue_value(
|
841
|
+
continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
|
833
842
|
else
|
834
843
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
835
844
|
end
|
836
845
|
end
|
837
846
|
|
838
|
-
def resolve_list_item(inner_value, inner_type,
|
839
|
-
set_all_interpreter_context(nil, nil, nil,
|
847
|
+
def resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
|
848
|
+
set_all_interpreter_context(nil, nil, nil, this_idx, response_list)
|
840
849
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
841
850
|
# This will update `response_list` with the lazy
|
842
|
-
after_lazy(inner_value, owner: inner_type,
|
843
|
-
continue_value = continue_value(
|
851
|
+
after_lazy(inner_value, owner: inner_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
|
852
|
+
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
|
844
853
|
if HALT != continue_value
|
845
|
-
continue_field(
|
854
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
846
855
|
end
|
847
856
|
end
|
848
857
|
end
|
@@ -861,7 +870,6 @@ module GraphQL
|
|
861
870
|
dir_defn = @schema_directives.fetch(dir_node.name)
|
862
871
|
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
863
872
|
dir_args = continue_value(
|
864
|
-
@context[:current_path], # path
|
865
873
|
raw_dir_args, # value
|
866
874
|
dir_defn, # parent_type
|
867
875
|
nil, # field
|
@@ -893,7 +901,7 @@ module GraphQL
|
|
893
901
|
true
|
894
902
|
end
|
895
903
|
|
896
|
-
def set_all_interpreter_context(object, field, arguments,
|
904
|
+
def set_all_interpreter_context(object, field, arguments, result_name, result)
|
897
905
|
ti = thread_info
|
898
906
|
if object
|
899
907
|
ti[:current_object] = object
|
@@ -904,26 +912,26 @@ module GraphQL
|
|
904
912
|
if arguments
|
905
913
|
ti[:current_arguments] = arguments
|
906
914
|
end
|
907
|
-
|
908
|
-
|
915
|
+
ti[:current_result_name] = result_name
|
916
|
+
if result
|
917
|
+
ti[:current_result] = result
|
909
918
|
end
|
910
919
|
end
|
911
920
|
|
912
921
|
# @param obj [Object] Some user-returned value that may want to be batched
|
913
|
-
# @param path [Array<String>]
|
914
922
|
# @param field [GraphQL::Schema::Field]
|
915
923
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
916
924
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
917
925
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
918
|
-
def after_lazy(lazy_obj, owner:, field:,
|
926
|
+
def after_lazy(lazy_obj, owner:, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
919
927
|
if lazy?(lazy_obj)
|
920
|
-
lazy = GraphQL::Execution::Lazy.new(
|
921
|
-
set_all_interpreter_context(owner_object, field, arguments,
|
928
|
+
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
929
|
+
set_all_interpreter_context(owner_object, field, arguments, result_name, result)
|
922
930
|
# Wrap the execution of _this_ method with tracing,
|
923
931
|
# but don't wrap the continuation below
|
924
932
|
inner_obj = begin
|
925
933
|
if trace
|
926
|
-
query.
|
934
|
+
query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
927
935
|
schema.sync_lazy(lazy_obj)
|
928
936
|
end
|
929
937
|
else
|
@@ -945,10 +953,11 @@ module GraphQL
|
|
945
953
|
lazy.value
|
946
954
|
else
|
947
955
|
set_result(result, result_name, lazy)
|
956
|
+
@lazies_at_depth[current_depth] << lazy
|
948
957
|
lazy
|
949
958
|
end
|
950
959
|
else
|
951
|
-
set_all_interpreter_context(owner_object, field, arguments,
|
960
|
+
set_all_interpreter_context(owner_object, field, arguments, result_name, result)
|
952
961
|
yield(lazy_obj)
|
953
962
|
end
|
954
963
|
end
|
@@ -962,25 +971,24 @@ module GraphQL
|
|
962
971
|
end
|
963
972
|
end
|
964
973
|
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
974
|
+
def delete_all_interpreter_context
|
975
|
+
if (ti = thread_info)
|
976
|
+
ti.delete(:current_result)
|
977
|
+
ti.delete(:current_result_name)
|
978
|
+
ti.delete(:current_field)
|
979
|
+
ti.delete(:current_object)
|
980
|
+
ti.delete(:current_arguments)
|
981
|
+
end
|
973
982
|
end
|
974
983
|
|
975
|
-
def resolve_type(type, value
|
976
|
-
|
977
|
-
resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
|
984
|
+
def resolve_type(type, value)
|
985
|
+
resolved_type, resolved_value = query.current_trace.resolve_type(query: query, type: type, object: value) do
|
978
986
|
query.resolve_type(type, value)
|
979
987
|
end
|
980
988
|
|
981
989
|
if lazy?(resolved_type)
|
982
990
|
GraphQL::Execution::Lazy.new do
|
983
|
-
query.
|
991
|
+
query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
984
992
|
schema.sync_lazy(resolved_type)
|
985
993
|
end
|
986
994
|
end
|
@@ -34,11 +34,12 @@ module GraphQL
|
|
34
34
|
end
|
35
35
|
|
36
36
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
37
|
-
multiplex.
|
37
|
+
multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
|
38
38
|
schema = multiplex.schema
|
39
39
|
queries = multiplex.queries
|
40
40
|
query_instrumenters = schema.instrumenters[:query]
|
41
41
|
multiplex_instrumenters = schema.instrumenters[:multiplex]
|
42
|
+
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
42
43
|
|
43
44
|
# First, run multiplex instrumentation, then query instrumentation for each query
|
44
45
|
call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
|
@@ -67,10 +68,10 @@ module GraphQL
|
|
67
68
|
# Although queries in a multiplex _share_ an Interpreter instance,
|
68
69
|
# they also have another item of state, which is private to that query
|
69
70
|
# in particular, assign it here:
|
70
|
-
runtime = Runtime.new(query: query)
|
71
|
+
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
71
72
|
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
72
73
|
|
73
|
-
query.
|
74
|
+
query.current_trace.execute_query(query: query) do
|
74
75
|
runtime.run_eager
|
75
76
|
end
|
76
77
|
rescue GraphQL::ExecutionError => err
|
@@ -95,16 +96,13 @@ module GraphQL
|
|
95
96
|
runtime ? runtime.final_result : nil
|
96
97
|
end
|
97
98
|
final_values.compact!
|
98
|
-
tracer.
|
99
|
-
Interpreter::Resolve.
|
99
|
+
tracer.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
100
|
+
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
100
101
|
end
|
101
102
|
queries.each do |query|
|
102
103
|
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
103
104
|
if runtime
|
104
|
-
runtime.
|
105
|
-
runtime.delete_interpreter_context(:current_field)
|
106
|
-
runtime.delete_interpreter_context(:current_object)
|
107
|
-
runtime.delete_interpreter_context(:current_arguments)
|
105
|
+
runtime.delete_all_interpreter_context
|
108
106
|
end
|
109
107
|
end
|
110
108
|
}
|
@@ -150,10 +148,7 @@ module GraphQL
|
|
150
148
|
queries.map { |query|
|
151
149
|
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
152
150
|
if runtime
|
153
|
-
runtime.
|
154
|
-
runtime.delete_interpreter_context(:current_field)
|
155
|
-
runtime.delete_interpreter_context(:current_object)
|
156
|
-
runtime.delete_interpreter_context(:current_arguments)
|
151
|
+
runtime.delete_all_interpreter_context
|
157
152
|
end
|
158
153
|
}
|
159
154
|
end
|
@@ -12,16 +12,14 @@ module GraphQL
|
|
12
12
|
# - It has no error-catching functionality
|
13
13
|
# @api private
|
14
14
|
class Lazy
|
15
|
-
attr_reader :
|
15
|
+
attr_reader :field
|
16
16
|
|
17
17
|
# Create a {Lazy} which will get its inner value by calling the block
|
18
|
-
# @param path [Array<String, Integer>]
|
19
18
|
# @param field [GraphQL::Schema::Field]
|
20
19
|
# @param get_value_func [Proc] a block to get the inner value (later)
|
21
|
-
def initialize(
|
20
|
+
def initialize(field: nil, &get_value_func)
|
22
21
|
@get_value_func = get_value_func
|
23
22
|
@resolved = false
|
24
|
-
@path = path
|
25
23
|
@field = field
|
26
24
|
end
|
27
25
|
|
@@ -25,13 +25,14 @@ module GraphQL
|
|
25
25
|
class Multiplex
|
26
26
|
include Tracing::Traceable
|
27
27
|
|
28
|
-
attr_reader :context, :queries, :schema, :max_complexity, :dataloader
|
28
|
+
attr_reader :context, :queries, :schema, :max_complexity, :dataloader, :current_trace
|
29
29
|
|
30
30
|
def initialize(schema:, queries:, context:, max_complexity:)
|
31
31
|
@schema = schema
|
32
32
|
@queries = queries
|
33
33
|
@queries.each { |q| q.multiplex = self }
|
34
34
|
@context = context
|
35
|
+
@current_trace = @context[:trace] || schema.new_trace(multiplex: self)
|
35
36
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
36
37
|
@tracers = schema.tracers + (context[:tracers] || [])
|
37
38
|
# Support `context: {backtrace: true}`
|