graphql 2.5.9 → 2.5.26
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/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis.rb +20 -13
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +2 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
- data/lib/graphql/dashboard.rb +11 -73
- data/lib/graphql/dataloader/active_record_association_source.rb +14 -2
- data/lib/graphql/dataloader/async_dataloader.rb +22 -11
- data/lib/graphql/dataloader/null_dataloader.rb +54 -9
- data/lib/graphql/dataloader.rb +75 -23
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/field_resolve_step.rb +631 -0
- data/lib/graphql/execution/finalize.rb +217 -0
- data/lib/graphql/execution/input_values.rb +261 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +28 -33
- data/lib/graphql/execution/interpreter.rb +8 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +64 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next.rb +90 -0
- data/lib/graphql/execution/prepare_object_step.rb +128 -0
- data/lib/graphql/execution/runner.rb +410 -0
- data/lib/graphql/execution/selections_step.rb +91 -0
- data/lib/graphql/execution.rb +8 -4
- data/lib/graphql/execution_error.rb +17 -10
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +20 -9
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +1 -0
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +11 -4
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query.rb +10 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/argument.rb +17 -0
- data/lib/graphql/schema/build_from_definition.rb +15 -2
- data/lib/graphql/schema/directive.rb +45 -13
- data/lib/graphql/schema/field/connection_extension.rb +4 -37
- data/lib/graphql/schema/field/scope_extension.rb +18 -13
- data/lib/graphql/schema/field.rb +87 -48
- data/lib/graphql/schema/field_extension.rb +11 -8
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
- data/lib/graphql/schema/member/has_arguments.rb +43 -14
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +37 -0
- data/lib/graphql/schema/member/has_fields.rb +86 -5
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +60 -1
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/validator/required_validator.rb +45 -5
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +140 -56
- data/lib/graphql/schema/visibility.rb +31 -18
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +108 -32
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +36 -1
- data/lib/graphql/testing/helpers.rb +12 -9
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace.rb +70 -7
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +209 -79
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +7 -3
- metadata +21 -3
|
@@ -35,11 +35,10 @@ module GraphQL
|
|
|
35
35
|
# @return [GraphQL::Query::Context]
|
|
36
36
|
attr_reader :context
|
|
37
37
|
|
|
38
|
-
def initialize(query
|
|
38
|
+
def initialize(query:)
|
|
39
39
|
@query = query
|
|
40
40
|
@current_trace = query.current_trace
|
|
41
41
|
@dataloader = query.multiplex.dataloader
|
|
42
|
-
@lazies_at_depth = lazies_at_depth
|
|
43
42
|
@schema = query.schema
|
|
44
43
|
@context = query.context
|
|
45
44
|
@response = nil
|
|
@@ -298,8 +297,6 @@ module GraphQL
|
|
|
298
297
|
end
|
|
299
298
|
|
|
300
299
|
call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
|
|
301
|
-
finished_jobs = 0
|
|
302
|
-
enqueued_jobs = gathered_selections.size
|
|
303
300
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
|
304
301
|
# Field resolution may pause the fiber,
|
|
305
302
|
# so it wouldn't get to the `Resolve` call that happens below.
|
|
@@ -310,12 +307,6 @@ module GraphQL
|
|
|
310
307
|
evaluate_selection(
|
|
311
308
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
312
309
|
)
|
|
313
|
-
finished_jobs += 1
|
|
314
|
-
if finished_jobs == enqueued_jobs
|
|
315
|
-
if target_result
|
|
316
|
-
selections_result.merge_into(target_result)
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
310
|
@dataloader.clear_cache
|
|
320
311
|
}
|
|
321
312
|
else
|
|
@@ -323,15 +314,12 @@ module GraphQL
|
|
|
323
314
|
evaluate_selection(
|
|
324
315
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
325
316
|
)
|
|
326
|
-
finished_jobs += 1
|
|
327
|
-
if finished_jobs == enqueued_jobs
|
|
328
|
-
if target_result
|
|
329
|
-
selections_result.merge_into(target_result)
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
317
|
}
|
|
333
318
|
end
|
|
334
319
|
end
|
|
320
|
+
if target_result
|
|
321
|
+
selections_result.merge_into(target_result)
|
|
322
|
+
end
|
|
335
323
|
selections_result
|
|
336
324
|
end
|
|
337
325
|
end
|
|
@@ -376,6 +364,10 @@ module GraphQL
|
|
|
376
364
|
else
|
|
377
365
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
|
378
366
|
runtime_state = get_current_runtime_state # This might be in a different fiber
|
|
367
|
+
runtime_state.current_field = field_defn
|
|
368
|
+
runtime_state.current_arguments = resolved_arguments
|
|
369
|
+
runtime_state.current_result_name = result_name
|
|
370
|
+
runtime_state.current_result = selections_result
|
|
379
371
|
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
|
|
380
372
|
end
|
|
381
373
|
end
|
|
@@ -384,6 +376,8 @@ module GraphQL
|
|
|
384
376
|
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
385
377
|
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
|
|
386
378
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
|
379
|
+
next if selection_result.collect_result(result_name, resolved_arguments)
|
|
380
|
+
|
|
387
381
|
return_type_non_null = field_defn.type.non_null?
|
|
388
382
|
continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
389
383
|
next
|
|
@@ -457,7 +451,7 @@ module GraphQL
|
|
|
457
451
|
}
|
|
458
452
|
end
|
|
459
453
|
|
|
460
|
-
|
|
454
|
+
call_method_on_directives(:resolve, object, directives) do
|
|
461
455
|
if !directives.empty?
|
|
462
456
|
# This might be executed in a different context; reset this info
|
|
463
457
|
runtime_state = get_current_runtime_state
|
|
@@ -483,6 +477,8 @@ module GraphQL
|
|
|
483
477
|
end
|
|
484
478
|
@current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
|
|
485
479
|
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
|
480
|
+
next if selection_result.collect_result(result_name, inner_result)
|
|
481
|
+
|
|
486
482
|
owner_type = selection_result.graphql_result_type
|
|
487
483
|
return_type = field_defn.type
|
|
488
484
|
continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
|
@@ -499,7 +495,7 @@ module GraphQL
|
|
|
499
495
|
# all of its child fields before moving on to the next root mutation field.
|
|
500
496
|
# (Subselections of this mutation will still be resolved level-by-level.)
|
|
501
497
|
if selection_result.graphql_is_eager
|
|
502
|
-
|
|
498
|
+
@dataloader.run
|
|
503
499
|
end
|
|
504
500
|
end
|
|
505
501
|
|
|
@@ -557,7 +553,7 @@ module GraphQL
|
|
|
557
553
|
path
|
|
558
554
|
end
|
|
559
555
|
|
|
560
|
-
HALT = Object.new
|
|
556
|
+
HALT = Object.new.freeze
|
|
561
557
|
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
|
562
558
|
case value
|
|
563
559
|
when nil
|
|
@@ -607,7 +603,7 @@ module GraphQL
|
|
|
607
603
|
err
|
|
608
604
|
end
|
|
609
605
|
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
610
|
-
elsif GraphQL::Execution::
|
|
606
|
+
elsif value.is_a?(GraphQL::Execution::Skip)
|
|
611
607
|
# It's possible a lazy was already written here
|
|
612
608
|
case selection_result
|
|
613
609
|
when GraphQLResultHash
|
|
@@ -678,7 +674,11 @@ module GraphQL
|
|
|
678
674
|
rescue GraphQL::ExecutionError => ex_err
|
|
679
675
|
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
680
676
|
rescue StandardError => err
|
|
681
|
-
|
|
677
|
+
begin
|
|
678
|
+
query.handle_or_reraise(err)
|
|
679
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
680
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
681
|
+
end
|
|
682
682
|
end
|
|
683
683
|
set_result(selection_result, result_name, r, false, is_non_null)
|
|
684
684
|
r
|
|
@@ -761,7 +761,7 @@ module GraphQL
|
|
|
761
761
|
idx += 1
|
|
762
762
|
if use_dataloader_job
|
|
763
763
|
@dataloader.append_job do
|
|
764
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped,
|
|
764
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, nil)
|
|
765
765
|
end
|
|
766
766
|
else
|
|
767
767
|
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
|
@@ -803,6 +803,7 @@ module GraphQL
|
|
|
803
803
|
end
|
|
804
804
|
|
|
805
805
|
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
806
|
+
runtime_state ||= get_current_runtime_state
|
|
806
807
|
runtime_state.current_result_name = this_idx
|
|
807
808
|
runtime_state.current_result = response_list
|
|
808
809
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
@@ -890,7 +891,6 @@ module GraphQL
|
|
|
890
891
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
|
891
892
|
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
|
892
893
|
if lazy?(lazy_obj)
|
|
893
|
-
orig_result = result
|
|
894
894
|
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
|
895
895
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
|
896
896
|
# This block might be called in a new fiber;
|
|
@@ -900,13 +900,13 @@ module GraphQL
|
|
|
900
900
|
runtime_state.current_field = field
|
|
901
901
|
runtime_state.current_arguments = arguments
|
|
902
902
|
runtime_state.current_result_name = result_name
|
|
903
|
-
runtime_state.current_result =
|
|
903
|
+
runtime_state.current_result = result
|
|
904
904
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
905
905
|
# Wrap the execution of _this_ method with tracing,
|
|
906
906
|
# but don't wrap the continuation below
|
|
907
|
-
|
|
907
|
+
sync_result = nil
|
|
908
908
|
inner_obj = begin
|
|
909
|
-
|
|
909
|
+
sync_result = if trace
|
|
910
910
|
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
|
911
911
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
|
912
912
|
schema.sync_lazy(lazy_obj)
|
|
@@ -924,7 +924,7 @@ module GraphQL
|
|
|
924
924
|
end
|
|
925
925
|
ensure
|
|
926
926
|
if trace
|
|
927
|
-
@current_trace.end_execute_field(field, owner_object, arguments, query,
|
|
927
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, sync_result)
|
|
928
928
|
end
|
|
929
929
|
end
|
|
930
930
|
yield(inner_obj, runtime_state)
|
|
@@ -934,12 +934,7 @@ module GraphQL
|
|
|
934
934
|
lazy.value
|
|
935
935
|
else
|
|
936
936
|
set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
|
|
937
|
-
|
|
938
|
-
while result
|
|
939
|
-
current_depth += 1
|
|
940
|
-
result = result.graphql_parent
|
|
941
|
-
end
|
|
942
|
-
@lazies_at_depth[current_depth] << lazy
|
|
937
|
+
@dataloader.lazy_at_depth(result.depth, lazy)
|
|
943
938
|
lazy
|
|
944
939
|
end
|
|
945
940
|
else
|
|
@@ -42,7 +42,6 @@ module GraphQL
|
|
|
42
42
|
trace.execute_multiplex(multiplex: multiplex) do
|
|
43
43
|
schema = multiplex.schema
|
|
44
44
|
queries = multiplex.queries
|
|
45
|
-
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
46
45
|
multiplex_analyzers = schema.multiplex_analyzers
|
|
47
46
|
if multiplex.max_complexity
|
|
48
47
|
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
|
@@ -59,11 +58,6 @@ module GraphQL
|
|
|
59
58
|
# Do as much eager evaluation of the query as possible
|
|
60
59
|
results = []
|
|
61
60
|
queries.each_with_index do |query, idx|
|
|
62
|
-
if query.subscription? && !query.subscription_update?
|
|
63
|
-
subs_namespace = query.context.namespace(:subscriptions)
|
|
64
|
-
subs_namespace[:events] = []
|
|
65
|
-
subs_namespace[:subscriptions] = {}
|
|
66
|
-
end
|
|
67
61
|
multiplex.dataloader.append_job {
|
|
68
62
|
operation = query.selected_operation
|
|
69
63
|
result = if operation.nil? || !query.valid? || !query.context.errors.empty?
|
|
@@ -73,38 +67,27 @@ module GraphQL
|
|
|
73
67
|
# Although queries in a multiplex _share_ an Interpreter instance,
|
|
74
68
|
# they also have another item of state, which is private to that query
|
|
75
69
|
# in particular, assign it here:
|
|
76
|
-
runtime = Runtime.new(query: query
|
|
70
|
+
runtime = Runtime.new(query: query)
|
|
77
71
|
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
|
78
|
-
|
|
72
|
+
if query.subscription? && !query.subscription_update?
|
|
73
|
+
schema.subscriptions.initialize_subscriptions(query)
|
|
74
|
+
end
|
|
79
75
|
query.current_trace.execute_query(query: query) do
|
|
80
76
|
runtime.run_eager
|
|
81
77
|
end
|
|
82
78
|
rescue GraphQL::ExecutionError => err
|
|
83
79
|
query.context.errors << err
|
|
84
|
-
NO_OPERATION
|
|
85
80
|
end
|
|
86
81
|
end
|
|
87
82
|
results[idx] = result
|
|
88
83
|
}
|
|
89
84
|
end
|
|
90
85
|
|
|
91
|
-
multiplex.dataloader.run
|
|
92
|
-
|
|
93
|
-
# Then, work through lazy results in a breadth-first way
|
|
94
|
-
multiplex.dataloader.append_job {
|
|
95
|
-
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
96
|
-
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
|
97
|
-
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
|
98
|
-
end
|
|
99
|
-
}
|
|
100
|
-
multiplex.dataloader.run
|
|
86
|
+
multiplex.dataloader.run(trace_query_lazy: multiplex)
|
|
101
87
|
|
|
102
88
|
# Then, find all errors and assign the result to the query object
|
|
103
89
|
results.each_with_index do |data_result, idx|
|
|
104
90
|
query = queries[idx]
|
|
105
|
-
if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
|
|
106
|
-
schema.subscriptions.write_subscription(query, events)
|
|
107
|
-
end
|
|
108
91
|
# Assign the result so that it can be accessed in instrumentation
|
|
109
92
|
query.result_values = if data_result.equal?(NO_OPERATION)
|
|
110
93
|
if !query.valid? || !query.context.errors.empty?
|
|
@@ -114,6 +97,9 @@ module GraphQL
|
|
|
114
97
|
data_result
|
|
115
98
|
end
|
|
116
99
|
else
|
|
100
|
+
if query.subscription?
|
|
101
|
+
schema.subscriptions.finish_subscriptions(query)
|
|
102
|
+
end
|
|
117
103
|
result = {}
|
|
118
104
|
|
|
119
105
|
if !query.context.errors.empty?
|
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
|
38
38
|
# (fewer clauses in a hot `case` block), but now it requires special handling here.
|
|
39
39
|
# I think it's still worth it for the performance win, but if the number of special
|
|
40
40
|
# cases grows, then maybe it's worth rethinking somehow.
|
|
41
|
-
if @value.is_a?(StandardError) &&
|
|
41
|
+
if @value.is_a?(StandardError) && !@value.is_a?(GraphQL::Execution::Skip)
|
|
42
42
|
raise @value
|
|
43
43
|
else
|
|
44
44
|
@value
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module Execution
|
|
4
|
+
class LoadArgumentStep
|
|
5
|
+
def initialize(field_resolve_step:, arguments:, load_receiver:, argument_value:, argument_definition:, argument_key:)
|
|
6
|
+
@field_resolve_step = field_resolve_step
|
|
7
|
+
@load_receiver = load_receiver
|
|
8
|
+
@arguments = arguments
|
|
9
|
+
@argument_value = argument_value
|
|
10
|
+
@argument_definition = argument_definition
|
|
11
|
+
@argument_key = argument_key
|
|
12
|
+
@loaded_value = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def value
|
|
16
|
+
@loaded_value = @field_resolve_step.sync(@loaded_value)
|
|
17
|
+
assign_value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
context = @field_resolve_step.selections_step.query.context
|
|
22
|
+
@loaded_value = begin
|
|
23
|
+
@load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
|
|
24
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
25
|
+
context.schema.unauthorized_object(auth_err)
|
|
26
|
+
end
|
|
27
|
+
if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
|
|
28
|
+
runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
29
|
+
else
|
|
30
|
+
assign_value
|
|
31
|
+
end
|
|
32
|
+
rescue GraphQL::RuntimeError => err
|
|
33
|
+
@loaded_value = err
|
|
34
|
+
assign_value
|
|
35
|
+
rescue StandardError => stderr
|
|
36
|
+
@loaded_value = begin
|
|
37
|
+
context.query.handle_or_reraise(stderr)
|
|
38
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
39
|
+
ex_err
|
|
40
|
+
end
|
|
41
|
+
assign_value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def assign_value
|
|
47
|
+
if @loaded_value.is_a?(GraphQL::RuntimeError)
|
|
48
|
+
@loaded_value.path = @field_resolve_step.path
|
|
49
|
+
@field_resolve_step.arguments = @loaded_value
|
|
50
|
+
else
|
|
51
|
+
query = @field_resolve_step.selections_step.query
|
|
52
|
+
query.current_trace.object_loaded(@argument_definition, @loaded_value, query.context)
|
|
53
|
+
@arguments[@argument_key] = @loaded_value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
field_pending_steps = @field_resolve_step.pending_steps
|
|
57
|
+
field_pending_steps.delete(self)
|
|
58
|
+
if @field_resolve_step.arguments && field_pending_steps.size == 0 # rubocop:disable Development/ContextIsPassedCop
|
|
59
|
+
@field_resolve_step.runner.add_step(@field_resolve_step)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -33,7 +33,7 @@ module GraphQL
|
|
|
33
33
|
@queries.each { |q| q.multiplex = self }
|
|
34
34
|
@context = context
|
|
35
35
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
|
36
|
-
@tracers = schema.tracers + (context[:tracers] ||
|
|
36
|
+
@tracers = schema.tracers + (context[:tracers] || EmptyObjects::EMPTY_ARRAY)
|
|
37
37
|
@max_complexity = max_complexity
|
|
38
38
|
@current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
|
|
39
39
|
@logger = nil
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "graphql/execution/prepare_object_step"
|
|
3
|
+
require "graphql/execution/input_values"
|
|
4
|
+
require "graphql/execution/field_resolve_step"
|
|
5
|
+
require "graphql/execution/finalize"
|
|
6
|
+
require "graphql/execution/load_argument_step"
|
|
7
|
+
require "graphql/execution/runner"
|
|
8
|
+
require "graphql/execution/selections_step"
|
|
9
|
+
module GraphQL
|
|
10
|
+
module Execution
|
|
11
|
+
module Finalizer
|
|
12
|
+
attr_accessor :path
|
|
13
|
+
def finalize_graphql_result(query, result_data, result_key)
|
|
14
|
+
raise RequiredImplementationMissingError
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module HaltExecution
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module PostProcessor
|
|
22
|
+
def after_resolve(field_results)
|
|
23
|
+
raise RequiredImplementationMissingError, "#{self.class}#after_resolve should handle `field_results` and return a new value to use"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module Next
|
|
28
|
+
module SchemaExtension
|
|
29
|
+
def execute_next(query_str = nil, context: nil, document: nil, operation_name: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
|
|
30
|
+
multiplex_context = if context
|
|
31
|
+
{
|
|
32
|
+
backtrace: context[:backtrace],
|
|
33
|
+
tracers: context[:tracers],
|
|
34
|
+
trace: context[:trace],
|
|
35
|
+
dataloader: context[:dataloader],
|
|
36
|
+
trace_mode: context[:trace_mode],
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
{}
|
|
40
|
+
end
|
|
41
|
+
query_opts = {
|
|
42
|
+
query: query_str,
|
|
43
|
+
document: document,
|
|
44
|
+
context: context,
|
|
45
|
+
validate: validate,
|
|
46
|
+
variables: variables,
|
|
47
|
+
root_value: root_value,
|
|
48
|
+
operation_name: operation_name,
|
|
49
|
+
visibility_profile: visibility_profile,
|
|
50
|
+
}
|
|
51
|
+
m_results = multiplex_next([query_opts], context: multiplex_context, max_complexity: nil)
|
|
52
|
+
m_results[0]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def multiplex_next(query_options, context: {}, max_complexity: self.max_complexity)
|
|
56
|
+
Next.run_all(self, query_options, context: context, max_complexity: max_complexity)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def execution_next_options
|
|
60
|
+
@execution_next_options || find_inherited_value(:execution_next_options, EmptyObjects::EMPTY_HASH)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
attr_writer :execution_next_options
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.use(schema, authorization: true)
|
|
67
|
+
schema.extend(SchemaExtension)
|
|
68
|
+
schema.execution_next_options = { authorization: authorization }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
72
|
+
queries = query_options.map do |opts|
|
|
73
|
+
query = case opts
|
|
74
|
+
when Hash
|
|
75
|
+
schema.query_class.new(schema, nil, **opts)
|
|
76
|
+
when GraphQL::Query, GraphQL::Query::Partial
|
|
77
|
+
opts
|
|
78
|
+
else
|
|
79
|
+
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
|
|
80
|
+
end
|
|
81
|
+
query.context[:__graphql_execute_next] = true
|
|
82
|
+
query
|
|
83
|
+
end
|
|
84
|
+
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
85
|
+
runner = Runner.new(multiplex, **schema.execution_next_options)
|
|
86
|
+
runner.execute
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module Execution
|
|
4
|
+
class PrepareObjectStep
|
|
5
|
+
def initialize(object:, runner:, graphql_result:, key:, is_non_null:, field_resolve_step:, next_objects:, next_results:, is_from_array:)
|
|
6
|
+
@object = object
|
|
7
|
+
@runner = runner
|
|
8
|
+
@field_resolve_step = field_resolve_step
|
|
9
|
+
@is_non_null = is_non_null
|
|
10
|
+
@next_objects = next_objects
|
|
11
|
+
@next_results = next_results
|
|
12
|
+
@graphql_result = graphql_result
|
|
13
|
+
@resolved_type = nil
|
|
14
|
+
@authorized_value = nil
|
|
15
|
+
@authorization_error = nil
|
|
16
|
+
@key = key
|
|
17
|
+
@next_step = :resolve_type
|
|
18
|
+
@is_from_array = is_from_array
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def value
|
|
22
|
+
if @authorized_value
|
|
23
|
+
query = @field_resolve_step.selections_step.query
|
|
24
|
+
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
25
|
+
@authorized_value = @field_resolve_step.sync(@authorized_value)
|
|
26
|
+
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
27
|
+
elsif @resolved_type
|
|
28
|
+
ctx = @field_resolve_step.selections_step.query.context
|
|
29
|
+
st = @field_resolve_step.static_type
|
|
30
|
+
ctx.query.current_trace.begin_resolve_type(st, @object, ctx)
|
|
31
|
+
@resolved_type, _ignored_value = @field_resolve_step.sync(@resolved_type)
|
|
32
|
+
ctx.query.current_trace.end_resolve_type(st, @object, ctx, @resolved_type)
|
|
33
|
+
end
|
|
34
|
+
@runner.add_step(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call
|
|
38
|
+
case @next_step
|
|
39
|
+
when :resolve_type
|
|
40
|
+
static_type = @field_resolve_step.static_type
|
|
41
|
+
if static_type.kind.abstract?
|
|
42
|
+
ctx = @field_resolve_step.selections_step.query.context
|
|
43
|
+
ctx.query.current_trace.begin_resolve_type(static_type, @object, ctx)
|
|
44
|
+
@resolved_type, _ignored_value = @runner.schema.resolve_type(static_type, @object, ctx)
|
|
45
|
+
ctx.query.current_trace.end_resolve_type(static_type, @object, ctx, @resolved_type)
|
|
46
|
+
else
|
|
47
|
+
@resolved_type = static_type
|
|
48
|
+
end
|
|
49
|
+
if @runner.resolves_lazies && @runner.lazy?(@resolved_type)
|
|
50
|
+
@next_step = :authorize
|
|
51
|
+
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
52
|
+
else
|
|
53
|
+
authorize
|
|
54
|
+
end
|
|
55
|
+
when :authorize
|
|
56
|
+
authorize
|
|
57
|
+
when :create_result
|
|
58
|
+
create_result
|
|
59
|
+
else
|
|
60
|
+
raise ArgumentError, "This is a bug, unknown step: #{@next_step.inspect}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def authorize
|
|
65
|
+
if @field_resolve_step.was_scoped && !@resolved_type.reauthorize_scoped_objects
|
|
66
|
+
@authorized_value = @object
|
|
67
|
+
create_result
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
query = @field_resolve_step.selections_step.query
|
|
72
|
+
begin
|
|
73
|
+
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
74
|
+
@authorized_value = @resolved_type.authorized?(@object, query.context)
|
|
75
|
+
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
76
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
77
|
+
@authorization_error = auth_err
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if @runner.resolves_lazies && @runner.lazy?(@authorized_value)
|
|
81
|
+
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
82
|
+
@next_step = :create_result
|
|
83
|
+
else
|
|
84
|
+
create_result
|
|
85
|
+
end
|
|
86
|
+
rescue GraphQL::RuntimeError => err
|
|
87
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def create_result
|
|
91
|
+
if !@authorized_value
|
|
92
|
+
@authorization_error ||= GraphQL::UnauthorizedError.new(object: @object, type: @resolved_type, context: @field_resolve_step.selections_step.query.context)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if @authorization_error
|
|
96
|
+
begin
|
|
97
|
+
new_obj = @runner.schema.unauthorized_object(@authorization_error)
|
|
98
|
+
if new_obj
|
|
99
|
+
@authorized_value = true
|
|
100
|
+
@object = new_obj
|
|
101
|
+
elsif @is_non_null
|
|
102
|
+
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
103
|
+
else
|
|
104
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(@authorization_error)
|
|
105
|
+
end
|
|
106
|
+
rescue GraphQL::RuntimeError => err
|
|
107
|
+
if @is_non_null
|
|
108
|
+
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
109
|
+
else
|
|
110
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if @authorized_value
|
|
116
|
+
next_result_h = {}
|
|
117
|
+
@next_results << next_result_h
|
|
118
|
+
@next_objects << @object
|
|
119
|
+
@graphql_result[@key] = next_result_h
|
|
120
|
+
@runner.runtime_type_at[next_result_h] = @resolved_type
|
|
121
|
+
@runner.static_type_at[next_result_h] = @field_resolve_step.static_type
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
@field_resolve_step.authorized_finished(self)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|