graphql 2.6.1 → 2.6.2
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/dataloader.rb +1 -1
- data/lib/graphql/execution/field_resolve_step.rb +165 -65
- data/lib/graphql/execution/finalize.rb +18 -7
- data/lib/graphql/execution/input_values.rb +110 -38
- data/lib/graphql/execution/interpreter/runtime.rb +36 -15
- data/lib/graphql/execution/load_argument_step.rb +35 -3
- data/lib/graphql/execution/next.rb +20 -12
- data/lib/graphql/execution/prepare_object_step.rb +18 -5
- data/lib/graphql/execution/resolve_type_step.rb +27 -0
- data/lib/graphql/execution/runner.rb +64 -29
- data/lib/graphql/execution/selections_step.rb +1 -1
- data/lib/graphql/execution.rb +8 -1
- data/lib/graphql/execution_error.rb +6 -12
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +6 -2
- data/lib/graphql/language/lexer.rb +1 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/pagination/connections.rb +1 -3
- data/lib/graphql/query.rb +2 -2
- data/lib/graphql/schema/argument.rb +2 -2
- data/lib/graphql/schema/directive/feature.rb +4 -0
- data/lib/graphql/schema/directive/transform.rb +20 -0
- data/lib/graphql/schema/has_single_input_argument.rb +24 -13
- data/lib/graphql/schema/input_object.rb +4 -0
- 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 +0 -7
- data/lib/graphql/schema/subscription.rb +53 -8
- data/lib/graphql/schema/timeout.rb +2 -2
- data/lib/graphql/schema/visibility/visit.rb +1 -1
- data/lib/graphql/schema.rb +30 -9
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +6 -0
- data/lib/graphql/subscriptions/event.rb +0 -1
- data/lib/graphql/tracing/perfetto_trace.rb +5 -3
- data/lib/graphql/version.rb +1 -1
- metadata +3 -2
|
@@ -38,6 +38,7 @@ module GraphQL
|
|
|
38
38
|
def argument_values(owner_defn, argument_nodes, field_resolve_step)
|
|
39
39
|
arg_defns = @query.types.arguments(owner_defn)
|
|
40
40
|
argument_values = {}
|
|
41
|
+
errors = nil
|
|
41
42
|
|
|
42
43
|
arg_defns.each do |argument_definition|
|
|
43
44
|
arg_ruby_key = argument_definition.keyword
|
|
@@ -52,11 +53,12 @@ module GraphQL
|
|
|
52
53
|
arg_value = value_from_ast(arg_node.value, argument_definition.type)
|
|
53
54
|
argument_value(argument_values, arg_ruby_key, argument_definition, arg_value, nil, field_resolve_step)
|
|
54
55
|
end
|
|
56
|
+
rescue GraphQL::RuntimeError => exec_err
|
|
57
|
+
errors ||= []
|
|
58
|
+
errors << exec_err
|
|
55
59
|
end
|
|
56
60
|
|
|
57
|
-
argument_values
|
|
58
|
-
rescue GraphQL::ExecutionError => exec_err
|
|
59
|
-
exec_err
|
|
61
|
+
return argument_values, errors
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
private
|
|
@@ -82,20 +84,43 @@ module GraphQL
|
|
|
82
84
|
elsif type.kind.input_object?
|
|
83
85
|
coerced_obj = {}
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
if value.is_a?(Hash)
|
|
88
|
+
@query.types.arguments(type).each do |arg|
|
|
89
|
+
arg_key = arg.keyword
|
|
90
|
+
if value.key?(arg.graphql_name)
|
|
91
|
+
arg_value = value[arg.graphql_name]
|
|
92
|
+
elsif value.key?(sym_name = arg.graphql_name.to_sym)
|
|
93
|
+
arg_value = value[sym_name]
|
|
94
|
+
elsif arg.default_value?
|
|
95
|
+
coerced_obj[arg_key] = arg.default_value
|
|
96
|
+
next
|
|
97
|
+
else
|
|
98
|
+
next
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if arg_value.nil? && arg.replace_null_with_default?
|
|
102
|
+
arg_value = arg.default_value
|
|
103
|
+
end
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
coerced_obj[arg_key] = variable_value(arg_value, arg.type)
|
|
106
|
+
end
|
|
107
|
+
else
|
|
108
|
+
@query.types.arguments(type).each do |arg|
|
|
109
|
+
arg_key = arg.keyword
|
|
110
|
+
arg_name = arg.graphql_name
|
|
111
|
+
if (v_node = value.arguments.find { |a| a.name == arg_name }) # rubocop:disable Development/ContextIsPassedCop
|
|
112
|
+
arg_value = v_node.value
|
|
113
|
+
coerced_obj[arg_key] = if arg_value.nil? && arg.replace_null_with_default?
|
|
114
|
+
arg.default_value
|
|
115
|
+
else
|
|
116
|
+
variable_value(arg_value, arg.type)
|
|
117
|
+
end
|
|
118
|
+
elsif arg.default_value?
|
|
119
|
+
coerced_obj[arg_key] = arg.default_value
|
|
120
|
+
else
|
|
121
|
+
# Nothing
|
|
122
|
+
end
|
|
123
|
+
end
|
|
99
124
|
end
|
|
100
125
|
|
|
101
126
|
coerced_obj
|
|
@@ -109,11 +134,18 @@ module GraphQL
|
|
|
109
134
|
def argument_value(argument_values, argument_key, argument_definition, arg_value, override_type, field_resolve_step)
|
|
110
135
|
treat_as_type = override_type || argument_definition.type
|
|
111
136
|
if treat_as_type.non_null?
|
|
137
|
+
if arg_value.nil?
|
|
138
|
+
treat_as_type.coerce_input(arg_value, @query.context)
|
|
139
|
+
end
|
|
112
140
|
treat_as_type = treat_as_type.of_type
|
|
113
141
|
end
|
|
114
142
|
|
|
143
|
+
if arg_value.nil? && argument_definition.replace_null_with_default?
|
|
144
|
+
arg_value = argument_definition.default_value
|
|
145
|
+
end
|
|
146
|
+
|
|
115
147
|
if treat_as_type.kind.list? && !arg_value.nil?
|
|
116
|
-
inner_t = treat_as_type.
|
|
148
|
+
inner_t = treat_as_type.of_type
|
|
117
149
|
arg_value = if arg_value.is_a?(Array)
|
|
118
150
|
values = Array.new(arg_value.size)
|
|
119
151
|
arg_value.each_with_index { |inner_v, idx| argument_value(values, idx, argument_definition, inner_v, inner_t, field_resolve_step)}
|
|
@@ -150,7 +182,7 @@ module GraphQL
|
|
|
150
182
|
arg_value = begin
|
|
151
183
|
argument_definition.prepare_value(nil, arg_value, context: @query.context)
|
|
152
184
|
rescue StandardError => err
|
|
153
|
-
@runner.schema.handle_or_reraise(@query.context, err)
|
|
185
|
+
@runner.schema.handle_or_reraise(@query.context, err, object: nil, arguments: argument_values, field: field_resolve_step&.field_definition)
|
|
154
186
|
end
|
|
155
187
|
end
|
|
156
188
|
|
|
@@ -205,8 +237,6 @@ module GraphQL
|
|
|
205
237
|
nil
|
|
206
238
|
elsif value_node.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
|
207
239
|
variable_values[value_node.name]
|
|
208
|
-
elsif value_node.is_a?(GraphQL::Language::Nodes::NullValue)
|
|
209
|
-
nil
|
|
210
240
|
elsif type.list?
|
|
211
241
|
inner_type = type.of_type
|
|
212
242
|
if value_node.is_a?(Array)
|
|
@@ -214,37 +244,53 @@ module GraphQL
|
|
|
214
244
|
value_from_ast(inner_value_node, inner_type)
|
|
215
245
|
end
|
|
216
246
|
coerced_items.freeze
|
|
247
|
+
elsif value_node.is_a?(Language::Nodes::NullValue)
|
|
248
|
+
nil
|
|
217
249
|
else
|
|
218
250
|
item_value = value_from_ast(value_node, inner_type)
|
|
219
251
|
[item_value].freeze
|
|
220
252
|
end
|
|
221
|
-
|
|
222
253
|
elsif type.kind.input_object?
|
|
223
254
|
coerced_obj = {}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
255
|
+
# TODO manually handle NullValue here?
|
|
256
|
+
if value_node.is_a?(Hash)
|
|
257
|
+
@query.types.arguments(type).each do |arg|
|
|
258
|
+
arg_value = value_node[arg.keyword]
|
|
259
|
+
arg_key = arg.keyword
|
|
260
|
+
if arg_value.nil?
|
|
261
|
+
if arg.default_value?
|
|
262
|
+
coerced_obj[arg_key] = arg.default_value
|
|
263
|
+
end
|
|
264
|
+
next
|
|
234
265
|
end
|
|
235
|
-
|
|
266
|
+
|
|
267
|
+
coerced_obj[arg_key] = value_from_ast(arg_value, arg.type)
|
|
268
|
+
end
|
|
269
|
+
else
|
|
270
|
+
arg_nodes_by_name = value_node.arguments.each_with_object({}) do |arg_node, acc| # rubocop:disable Development/ContextIsPassedCop
|
|
271
|
+
acc[arg_node.name] = arg_node
|
|
236
272
|
end
|
|
237
273
|
|
|
238
|
-
|
|
239
|
-
|
|
274
|
+
@query.types.arguments(type).each do |arg|
|
|
275
|
+
arg_node = arg_nodes_by_name[arg.graphql_name]
|
|
276
|
+
arg_key = arg.keyword
|
|
277
|
+
if arg_node.nil? || (arg_node.value.is_a?(Language::Nodes::VariableIdentifier) && !variable_values.key?(arg_node.value.name))
|
|
278
|
+
if arg.default_value?
|
|
279
|
+
coerced_obj[arg_key] = arg.default_value
|
|
280
|
+
end
|
|
281
|
+
next
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
arg_value = value_from_ast(arg_node.value, arg.type)
|
|
285
|
+
coerced_obj[arg_key] = arg_value
|
|
286
|
+
end
|
|
240
287
|
end
|
|
241
288
|
|
|
289
|
+
|
|
242
290
|
coerced_obj
|
|
243
291
|
elsif type.kind.leaf?
|
|
244
|
-
if
|
|
245
|
-
|
|
246
|
-
value_node = value_node.name
|
|
247
|
-
end
|
|
292
|
+
if value_node.is_a?(Language::Nodes::AbstractNode) || value_node.is_a?(Array)
|
|
293
|
+
value_node = coerce_untyped_input(value_node)
|
|
248
294
|
end
|
|
249
295
|
|
|
250
296
|
begin
|
|
@@ -256,6 +302,32 @@ module GraphQL
|
|
|
256
302
|
raise "Unexpected input type: #{type.to_type_signature}."
|
|
257
303
|
end
|
|
258
304
|
end
|
|
305
|
+
|
|
306
|
+
private
|
|
307
|
+
|
|
308
|
+
def coerce_untyped_input(input_value)
|
|
309
|
+
case input_value
|
|
310
|
+
when Language::Nodes::AbstractNode
|
|
311
|
+
case input_value
|
|
312
|
+
when Language::Nodes::NullValue
|
|
313
|
+
nil
|
|
314
|
+
when Language::Nodes::Enum
|
|
315
|
+
input_value.name
|
|
316
|
+
when Language::Nodes::InputObject
|
|
317
|
+
value_h = {}
|
|
318
|
+
input_value.arguments.each do |arg| # rubocop:disable Development/ContextIsPassedCop
|
|
319
|
+
value_h[arg.name] = coerce_untyped_input(arg.value)
|
|
320
|
+
end
|
|
321
|
+
value_h
|
|
322
|
+
else
|
|
323
|
+
raise "Unhandled untyped input AST node: #{input_value.class}"
|
|
324
|
+
end
|
|
325
|
+
when Array
|
|
326
|
+
input_value.map { |v| coerce_untyped_input(v) }
|
|
327
|
+
else
|
|
328
|
+
input_value
|
|
329
|
+
end
|
|
330
|
+
end
|
|
259
331
|
end
|
|
260
332
|
end
|
|
261
333
|
end
|
|
@@ -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
|
|
@@ -10,10 +10,29 @@ module GraphQL
|
|
|
10
10
|
@argument_definition = argument_definition
|
|
11
11
|
@argument_key = argument_key
|
|
12
12
|
@loaded_value = nil
|
|
13
|
+
@is_authorized = true
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def value
|
|
16
|
-
|
|
17
|
+
schema = @field_resolve_step.runner.schema
|
|
18
|
+
@loaded_value = schema.sync_lazy(@loaded_value)
|
|
19
|
+
assign_value
|
|
20
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
21
|
+
@is_authorized = false
|
|
22
|
+
schema.unauthorized_object(auth_err)
|
|
23
|
+
rescue GraphQL::RuntimeError => err
|
|
24
|
+
@loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
|
|
25
|
+
err.unsubscribed_result
|
|
26
|
+
else
|
|
27
|
+
err
|
|
28
|
+
end
|
|
29
|
+
assign_value
|
|
30
|
+
rescue StandardError => stderr
|
|
31
|
+
begin
|
|
32
|
+
@field_resolve_step.selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
33
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
34
|
+
@loaded_value = ex_err
|
|
35
|
+
end
|
|
17
36
|
assign_value
|
|
18
37
|
end
|
|
19
38
|
|
|
@@ -22,6 +41,7 @@ module GraphQL
|
|
|
22
41
|
@loaded_value = begin
|
|
23
42
|
@load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
|
|
24
43
|
rescue GraphQL::UnauthorizedError => auth_err
|
|
44
|
+
@is_authorized = false
|
|
25
45
|
context.schema.unauthorized_object(auth_err)
|
|
26
46
|
end
|
|
27
47
|
if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
|
|
@@ -30,11 +50,16 @@ module GraphQL
|
|
|
30
50
|
assign_value
|
|
31
51
|
end
|
|
32
52
|
rescue GraphQL::RuntimeError => err
|
|
33
|
-
@loaded_value = err
|
|
53
|
+
@loaded_value = if err.is_a?(Schema::Subscription::EarlyUnsubscribe)
|
|
54
|
+
@is_authorized = false
|
|
55
|
+
err.unsubscribed_result
|
|
56
|
+
else
|
|
57
|
+
err
|
|
58
|
+
end
|
|
34
59
|
assign_value
|
|
35
60
|
rescue StandardError => stderr
|
|
36
61
|
@loaded_value = begin
|
|
37
|
-
context.query.handle_or_reraise(stderr)
|
|
62
|
+
context.query.handle_or_reraise(stderr, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: nil) # rubocop:disable Development/ContextIsPassedCop
|
|
38
63
|
rescue GraphQL::ExecutionError => ex_err
|
|
39
64
|
ex_err
|
|
40
65
|
end
|
|
@@ -47,6 +72,13 @@ module GraphQL
|
|
|
47
72
|
if @loaded_value.is_a?(GraphQL::RuntimeError)
|
|
48
73
|
@loaded_value.path = @field_resolve_step.path
|
|
49
74
|
@field_resolve_step.arguments = @loaded_value
|
|
75
|
+
elsif @is_authorized == false
|
|
76
|
+
# An unauthorized_object hook ate the error
|
|
77
|
+
@field_resolve_step.arguments = EmptyObjects::EMPTY_HASH
|
|
78
|
+
field_pending_steps = @field_resolve_step.pending_steps
|
|
79
|
+
field_pending_steps.clear
|
|
80
|
+
@field_resolve_step.build_errors_result(nil, nil)
|
|
81
|
+
return
|
|
50
82
|
else
|
|
51
83
|
query = @field_resolve_step.selections_step.query
|
|
52
84
|
query.current_trace.object_loaded(@argument_definition, @loaded_value, query.context)
|
|
@@ -4,15 +4,27 @@ require "graphql/execution/input_values"
|
|
|
4
4
|
require "graphql/execution/field_resolve_step"
|
|
5
5
|
require "graphql/execution/finalize"
|
|
6
6
|
require "graphql/execution/load_argument_step"
|
|
7
|
+
require "graphql/execution/resolve_type_step"
|
|
7
8
|
require "graphql/execution/runner"
|
|
8
9
|
require "graphql/execution/selections_step"
|
|
9
10
|
module GraphQL
|
|
10
11
|
module Execution
|
|
11
12
|
module Finalizer
|
|
12
13
|
attr_accessor :path
|
|
14
|
+
|
|
13
15
|
def finalize_graphql_result(query, result_data, result_key)
|
|
14
|
-
raise RequiredImplementationMissingError
|
|
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]
|
|
15
25
|
end
|
|
26
|
+
|
|
27
|
+
attr_accessor :ast_nodes
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
module HaltExecution
|
|
@@ -26,7 +38,7 @@ module GraphQL
|
|
|
26
38
|
|
|
27
39
|
module Next
|
|
28
40
|
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)
|
|
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)
|
|
30
42
|
multiplex_context = if context
|
|
31
43
|
{
|
|
32
44
|
backtrace: context[:backtrace],
|
|
@@ -39,7 +51,8 @@ module GraphQL
|
|
|
39
51
|
{}
|
|
40
52
|
end
|
|
41
53
|
query_opts = {
|
|
42
|
-
query: query_str,
|
|
54
|
+
query: query || query_str,
|
|
55
|
+
subscription_topic: subscription_topic,
|
|
43
56
|
document: document,
|
|
44
57
|
context: context,
|
|
45
58
|
validate: validate,
|
|
@@ -47,6 +60,7 @@ module GraphQL
|
|
|
47
60
|
root_value: root_value,
|
|
48
61
|
operation_name: operation_name,
|
|
49
62
|
visibility_profile: visibility_profile,
|
|
63
|
+
warden: warden,
|
|
50
64
|
}
|
|
51
65
|
m_results = multiplex_next([query_opts], context: multiplex_context, max_complexity: nil)
|
|
52
66
|
m_results[0]
|
|
@@ -55,17 +69,11 @@ module GraphQL
|
|
|
55
69
|
def multiplex_next(query_options, context: {}, max_complexity: self.max_complexity)
|
|
56
70
|
Next.run_all(self, query_options, context: context, max_complexity: max_complexity)
|
|
57
71
|
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
72
|
end
|
|
65
73
|
|
|
66
|
-
def self.use(schema,
|
|
74
|
+
def self.use(schema, as_default: false)
|
|
67
75
|
schema.extend(SchemaExtension)
|
|
68
|
-
schema.
|
|
76
|
+
schema.default_execution_next(as_default)
|
|
69
77
|
end
|
|
70
78
|
|
|
71
79
|
def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
@@ -82,7 +90,7 @@ module GraphQL
|
|
|
82
90
|
query
|
|
83
91
|
end
|
|
84
92
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
85
|
-
runner = Runner.new(multiplex
|
|
93
|
+
runner = Runner.new(multiplex)
|
|
86
94
|
runner.execute
|
|
87
95
|
end
|
|
88
96
|
end
|
|
@@ -28,7 +28,11 @@ module GraphQL
|
|
|
28
28
|
ctx = @field_resolve_step.selections_step.query.context
|
|
29
29
|
st = @field_resolve_step.static_type
|
|
30
30
|
ctx.query.current_trace.begin_resolve_type(st, @object, ctx)
|
|
31
|
-
@resolved_type,
|
|
31
|
+
@resolved_type, new_value = @field_resolve_step.sync(@resolved_type)
|
|
32
|
+
ResolveTypeStep.assert_valid_resolved_type(st, @resolved_type, new_value, @field_resolve_step)
|
|
33
|
+
if new_value
|
|
34
|
+
@object = new_value
|
|
35
|
+
end
|
|
32
36
|
ctx.query.current_trace.end_resolve_type(st, @object, ctx, @resolved_type)
|
|
33
37
|
end
|
|
34
38
|
@runner.add_step(self)
|
|
@@ -39,10 +43,12 @@ module GraphQL
|
|
|
39
43
|
when :resolve_type
|
|
40
44
|
static_type = @field_resolve_step.static_type
|
|
41
45
|
if static_type.kind.abstract?
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
query = @field_resolve_step.selections_step.query
|
|
47
|
+
@resolved_type, new_value = ResolveTypeStep.resolve_type(static_type, @object, query)
|
|
48
|
+
if new_value
|
|
49
|
+
ResolveTypeStep.assert_valid_resolved_type(static_type, @resolved_type, new_value, @field_resolve_step)
|
|
50
|
+
@object = new_value
|
|
51
|
+
end
|
|
46
52
|
else
|
|
47
53
|
@resolved_type = static_type
|
|
48
54
|
end
|
|
@@ -85,6 +91,13 @@ module GraphQL
|
|
|
85
91
|
end
|
|
86
92
|
rescue GraphQL::RuntimeError => err
|
|
87
93
|
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
94
|
+
rescue StandardError => err
|
|
95
|
+
query ||= @field_resolve_step.selections_step.query
|
|
96
|
+
begin
|
|
97
|
+
query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
|
|
98
|
+
rescue GraphQL::RuntimeError => err
|
|
99
|
+
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
100
|
+
end
|
|
88
101
|
end
|
|
89
102
|
|
|
90
103
|
def create_result
|
|
@@ -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
|