graphql 2.5.23 → 2.6.3
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/graphql/analysis/query_complexity.rb +29 -13
- data/lib/graphql/analysis.rb +20 -13
- data/lib/graphql/backtrace/table.rb +10 -1
- data/lib/graphql/current.rb +7 -1
- data/lib/graphql/dataloader.rb +1 -1
- data/lib/graphql/execution/directive_checks.rb +2 -0
- data/lib/graphql/execution/field_resolve_step.rb +744 -0
- data/lib/graphql/execution/finalize.rb +230 -0
- data/lib/graphql/execution/input_values.rb +333 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/runtime.rb +36 -15
- data/lib/graphql/execution/load_argument_step.rb +102 -0
- data/lib/graphql/execution/next.rb +42 -16
- data/lib/graphql/execution/prepare_object_step.rb +147 -0
- data/lib/graphql/execution/resolve_type_step.rb +27 -0
- data/lib/graphql/execution/runner.rb +445 -0
- data/lib/graphql/execution/selections_step.rb +91 -0
- data/lib/graphql/execution.rb +10 -3
- data/lib/graphql/execution_error.rb +7 -13
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +6 -2
- data/lib/graphql/language/lexer.rb +12 -8
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language.rb +8 -2
- data/lib/graphql/pagination/connections.rb +1 -3
- data/lib/graphql/query/context.rb +6 -0
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query.rb +12 -3
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/argument.rb +3 -3
- data/lib/graphql/schema/build_from_definition.rb +10 -0
- data/lib/graphql/schema/directive/feature.rb +4 -0
- data/lib/graphql/schema/directive/transform.rb +20 -0
- data/lib/graphql/schema/directive.rb +23 -9
- data/lib/graphql/schema/field/connection_extension.rb +2 -15
- data/lib/graphql/schema/field/scope_extension.rb +0 -4
- data/lib/graphql/schema/field.rb +20 -20
- data/lib/graphql/schema/field_extension.rb +11 -41
- data/lib/graphql/schema/has_single_input_argument.rb +24 -13
- data/lib/graphql/schema/input_object.rb +4 -0
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/introspection_system.rb +6 -21
- data/lib/graphql/schema/list.rb +4 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/ractor_shareable.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
- data/lib/graphql/schema/resolver.rb +30 -14
- data/lib/graphql/schema/subscription.rb +53 -8
- data/lib/graphql/schema/timeout.rb +2 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
- data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
- data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/format_validator.rb +3 -3
- data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/length_validator.rb +6 -6
- data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
- data/lib/graphql/schema/validator/required_validator.rb +6 -4
- data/lib/graphql/schema/validator.rb +9 -0
- data/lib/graphql/schema/visibility/profile.rb +6 -4
- data/lib/graphql/schema/visibility/visit.rb +1 -1
- data/lib/graphql/schema/visibility.rb +30 -22
- data/lib/graphql/schema.rb +43 -20
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +31 -25
- data/lib/graphql/subscriptions/event.rb +0 -1
- data/lib/graphql/subscriptions.rb +15 -0
- data/lib/graphql/tracing/perfetto_trace.rb +5 -3
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -3
- metadata +11 -7
- data/lib/graphql/execution/next/field_resolve_step.rb +0 -743
- data/lib/graphql/execution/next/load_argument_step.rb +0 -64
- data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
- data/lib/graphql/execution/next/runner.rb +0 -411
- data/lib/graphql/execution/next/selections_step.rb +0 -37
|
@@ -198,7 +198,7 @@ module GraphQL
|
|
|
198
198
|
|
|
199
199
|
def each_gathered_selections(response_hash)
|
|
200
200
|
ordered_result_keys = []
|
|
201
|
-
gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
|
|
201
|
+
gathered_selections = gather_selections(response_hash, response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
|
|
202
202
|
ordered_result_keys.uniq!
|
|
203
203
|
if gathered_selections.is_a?(Array)
|
|
204
204
|
gathered_selections.each do |item|
|
|
@@ -209,15 +209,13 @@ module GraphQL
|
|
|
209
209
|
end
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
|
|
212
|
+
def gather_selections(graphql_response, owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
|
|
213
213
|
selections.each do |node|
|
|
214
|
-
# Skip gathering this if the directive says so
|
|
215
|
-
if !directives_include?(node, owner_object, owner_type)
|
|
216
|
-
next
|
|
217
|
-
end
|
|
218
|
-
|
|
219
214
|
if node.is_a?(GraphQL::Language::Nodes::Field)
|
|
220
215
|
response_key = node.alias || node.name
|
|
216
|
+
if !directives_include?(node, owner_object, owner_type, graphql_response, response_key)
|
|
217
|
+
next
|
|
218
|
+
end
|
|
221
219
|
ordered_result_keys << response_key
|
|
222
220
|
selections = selections_by_name[response_key]
|
|
223
221
|
# if there was already a selection of this field,
|
|
@@ -234,6 +232,9 @@ module GraphQL
|
|
|
234
232
|
selections_by_name[response_key] = node
|
|
235
233
|
end
|
|
236
234
|
else
|
|
235
|
+
if !directives_include?(node, owner_object, owner_type, graphql_response, nil)
|
|
236
|
+
next
|
|
237
|
+
end
|
|
237
238
|
# This is an InlineFragment or a FragmentSpread
|
|
238
239
|
if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
|
239
240
|
next_selections = {}
|
|
@@ -255,14 +256,14 @@ module GraphQL
|
|
|
255
256
|
type_defn = query.types.type(node.type.name)
|
|
256
257
|
|
|
257
258
|
if query.types.possible_types(type_defn).include?(owner_type)
|
|
258
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
259
|
+
result = gather_selections(graphql_response, owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
259
260
|
if !result.equal?(next_selections)
|
|
260
261
|
selections_to_run = result
|
|
261
262
|
end
|
|
262
263
|
end
|
|
263
264
|
else
|
|
264
265
|
# it's an untyped fragment, definitely continue
|
|
265
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
266
|
+
result = gather_selections(graphql_response, owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
266
267
|
if !result.equal?(next_selections)
|
|
267
268
|
selections_to_run = result
|
|
268
269
|
end
|
|
@@ -271,7 +272,7 @@ module GraphQL
|
|
|
271
272
|
fragment_def = query.fragments[node.name]
|
|
272
273
|
type_defn = query.types.type(fragment_def.type.name)
|
|
273
274
|
if query.types.possible_types(type_defn).include?(owner_type)
|
|
274
|
-
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
275
|
+
result = gather_selections(graphql_response, owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
|
|
275
276
|
if !result.equal?(next_selections)
|
|
276
277
|
selections_to_run = result
|
|
277
278
|
end
|
|
@@ -339,7 +340,7 @@ module GraphQL
|
|
|
339
340
|
end
|
|
340
341
|
field_name = ast_node.name
|
|
341
342
|
owner_type = selections_result.graphql_result_type
|
|
342
|
-
field_defn = query.types.field(owner_type, field_name)
|
|
343
|
+
field_defn = query.types.field(owner_type, field_name) || raise(GraphQL::Error, "No field definition found for #{owner_type.graphql_name}.#{field_name} (at #{ast_node.position})")
|
|
343
344
|
|
|
344
345
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
|
345
346
|
runtime_state = get_current_runtime_state
|
|
@@ -579,7 +580,7 @@ module GraphQL
|
|
|
579
580
|
value.path ||= current_path
|
|
580
581
|
value.ast_node ||= ast_node
|
|
581
582
|
context.errors << value
|
|
582
|
-
if selection_result
|
|
583
|
+
if selection_result && result_name
|
|
583
584
|
set_result(selection_result, result_name, nil, false, is_non_null)
|
|
584
585
|
end
|
|
585
586
|
end
|
|
@@ -856,11 +857,31 @@ module GraphQL
|
|
|
856
857
|
end
|
|
857
858
|
|
|
858
859
|
# Check {Schema::Directive.include?} for each directive that's present
|
|
859
|
-
def directives_include?(node, graphql_object, parent_type)
|
|
860
|
+
def directives_include?(node, graphql_object, parent_type, selection_result, extra_path_part)
|
|
860
861
|
node.directives.each do |dir_node|
|
|
861
862
|
dir_defn = @schema_directives.fetch(dir_node.name)
|
|
862
|
-
|
|
863
|
-
if !
|
|
863
|
+
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
|
864
|
+
if !raw_dir_args.is_a?(GraphQL::ExecutionError)
|
|
865
|
+
begin
|
|
866
|
+
dir_defn.validate!(raw_dir_args, context)
|
|
867
|
+
rescue GraphQL::ExecutionError => err
|
|
868
|
+
raw_dir_args = err
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
if extra_path_part && raw_dir_args.is_a?(GraphQL::ExecutionError)
|
|
873
|
+
raw_dir_args.path = current_path + [extra_path_part]
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
dir_args = continue_value(
|
|
877
|
+
raw_dir_args, # value
|
|
878
|
+
nil, # field
|
|
879
|
+
false, # is_non_null
|
|
880
|
+
dir_node, # ast_node
|
|
881
|
+
nil, # result_name
|
|
882
|
+
selection_result
|
|
883
|
+
)
|
|
884
|
+
if dir_args == HALT || !dir_defn.include?(graphql_object, dir_args, context)
|
|
864
885
|
return false
|
|
865
886
|
end
|
|
866
887
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
@is_authorized = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def value
|
|
17
|
+
@field_resolve_step.set_current_field
|
|
18
|
+
schema = @field_resolve_step.runner.schema
|
|
19
|
+
@loaded_value = schema.sync_lazy(@loaded_value)
|
|
20
|
+
assign_value
|
|
21
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
22
|
+
@is_authorized = false
|
|
23
|
+
schema.unauthorized_object(auth_err)
|
|
24
|
+
rescue GraphQL::RuntimeError => err
|
|
25
|
+
@loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
|
|
26
|
+
err.unsubscribed_result
|
|
27
|
+
else
|
|
28
|
+
err
|
|
29
|
+
end
|
|
30
|
+
assign_value
|
|
31
|
+
rescue StandardError => stderr
|
|
32
|
+
begin
|
|
33
|
+
@field_resolve_step.selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
34
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
35
|
+
@loaded_value = ex_err
|
|
36
|
+
end
|
|
37
|
+
assign_value
|
|
38
|
+
ensure
|
|
39
|
+
@field_resolve_step.set_current_field(nil)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def call
|
|
43
|
+
@field_resolve_step.set_current_field
|
|
44
|
+
context = @field_resolve_step.selections_step.query.context
|
|
45
|
+
@loaded_value = begin
|
|
46
|
+
@load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
|
|
47
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
48
|
+
@is_authorized = false
|
|
49
|
+
context.schema.unauthorized_object(auth_err)
|
|
50
|
+
end
|
|
51
|
+
if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
|
|
52
|
+
runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
53
|
+
else
|
|
54
|
+
assign_value
|
|
55
|
+
end
|
|
56
|
+
rescue GraphQL::RuntimeError => err
|
|
57
|
+
@loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
|
|
58
|
+
@is_authorized = false
|
|
59
|
+
err.unsubscribed_result
|
|
60
|
+
else
|
|
61
|
+
err
|
|
62
|
+
end
|
|
63
|
+
assign_value
|
|
64
|
+
rescue StandardError => stderr
|
|
65
|
+
@loaded_value = begin
|
|
66
|
+
context.query.handle_or_reraise(stderr, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: nil) # rubocop:disable Development/ContextIsPassedCop
|
|
67
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
68
|
+
ex_err
|
|
69
|
+
end
|
|
70
|
+
assign_value
|
|
71
|
+
ensure
|
|
72
|
+
@field_resolve_step.set_current_field(nil)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def assign_value
|
|
78
|
+
if @loaded_value.is_a?(GraphQL::RuntimeError)
|
|
79
|
+
@loaded_value.path = @field_resolve_step.path
|
|
80
|
+
@field_resolve_step.arguments = @loaded_value
|
|
81
|
+
elsif @is_authorized == false
|
|
82
|
+
# An unauthorized_object hook ate the error
|
|
83
|
+
@field_resolve_step.arguments = EmptyObjects::EMPTY_HASH
|
|
84
|
+
field_pending_steps = @field_resolve_step.pending_steps
|
|
85
|
+
field_pending_steps.clear
|
|
86
|
+
@field_resolve_step.build_errors_result(nil, nil)
|
|
87
|
+
return
|
|
88
|
+
else
|
|
89
|
+
query = @field_resolve_step.selections_step.query
|
|
90
|
+
query.current_trace.object_loaded(@argument_definition, @loaded_value, query.context)
|
|
91
|
+
@arguments[@argument_key] = @loaded_value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
field_pending_steps = @field_resolve_step.pending_steps
|
|
95
|
+
field_pending_steps.delete(self)
|
|
96
|
+
if @field_resolve_step.arguments && field_pending_steps.size == 0 # rubocop:disable Development/ContextIsPassedCop
|
|
97
|
+
@field_resolve_step.runner.add_step(@field_resolve_step)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require "graphql/execution/
|
|
3
|
-
require "graphql/execution/
|
|
4
|
-
require "graphql/execution/
|
|
5
|
-
require "graphql/execution/
|
|
6
|
-
require "graphql/execution/
|
|
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/resolve_type_step"
|
|
8
|
+
require "graphql/execution/runner"
|
|
9
|
+
require "graphql/execution/selections_step"
|
|
7
10
|
module GraphQL
|
|
8
11
|
module Execution
|
|
12
|
+
module Finalizer
|
|
13
|
+
attr_accessor :path
|
|
14
|
+
|
|
15
|
+
def finalize_graphql_result(query, result_data, result_key)
|
|
16
|
+
raise RequiredImplementationMissingError, "#{self.class} must implement #finalize_graphql_result(query, result_data, result_key)\n\nresult_data: #{result_data}\nresult_key: #{result_key.inspect}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ast_node
|
|
20
|
+
ast_nodes&.first
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ast_node=(new_node)
|
|
24
|
+
@ast_nodes = [new_node]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_accessor :ast_nodes
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module HaltExecution
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module PostProcessor
|
|
34
|
+
def after_resolve(field_results)
|
|
35
|
+
raise RequiredImplementationMissingError, "#{self.class}#after_resolve should handle `field_results` and return a new value to use"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
9
39
|
module Next
|
|
10
40
|
module SchemaExtension
|
|
11
|
-
def execute_next(query_str = nil, context: nil, document: nil, operation_name: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
|
|
41
|
+
def execute_next(query_str = nil, query: nil, subscription_topic: nil, context: nil, document: nil, operation_name: nil, variables: nil, warden: nil, root_value: nil, validate: true, visibility_profile: nil)
|
|
12
42
|
multiplex_context = if context
|
|
13
43
|
{
|
|
14
44
|
backtrace: context[:backtrace],
|
|
@@ -21,7 +51,8 @@ module GraphQL
|
|
|
21
51
|
{}
|
|
22
52
|
end
|
|
23
53
|
query_opts = {
|
|
24
|
-
query: query_str,
|
|
54
|
+
query: query || query_str,
|
|
55
|
+
subscription_topic: subscription_topic,
|
|
25
56
|
document: document,
|
|
26
57
|
context: context,
|
|
27
58
|
validate: validate,
|
|
@@ -29,6 +60,7 @@ module GraphQL
|
|
|
29
60
|
root_value: root_value,
|
|
30
61
|
operation_name: operation_name,
|
|
31
62
|
visibility_profile: visibility_profile,
|
|
63
|
+
warden: warden,
|
|
32
64
|
}
|
|
33
65
|
m_results = multiplex_next([query_opts], context: multiplex_context, max_complexity: nil)
|
|
34
66
|
m_results[0]
|
|
@@ -37,17 +69,11 @@ module GraphQL
|
|
|
37
69
|
def multiplex_next(query_options, context: {}, max_complexity: self.max_complexity)
|
|
38
70
|
Next.run_all(self, query_options, context: context, max_complexity: max_complexity)
|
|
39
71
|
end
|
|
40
|
-
|
|
41
|
-
def execution_next_options
|
|
42
|
-
@execution_next_options || find_inherited_value(:execution_next_options, EmptyObjects::EMPTY_HASH)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
attr_writer :execution_next_options
|
|
46
72
|
end
|
|
47
73
|
|
|
48
|
-
def self.use(schema,
|
|
74
|
+
def self.use(schema, as_default: false)
|
|
49
75
|
schema.extend(SchemaExtension)
|
|
50
|
-
schema.
|
|
76
|
+
schema.default_execution_next(as_default)
|
|
51
77
|
end
|
|
52
78
|
|
|
53
79
|
def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
@@ -64,7 +90,7 @@ module GraphQL
|
|
|
64
90
|
query
|
|
65
91
|
end
|
|
66
92
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
67
|
-
runner = Runner.new(multiplex
|
|
93
|
+
runner = Runner.new(multiplex)
|
|
68
94
|
runner.execute
|
|
69
95
|
end
|
|
70
96
|
end
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
@field_resolve_step.set_current_field
|
|
23
|
+
if @authorized_value
|
|
24
|
+
query = @field_resolve_step.selections_step.query
|
|
25
|
+
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
26
|
+
@authorized_value = @field_resolve_step.sync(@authorized_value)
|
|
27
|
+
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
28
|
+
elsif @resolved_type
|
|
29
|
+
ctx = @field_resolve_step.selections_step.query.context
|
|
30
|
+
st = @field_resolve_step.static_type
|
|
31
|
+
ctx.query.current_trace.begin_resolve_type(st, @object, ctx)
|
|
32
|
+
@resolved_type, new_value = @field_resolve_step.sync(@resolved_type)
|
|
33
|
+
ResolveTypeStep.assert_valid_resolved_type(st, @resolved_type, new_value, @field_resolve_step)
|
|
34
|
+
if new_value
|
|
35
|
+
@object = new_value
|
|
36
|
+
end
|
|
37
|
+
ctx.query.current_trace.end_resolve_type(st, @object, ctx, @resolved_type)
|
|
38
|
+
end
|
|
39
|
+
@runner.add_step(self)
|
|
40
|
+
ensure
|
|
41
|
+
@field_resolve_step.set_current_field(nil)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def call
|
|
45
|
+
@field_resolve_step.set_current_field
|
|
46
|
+
case @next_step
|
|
47
|
+
when :resolve_type
|
|
48
|
+
static_type = @field_resolve_step.static_type
|
|
49
|
+
if static_type.kind.abstract?
|
|
50
|
+
query = @field_resolve_step.selections_step.query
|
|
51
|
+
@resolved_type, new_value = ResolveTypeStep.resolve_type(static_type, @object, query)
|
|
52
|
+
if new_value
|
|
53
|
+
ResolveTypeStep.assert_valid_resolved_type(static_type, @resolved_type, new_value, @field_resolve_step)
|
|
54
|
+
@object = new_value
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
@resolved_type = static_type
|
|
58
|
+
end
|
|
59
|
+
if @runner.resolves_lazies && @runner.lazy?(@resolved_type)
|
|
60
|
+
@next_step = :authorize
|
|
61
|
+
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
62
|
+
else
|
|
63
|
+
authorize
|
|
64
|
+
end
|
|
65
|
+
when :authorize
|
|
66
|
+
authorize
|
|
67
|
+
when :create_result
|
|
68
|
+
create_result
|
|
69
|
+
else
|
|
70
|
+
raise ArgumentError, "This is a bug, unknown step: #{@next_step.inspect}"
|
|
71
|
+
end
|
|
72
|
+
ensure
|
|
73
|
+
@field_resolve_step.set_current_field(nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def authorize
|
|
77
|
+
if @field_resolve_step.was_scoped && !@resolved_type.reauthorize_scoped_objects
|
|
78
|
+
@authorized_value = @object
|
|
79
|
+
create_result
|
|
80
|
+
return
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
query = @field_resolve_step.selections_step.query
|
|
84
|
+
begin
|
|
85
|
+
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
86
|
+
@authorized_value = @resolved_type.authorized?(@object, query.context)
|
|
87
|
+
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
88
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
89
|
+
@authorization_error = auth_err
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if @runner.resolves_lazies && @runner.lazy?(@authorized_value)
|
|
93
|
+
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
94
|
+
@next_step = :create_result
|
|
95
|
+
else
|
|
96
|
+
create_result
|
|
97
|
+
end
|
|
98
|
+
rescue GraphQL::RuntimeError => err
|
|
99
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
100
|
+
rescue StandardError => err
|
|
101
|
+
query ||= @field_resolve_step.selections_step.query
|
|
102
|
+
begin
|
|
103
|
+
query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
|
|
104
|
+
rescue GraphQL::RuntimeError => err
|
|
105
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def create_result
|
|
110
|
+
if !@authorized_value
|
|
111
|
+
@authorization_error ||= GraphQL::UnauthorizedError.new(object: @object, type: @resolved_type, context: @field_resolve_step.selections_step.query.context)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if @authorization_error
|
|
115
|
+
begin
|
|
116
|
+
new_obj = @runner.schema.unauthorized_object(@authorization_error)
|
|
117
|
+
if new_obj
|
|
118
|
+
@authorized_value = true
|
|
119
|
+
@object = new_obj
|
|
120
|
+
elsif @is_non_null
|
|
121
|
+
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
122
|
+
else
|
|
123
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(@authorization_error)
|
|
124
|
+
end
|
|
125
|
+
rescue GraphQL::RuntimeError => err
|
|
126
|
+
if @is_non_null
|
|
127
|
+
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
|
|
128
|
+
else
|
|
129
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if @authorized_value
|
|
135
|
+
next_result_h = {}
|
|
136
|
+
@next_results << next_result_h
|
|
137
|
+
@next_objects << @object
|
|
138
|
+
@graphql_result[@key] = next_result_h
|
|
139
|
+
@runner.runtime_type_at[next_result_h] = @resolved_type
|
|
140
|
+
@runner.static_type_at[next_result_h] = @field_resolve_step.static_type
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@field_resolve_step.authorized_finished(self)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module Execution
|
|
4
|
+
class ResolveTypeStep
|
|
5
|
+
def self.resolve_type(type, object, query)
|
|
6
|
+
query.current_trace.begin_resolve_type(type, object, query.context)
|
|
7
|
+
resolved_type_response = query.resolve_type(type, object)
|
|
8
|
+
resolved_type = if resolved_type_response.is_a?(Array)
|
|
9
|
+
resolved_type_response.first
|
|
10
|
+
else
|
|
11
|
+
resolved_type_response
|
|
12
|
+
end
|
|
13
|
+
query.current_trace.end_resolve_type(type, object, query.context, resolved_type)
|
|
14
|
+
resolved_type_response
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.assert_valid_resolved_type(abstract_type, resolved_type, object, field_resolution_step, query: field_resolution_step.selections_step.query)
|
|
18
|
+
possible_types = query.types.possible_types(abstract_type)
|
|
19
|
+
if !possible_types.include?(resolved_type)
|
|
20
|
+
err_class = abstract_type::UnresolvedTypeError
|
|
21
|
+
type_error = err_class.new(object, field_resolution_step.field_definition, abstract_type, resolved_type, possible_types)
|
|
22
|
+
query.schema.type_error(type_error, query.context)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|