graphql 2.0.16 → 2.0.18
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.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/visitor.rb +42 -35
- 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 +106 -88
- data/lib/graphql/execution/interpreter.rb +14 -9
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/multiplex.rb +2 -1
- data/lib/graphql/graphql_ext.bundle +0 -0
- data/lib/graphql/introspection/directive_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/lexer.rb +216 -1505
- data/lib/graphql/language/lexer.ri +744 -0
- data/lib/graphql/language/nodes.rb +39 -31
- data/lib/graphql/language/parser.rb +9 -9
- data/lib/graphql/language/parser.y +9 -9
- data/lib/graphql/language/visitor.rb +191 -83
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/query/context.rb +45 -11
- data/lib/graphql/query.rb +15 -2
- data/lib/graphql/schema/argument.rb +0 -4
- data/lib/graphql/schema/directive.rb +12 -2
- data/lib/graphql/schema/enum.rb +24 -17
- data/lib/graphql/schema/enum_value.rb +5 -3
- data/lib/graphql/schema/field.rb +53 -45
- data/lib/graphql/schema/interface.rb +0 -10
- data/lib/graphql/schema/late_bound_type.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -14
- data/lib/graphql/schema/member/has_arguments.rb +104 -57
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_fields.rb +14 -2
- data/lib/graphql/schema/member/has_interfaces.rb +49 -8
- data/lib/graphql/schema/member/has_validators.rb +31 -5
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
- 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 +23 -27
- data/lib/graphql/schema/warden.rb +26 -4
- data/lib/graphql/schema.rb +37 -19
- data/lib/graphql/static_validation/literal_validator.rb +15 -1
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions/event.rb +2 -7
- 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/connection_behaviors.rb +0 -4
- data/lib/graphql/types/relay/edge_behaviors.rb +0 -4
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +7 -8
- metadata +15 -4
- data/lib/graphql/language/lexer.rl +0 -280
@@ -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
|
@@ -409,10 +412,8 @@ module GraphQL
|
|
409
412
|
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
410
413
|
end
|
411
414
|
end
|
412
|
-
return_type = field_defn.type
|
413
415
|
|
414
|
-
|
415
|
-
next_path.freeze
|
416
|
+
return_type = field_defn.type
|
416
417
|
|
417
418
|
# This seems janky, but we need to know
|
418
419
|
# the field's return type at this path in order
|
@@ -421,7 +422,7 @@ module GraphQL
|
|
421
422
|
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
422
423
|
end
|
423
424
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
424
|
-
set_all_interpreter_context(nil, field_defn, nil,
|
425
|
+
set_all_interpreter_context(nil, field_defn, nil, result_name, selections_result)
|
425
426
|
object = owner_object
|
426
427
|
|
427
428
|
if is_introspection
|
@@ -431,20 +432,19 @@ module GraphQL
|
|
431
432
|
total_args_count = field_defn.arguments(context).size
|
432
433
|
if total_args_count == 0
|
433
434
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
434
|
-
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)
|
435
436
|
else
|
436
437
|
# TODO remove all arguments(...) usages?
|
437
438
|
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
438
|
-
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)
|
439
440
|
end
|
440
441
|
end
|
441
442
|
end
|
442
443
|
|
443
|
-
def evaluate_selection_with_args(arguments, field_defn,
|
444
|
-
|
445
|
-
after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
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
|
@@ -615,6 +637,16 @@ module GraphQL
|
|
615
637
|
end
|
616
638
|
end
|
617
639
|
HALT
|
640
|
+
elsif value.is_a?(GraphQL::UnauthorizedFieldError)
|
641
|
+
value.field ||= field
|
642
|
+
# this hook might raise & crash, or it might return
|
643
|
+
# a replacement value
|
644
|
+
next_value = begin
|
645
|
+
schema.unauthorized_field(value)
|
646
|
+
rescue GraphQL::ExecutionError => err
|
647
|
+
err
|
648
|
+
end
|
649
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
618
650
|
elsif value.is_a?(GraphQL::UnauthorizedError)
|
619
651
|
# this hook might raise & crash, or it might return
|
620
652
|
# a replacement value
|
@@ -623,7 +655,7 @@ module GraphQL
|
|
623
655
|
rescue GraphQL::ExecutionError => err
|
624
656
|
err
|
625
657
|
end
|
626
|
-
continue_value(
|
658
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
627
659
|
elsif GraphQL::Execution::SKIP == value
|
628
660
|
# It's possible a lazy was already written here
|
629
661
|
case selection_result
|
@@ -649,7 +681,7 @@ module GraphQL
|
|
649
681
|
if selection_result.nil? || !dead_result?(selection_result)
|
650
682
|
value.each_with_index do |error, index|
|
651
683
|
error.ast_node ||= ast_node
|
652
|
-
error.path ||=
|
684
|
+
error.path ||= current_path + (list_type_at_all ? [index] : [])
|
653
685
|
context.errors << error
|
654
686
|
end
|
655
687
|
if selection_result
|
@@ -682,7 +714,7 @@ module GraphQL
|
|
682
714
|
# Location information from `path` and `ast_node`.
|
683
715
|
#
|
684
716
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
685
|
-
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
|
686
718
|
if current_type.non_null?
|
687
719
|
current_type = current_type.of_type
|
688
720
|
is_non_null = true
|
@@ -698,8 +730,8 @@ module GraphQL
|
|
698
730
|
set_result(selection_result, result_name, r)
|
699
731
|
r
|
700
732
|
when "UNION", "INTERFACE"
|
701
|
-
resolved_type_or_lazy = resolve_type(current_type, value
|
702
|
-
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|
|
703
735
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
704
736
|
resolved_type, resolved_value = resolved_type_result
|
705
737
|
else
|
@@ -716,7 +748,7 @@ module GraphQL
|
|
716
748
|
set_result(selection_result, result_name, nil)
|
717
749
|
nil
|
718
750
|
else
|
719
|
-
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)
|
720
752
|
end
|
721
753
|
end
|
722
754
|
when "OBJECT"
|
@@ -725,8 +757,8 @@ module GraphQL
|
|
725
757
|
rescue GraphQL::ExecutionError => err
|
726
758
|
err
|
727
759
|
end
|
728
|
-
after_lazy(object_proxy, owner: current_type,
|
729
|
-
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)
|
730
762
|
if HALT != continue_value
|
731
763
|
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
732
764
|
set_result(selection_result, result_name, response_hash)
|
@@ -747,10 +779,10 @@ module GraphQL
|
|
747
779
|
this_result = response_hash
|
748
780
|
final_result = nil
|
749
781
|
end
|
750
|
-
|
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
|
751
784
|
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
752
785
|
evaluate_selections(
|
753
|
-
path,
|
754
786
|
continue_value,
|
755
787
|
current_type,
|
756
788
|
false,
|
@@ -771,40 +803,27 @@ module GraphQL
|
|
771
803
|
response_list = GraphQLResultArray.new(result_name, selection_result)
|
772
804
|
response_list.graphql_non_null_list_items = inner_type.non_null?
|
773
805
|
set_result(selection_result, result_name, response_list)
|
774
|
-
result_was_set = false
|
775
806
|
idx = 0
|
776
807
|
list_value = begin
|
777
808
|
value.each do |inner_value|
|
778
809
|
break if dead_result?(response_list)
|
779
|
-
if !result_was_set
|
780
|
-
# Don't set the result unless `.each` is successful
|
781
|
-
set_result(selection_result, result_name, response_list)
|
782
|
-
result_was_set = true
|
783
|
-
end
|
784
|
-
next_path = path + [idx]
|
785
810
|
this_idx = idx
|
786
|
-
next_path.freeze
|
787
811
|
idx += 1
|
788
812
|
if use_dataloader_job
|
789
813
|
@dataloader.append_job do
|
790
|
-
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)
|
791
815
|
end
|
792
816
|
else
|
793
|
-
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)
|
794
818
|
end
|
795
819
|
end
|
796
|
-
# Maybe the list was empty and the block was never called.
|
797
|
-
if !result_was_set
|
798
|
-
set_result(selection_result, result_name, response_list)
|
799
|
-
result_was_set = true
|
800
|
-
end
|
801
820
|
|
802
821
|
response_list
|
803
822
|
rescue NoMethodError => err
|
804
823
|
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
805
824
|
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
806
825
|
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
807
|
-
raise ListResultFailedError.new(value: value, field: field, path:
|
826
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
808
827
|
else
|
809
828
|
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
810
829
|
raise
|
@@ -819,20 +838,20 @@ module GraphQL
|
|
819
838
|
end
|
820
839
|
end
|
821
840
|
|
822
|
-
continue_value(
|
841
|
+
continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
|
823
842
|
else
|
824
843
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
825
844
|
end
|
826
845
|
end
|
827
846
|
|
828
|
-
def resolve_list_item(inner_value, inner_type,
|
829
|
-
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)
|
830
849
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
831
850
|
# This will update `response_list` with the lazy
|
832
|
-
after_lazy(inner_value, owner: inner_type,
|
833
|
-
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)
|
834
853
|
if HALT != continue_value
|
835
|
-
continue_field(
|
854
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
836
855
|
end
|
837
856
|
end
|
838
857
|
end
|
@@ -851,7 +870,6 @@ module GraphQL
|
|
851
870
|
dir_defn = @schema_directives.fetch(dir_node.name)
|
852
871
|
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
853
872
|
dir_args = continue_value(
|
854
|
-
@context[:current_path], # path
|
855
873
|
raw_dir_args, # value
|
856
874
|
dir_defn, # parent_type
|
857
875
|
nil, # field
|
@@ -883,7 +901,7 @@ module GraphQL
|
|
883
901
|
true
|
884
902
|
end
|
885
903
|
|
886
|
-
def set_all_interpreter_context(object, field, arguments,
|
904
|
+
def set_all_interpreter_context(object, field, arguments, result_name, result)
|
887
905
|
ti = thread_info
|
888
906
|
if object
|
889
907
|
ti[:current_object] = object
|
@@ -894,26 +912,26 @@ module GraphQL
|
|
894
912
|
if arguments
|
895
913
|
ti[:current_arguments] = arguments
|
896
914
|
end
|
897
|
-
|
898
|
-
|
915
|
+
ti[:current_result_name] = result_name
|
916
|
+
if result
|
917
|
+
ti[:current_result] = result
|
899
918
|
end
|
900
919
|
end
|
901
920
|
|
902
921
|
# @param obj [Object] Some user-returned value that may want to be batched
|
903
|
-
# @param path [Array<String>]
|
904
922
|
# @param field [GraphQL::Schema::Field]
|
905
923
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
906
924
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
907
925
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
908
|
-
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)
|
909
927
|
if lazy?(lazy_obj)
|
910
|
-
lazy = GraphQL::Execution::Lazy.new(
|
911
|
-
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)
|
912
930
|
# Wrap the execution of _this_ method with tracing,
|
913
931
|
# but don't wrap the continuation below
|
914
932
|
inner_obj = begin
|
915
933
|
if trace
|
916
|
-
query.
|
934
|
+
query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
917
935
|
schema.sync_lazy(lazy_obj)
|
918
936
|
end
|
919
937
|
else
|
@@ -935,10 +953,11 @@ module GraphQL
|
|
935
953
|
lazy.value
|
936
954
|
else
|
937
955
|
set_result(result, result_name, lazy)
|
956
|
+
@lazies_at_depth[current_depth] << lazy
|
938
957
|
lazy
|
939
958
|
end
|
940
959
|
else
|
941
|
-
set_all_interpreter_context(owner_object, field, arguments,
|
960
|
+
set_all_interpreter_context(owner_object, field, arguments, result_name, result)
|
942
961
|
yield(lazy_obj)
|
943
962
|
end
|
944
963
|
end
|
@@ -952,25 +971,24 @@ module GraphQL
|
|
952
971
|
end
|
953
972
|
end
|
954
973
|
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
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
|
963
982
|
end
|
964
983
|
|
965
|
-
def resolve_type(type, value
|
966
|
-
|
967
|
-
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
|
968
986
|
query.resolve_type(type, value)
|
969
987
|
end
|
970
988
|
|
971
989
|
if lazy?(resolved_type)
|
972
990
|
GraphQL::Execution::Lazy.new do
|
973
|
-
query.
|
991
|
+
query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
974
992
|
schema.sync_lazy(resolved_type)
|
975
993
|
end
|
976
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
|
}
|
@@ -146,6 +144,13 @@ module GraphQL
|
|
146
144
|
# Assign values here so that the query's `@executed` becomes true
|
147
145
|
queries.map { |q| q.result_values ||= {} }
|
148
146
|
raise
|
147
|
+
ensure
|
148
|
+
queries.map { |query|
|
149
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
150
|
+
if runtime
|
151
|
+
runtime.delete_all_interpreter_context
|
152
|
+
end
|
153
|
+
}
|
149
154
|
end
|
150
155
|
end
|
151
156
|
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
|
|
@@ -29,15 +27,11 @@ module GraphQL
|
|
29
27
|
def value
|
30
28
|
if !@resolved
|
31
29
|
@resolved = true
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
v = v.value
|
36
|
-
end
|
37
|
-
v
|
38
|
-
rescue GraphQL::ExecutionError => err
|
39
|
-
err
|
30
|
+
v = @get_value_func.call
|
31
|
+
if v.is_a?(Lazy)
|
32
|
+
v = v.value
|
40
33
|
end
|
34
|
+
@value = v
|
41
35
|
end
|
42
36
|
|
43
37
|
# `SKIP` was made into a subclass of `GraphQL::Error` to improve runtime performance
|
@@ -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}`
|
Binary file
|
@@ -11,8 +11,8 @@ module GraphQL
|
|
11
11
|
"to the executor."
|
12
12
|
field :name, String, null: false, method: :graphql_name
|
13
13
|
field :description, String
|
14
|
-
field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false
|
15
|
-
field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
|
14
|
+
field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false
|
15
|
+
field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
|
16
16
|
argument :include_deprecated, Boolean, required: false, default_value: false
|
17
17
|
end
|
18
18
|
field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
7
7
|
"a name, potentially a list of arguments, and a return type."
|
8
8
|
field :name, String, null: false
|
9
9
|
field :description, String
|
10
|
-
field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
|
10
|
+
field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
|
11
11
|
argument :include_deprecated, Boolean, required: false, default_value: false
|
12
12
|
end
|
13
13
|
field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
|
@@ -8,11 +8,11 @@ module GraphQL
|
|
8
8
|
"available types and directives on the server, as well as the entry points for "\
|
9
9
|
"query, mutation, and subscription operations."
|
10
10
|
|
11
|
-
field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false
|
11
|
+
field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false, scope: false
|
12
12
|
field :query_type, GraphQL::Schema::LateBoundType.new("__Type"), "The type that query operations will be rooted at.", null: false
|
13
13
|
field :mutation_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server supports mutation, the type that mutation operations will be rooted at."
|
14
14
|
field :subscription_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server support subscription, the type that subscription operations will be rooted at."
|
15
|
-
field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false
|
15
|
+
field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false, scope: false
|
16
16
|
field :description, String, resolver_method: :schema_description
|
17
17
|
|
18
18
|
def schema_description
|