graphql 2.0.20 → 2.0.21
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/backtrace/trace.rb +96 -0
- data/lib/graphql/backtrace.rb +6 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -3
- data/lib/graphql/execution/interpreter/runtime.rb +200 -161
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/filter.rb +7 -2
- data/lib/graphql/language/document_from_schema_definition.rb +25 -9
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +475 -458
- data/lib/graphql/language/parser.y +5 -1
- data/lib/graphql/pagination/connection.rb +5 -5
- data/lib/graphql/query/context.rb +13 -12
- data/lib/graphql/query/null_context.rb +1 -1
- data/lib/graphql/query.rb +9 -5
- data/lib/graphql/schema/argument.rb +7 -9
- data/lib/graphql/schema/build_from_definition.rb +15 -3
- data/lib/graphql/schema/enum_value.rb +2 -5
- data/lib/graphql/schema/field.rb +14 -13
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/member/has_arguments.rb +1 -1
- data/lib/graphql/schema/member/has_directives.rb +4 -6
- data/lib/graphql/schema/member/has_fields.rb +80 -36
- data/lib/graphql/schema/member/has_validators.rb +2 -2
- data/lib/graphql/schema/resolver.rb +4 -4
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/warden.rb +3 -1
- data/lib/graphql/schema.rb +40 -11
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +6 -0
- data/lib/graphql/tracing/legacy_trace.rb +65 -0
- data/lib/graphql/tracing/notifications_trace.rb +2 -1
- data/lib/graphql/tracing/platform_trace.rb +21 -19
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
- data/lib/graphql/tracing/trace.rb +75 -0
- data/lib/graphql/tracing.rb +3 -123
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +4 -0
- metadata +5 -2
@@ -8,6 +8,18 @@ module GraphQL
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
class Runtime
|
11
|
+
class CurrentState
|
12
|
+
def initialize
|
13
|
+
@current_object = nil
|
14
|
+
@current_field = nil
|
15
|
+
@current_arguments = nil
|
16
|
+
@current_result_name = nil
|
17
|
+
@current_result = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :current_result, :current_result_name,
|
21
|
+
:current_arguments, :current_field, :current_object
|
22
|
+
end
|
11
23
|
|
12
24
|
module GraphQLResult
|
13
25
|
def initialize(result_name, parent_result)
|
@@ -54,7 +66,7 @@ module GraphQL
|
|
54
66
|
|
55
67
|
attr_accessor :graphql_merged_into
|
56
68
|
|
57
|
-
def
|
69
|
+
def set_leaf(key, value)
|
58
70
|
# This is a hack.
|
59
71
|
# Basically, this object is merged into the root-level result at some point.
|
60
72
|
# But the problem is, some lazies are created whose closures retain reference to _this_
|
@@ -64,20 +76,24 @@ module GraphQL
|
|
64
76
|
# In order to return a proper partial result (eg, for a directive), we have to update this object, too.
|
65
77
|
# Yowza.
|
66
78
|
if (t = @graphql_merged_into)
|
67
|
-
t
|
79
|
+
t.set_leaf(key, value)
|
68
80
|
end
|
69
81
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
@graphql_result_data[key] = value
|
77
|
-
# keep this up-to-date if it's been initialized
|
78
|
-
@graphql_metadata && @graphql_metadata[key] = value
|
79
|
-
end
|
82
|
+
@graphql_result_data[key] = value
|
83
|
+
# keep this up-to-date if it's been initialized
|
84
|
+
@graphql_metadata && @graphql_metadata[key] = value
|
85
|
+
|
86
|
+
value
|
87
|
+
end
|
80
88
|
|
89
|
+
def set_child_result(key, value)
|
90
|
+
if (t = @graphql_merged_into)
|
91
|
+
t.set_child_result(key, value)
|
92
|
+
end
|
93
|
+
@graphql_result_data[key] = value.graphql_result_data
|
94
|
+
# If we encounter some part of this response that requires metadata tracking,
|
95
|
+
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
96
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
81
97
|
value
|
82
98
|
end
|
83
99
|
|
@@ -101,6 +117,29 @@ module GraphQL
|
|
101
117
|
def [](k)
|
102
118
|
(@graphql_metadata || @graphql_result_data)[k]
|
103
119
|
end
|
120
|
+
|
121
|
+
def merge_into(into_result)
|
122
|
+
self.each do |key, value|
|
123
|
+
case value
|
124
|
+
when GraphQLResultHash
|
125
|
+
next_into = into_result[key]
|
126
|
+
if next_into
|
127
|
+
value.merge_into(next_into)
|
128
|
+
else
|
129
|
+
into_result.set_child_result(key, value)
|
130
|
+
end
|
131
|
+
when GraphQLResultArray
|
132
|
+
# There's no special handling of arrays because currently, there's no way to split the execution
|
133
|
+
# of a list over several concurrent flows.
|
134
|
+
next_result.set_child_result(key, value)
|
135
|
+
else
|
136
|
+
# We have to assume that, since this passed the `fields_will_merge` selection,
|
137
|
+
# that the old and new values are the same.
|
138
|
+
into_result.set_leaf(key, value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
@graphql_merged_into = into_result
|
142
|
+
end
|
104
143
|
end
|
105
144
|
|
106
145
|
class GraphQLResultArray
|
@@ -123,19 +162,25 @@ module GraphQL
|
|
123
162
|
@graphql_result_data.delete_at(delete_at_index)
|
124
163
|
end
|
125
164
|
|
126
|
-
def
|
165
|
+
def set_leaf(idx, value)
|
127
166
|
if @skip_indices
|
128
167
|
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
129
168
|
idx -= offset_by
|
130
169
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
@graphql_result_data[idx] = value
|
136
|
-
@graphql_metadata && @graphql_metadata[idx] = value
|
137
|
-
end
|
170
|
+
@graphql_result_data[idx] = value
|
171
|
+
@graphql_metadata && @graphql_metadata[idx] = value
|
172
|
+
value
|
173
|
+
end
|
138
174
|
|
175
|
+
def set_child_result(idx, value)
|
176
|
+
if @skip_indices
|
177
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
178
|
+
idx -= offset_by
|
179
|
+
end
|
180
|
+
@graphql_result_data[idx] = value.graphql_result_data
|
181
|
+
# If we encounter some part of this response that requires metadata tracking,
|
182
|
+
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
183
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
|
139
184
|
value
|
140
185
|
end
|
141
186
|
|
@@ -157,15 +202,6 @@ module GraphQL
|
|
157
202
|
# @return [GraphQL::Query::Context]
|
158
203
|
attr_reader :context
|
159
204
|
|
160
|
-
def thread_info
|
161
|
-
info = Thread.current[:__graphql_runtime_info]
|
162
|
-
if !info
|
163
|
-
new_ti = {}
|
164
|
-
info = Thread.current[:__graphql_runtime_info] = new_ti
|
165
|
-
end
|
166
|
-
info
|
167
|
-
end
|
168
|
-
|
169
205
|
def initialize(query:, lazies_at_depth:)
|
170
206
|
@query = query
|
171
207
|
@dataloader = query.multiplex.dataloader
|
@@ -218,7 +254,9 @@ module GraphQL
|
|
218
254
|
root_operation = query.selected_operation
|
219
255
|
root_op_type = root_operation.operation_type || "query"
|
220
256
|
root_type = schema.root_type_for_operation(root_op_type)
|
221
|
-
|
257
|
+
st = get_current_runtime_state
|
258
|
+
st.current_object = query.root_value
|
259
|
+
st.current_result = @response
|
222
260
|
object_proxy = authorized_new(root_type, query.root_value, context)
|
223
261
|
object_proxy = schema.sync_lazy(object_proxy)
|
224
262
|
|
@@ -245,7 +283,10 @@ module GraphQL
|
|
245
283
|
end
|
246
284
|
|
247
285
|
@dataloader.append_job {
|
248
|
-
|
286
|
+
st = get_current_runtime_state
|
287
|
+
st.current_object = query.root_value
|
288
|
+
st.current_result = selection_response
|
289
|
+
|
249
290
|
call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
|
250
291
|
evaluate_selections(
|
251
292
|
object_proxy,
|
@@ -265,28 +306,6 @@ module GraphQL
|
|
265
306
|
nil
|
266
307
|
end
|
267
308
|
|
268
|
-
# @return [void]
|
269
|
-
def deep_merge_selection_result(from_result, into_result)
|
270
|
-
from_result.each do |key, value|
|
271
|
-
if !into_result.key?(key)
|
272
|
-
into_result[key] = value
|
273
|
-
else
|
274
|
-
case value
|
275
|
-
when GraphQLResultHash
|
276
|
-
deep_merge_selection_result(value, into_result[key])
|
277
|
-
else
|
278
|
-
# We have to assume that, since this passed the `fields_will_merge` selection,
|
279
|
-
# that the old and new values are the same.
|
280
|
-
# There's no special handling of arrays because currently, there's no way to split the execution
|
281
|
-
# of a list over several concurrent flows.
|
282
|
-
into_result[key] = value
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
from_result.graphql_merged_into = into_result
|
287
|
-
nil
|
288
|
-
end
|
289
|
-
|
290
309
|
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
|
291
310
|
selections.each do |node|
|
292
311
|
# Skip gathering this if the directive says so
|
@@ -360,11 +379,14 @@ module GraphQL
|
|
360
379
|
selections_to_run || selections_by_name
|
361
380
|
end
|
362
381
|
|
363
|
-
NO_ARGS =
|
382
|
+
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
364
383
|
|
365
384
|
# @return [void]
|
366
385
|
def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
367
|
-
|
386
|
+
st = get_current_runtime_state
|
387
|
+
st.current_object = owner_object
|
388
|
+
st.current_result_name = nil
|
389
|
+
st.current_result = selections_result
|
368
390
|
|
369
391
|
finished_jobs = 0
|
370
392
|
enqueued_jobs = gathered_selections.size
|
@@ -375,7 +397,7 @@ module GraphQL
|
|
375
397
|
)
|
376
398
|
finished_jobs += 1
|
377
399
|
if target_result && finished_jobs == enqueued_jobs
|
378
|
-
|
400
|
+
selections_result.merge_into(target_result)
|
379
401
|
end
|
380
402
|
}
|
381
403
|
end
|
@@ -418,11 +440,16 @@ module GraphQL
|
|
418
440
|
# This seems janky, but we need to know
|
419
441
|
# the field's return type at this path in order
|
420
442
|
# to propagate `null`
|
421
|
-
|
443
|
+
return_type_non_null = return_type.non_null?
|
444
|
+
if return_type_non_null
|
422
445
|
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
423
446
|
end
|
424
447
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
425
|
-
|
448
|
+
st = get_current_runtime_state
|
449
|
+
st.current_field = field_defn
|
450
|
+
st.current_result = selections_result
|
451
|
+
st.current_result_name = result_name
|
452
|
+
|
426
453
|
object = owner_object
|
427
454
|
|
428
455
|
if is_introspection
|
@@ -432,25 +459,34 @@ module GraphQL
|
|
432
459
|
total_args_count = field_defn.arguments(context).size
|
433
460
|
if total_args_count == 0
|
434
461
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
435
|
-
|
462
|
+
if field_defn.extras.size == 0
|
463
|
+
evaluate_selection_with_resolved_keyword_args(
|
464
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
|
465
|
+
)
|
466
|
+
else
|
467
|
+
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, return_type_non_null)
|
468
|
+
end
|
436
469
|
else
|
437
|
-
# TODO remove all arguments(...) usages?
|
438
470
|
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
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)
|
471
|
+
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, return_type_non_null)
|
440
472
|
end
|
441
473
|
end
|
442
474
|
end
|
443
475
|
|
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
|
476
|
+
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, return_type_non_null) # rubocop:disable Metrics/ParameterLists
|
445
477
|
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
478
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
447
|
-
continue_value(resolved_arguments, owner_type, field_defn,
|
479
|
+
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
448
480
|
next
|
449
481
|
end
|
450
482
|
|
451
|
-
kwarg_arguments = if
|
452
|
-
|
453
|
-
|
483
|
+
kwarg_arguments = if field_defn.extras.empty?
|
484
|
+
if resolved_arguments.empty?
|
485
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
486
|
+
NO_ARGS
|
487
|
+
else
|
488
|
+
resolved_arguments.keyword_arguments
|
489
|
+
end
|
454
490
|
else
|
455
491
|
# Bundle up the extras, then make a new arguments instance
|
456
492
|
# that includes the extras, too.
|
@@ -489,61 +525,70 @@ module GraphQL
|
|
489
525
|
resolved_arguments.keyword_arguments
|
490
526
|
end
|
491
527
|
|
492
|
-
|
528
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
|
529
|
+
end
|
530
|
+
end
|
493
531
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
ex_err
|
520
|
-
end
|
532
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
|
533
|
+
st = get_current_runtime_state
|
534
|
+
st.current_field = field_defn
|
535
|
+
st.current_object = object
|
536
|
+
st.current_arguments = resolved_arguments
|
537
|
+
st.current_result_name = result_name
|
538
|
+
st.current_result = selection_result
|
539
|
+
# Optimize for the case that field is selected only once
|
540
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
541
|
+
next_selections = ast_node.selections
|
542
|
+
directives = ast_node.directives
|
543
|
+
else
|
544
|
+
next_selections = []
|
545
|
+
directives = []
|
546
|
+
field_ast_nodes.each { |f|
|
547
|
+
next_selections.concat(f.selections)
|
548
|
+
directives.concat(f.directives)
|
549
|
+
}
|
550
|
+
end
|
551
|
+
|
552
|
+
field_result = call_method_on_directives(:resolve, object, directives) do
|
553
|
+
# Actually call the field resolver and capture the result
|
554
|
+
app_result = begin
|
555
|
+
query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
556
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
521
557
|
end
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
558
|
+
rescue GraphQL::ExecutionError => err
|
559
|
+
err
|
560
|
+
rescue StandardError => err
|
561
|
+
begin
|
562
|
+
query.handle_or_reraise(err)
|
563
|
+
rescue GraphQL::ExecutionError => ex_err
|
564
|
+
ex_err
|
527
565
|
end
|
528
566
|
end
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
535
|
-
else
|
536
|
-
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
537
|
-
field_result
|
567
|
+
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|
|
568
|
+
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
569
|
+
if HALT != continue_value
|
570
|
+
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
571
|
+
end
|
538
572
|
end
|
539
573
|
end
|
574
|
+
|
575
|
+
# If this field is a root mutation field, immediately resolve
|
576
|
+
# all of its child fields before moving on to the next root mutation field.
|
577
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
578
|
+
if is_eager_field
|
579
|
+
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
580
|
+
else
|
581
|
+
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
582
|
+
field_result
|
583
|
+
end
|
540
584
|
end
|
541
585
|
|
586
|
+
|
542
587
|
def dead_result?(selection_result)
|
543
|
-
selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
588
|
+
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
544
589
|
end
|
545
590
|
|
546
|
-
def set_result(selection_result, result_name, value)
|
591
|
+
def set_result(selection_result, result_name, value, is_child_result)
|
547
592
|
if !dead_result?(selection_result)
|
548
593
|
if value.nil? &&
|
549
594
|
( # there are two conditions under which `nil` is not allowed in the response:
|
@@ -563,11 +608,13 @@ module GraphQL
|
|
563
608
|
if parent.nil? # This is a top-level result hash
|
564
609
|
@response = nil
|
565
610
|
else
|
566
|
-
set_result(parent, name_in_parent, nil)
|
611
|
+
set_result(parent, name_in_parent, nil, false)
|
567
612
|
set_graphql_dead(selection_result)
|
568
613
|
end
|
614
|
+
elsif is_child_result
|
615
|
+
selection_result.set_child_result(result_name, value)
|
569
616
|
else
|
570
|
-
selection_result
|
617
|
+
selection_result.set_leaf(result_name, value)
|
571
618
|
end
|
572
619
|
end
|
573
620
|
end
|
@@ -588,11 +635,10 @@ module GraphQL
|
|
588
635
|
end
|
589
636
|
|
590
637
|
def current_path
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
if path && (rn = ti[:current_result_name])
|
638
|
+
st = get_current_runtime_state
|
639
|
+
result = st.current_result
|
640
|
+
path = result && result.path
|
641
|
+
if path && (rn = st.current_result_name)
|
596
642
|
path = path.dup
|
597
643
|
path.push(rn)
|
598
644
|
end
|
@@ -604,13 +650,13 @@ module GraphQL
|
|
604
650
|
case value
|
605
651
|
when nil
|
606
652
|
if is_non_null
|
607
|
-
set_result(selection_result, result_name, nil) do
|
653
|
+
set_result(selection_result, result_name, nil, false) do
|
608
654
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
609
655
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
610
656
|
schema.type_error(err, context)
|
611
657
|
end
|
612
658
|
else
|
613
|
-
set_result(selection_result, result_name, nil)
|
659
|
+
set_result(selection_result, result_name, nil, false)
|
614
660
|
end
|
615
661
|
HALT
|
616
662
|
when GraphQL::Error
|
@@ -623,7 +669,7 @@ module GraphQL
|
|
623
669
|
value.ast_node ||= ast_node
|
624
670
|
context.errors << value
|
625
671
|
if selection_result
|
626
|
-
set_result(selection_result, result_name, nil)
|
672
|
+
set_result(selection_result, result_name, nil, false)
|
627
673
|
end
|
628
674
|
end
|
629
675
|
HALT
|
@@ -677,9 +723,9 @@ module GraphQL
|
|
677
723
|
if selection_result
|
678
724
|
if list_type_at_all
|
679
725
|
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
680
|
-
set_result(selection_result, result_name, result_without_errors)
|
726
|
+
set_result(selection_result, result_name, result_without_errors, false)
|
681
727
|
else
|
682
|
-
set_result(selection_result, result_name, nil)
|
728
|
+
set_result(selection_result, result_name, nil, false)
|
683
729
|
end
|
684
730
|
end
|
685
731
|
end
|
@@ -689,7 +735,7 @@ module GraphQL
|
|
689
735
|
end
|
690
736
|
when GraphQL::Execution::Interpreter::RawValue
|
691
737
|
# Write raw value directly to the response without resolving nested objects
|
692
|
-
set_result(selection_result, result_name, value.resolve)
|
738
|
+
set_result(selection_result, result_name, value.resolve, false)
|
693
739
|
HALT
|
694
740
|
else
|
695
741
|
value
|
@@ -717,7 +763,7 @@ module GraphQL
|
|
717
763
|
rescue StandardError => err
|
718
764
|
schema.handle_or_reraise(context, err)
|
719
765
|
end
|
720
|
-
set_result(selection_result, result_name, r)
|
766
|
+
set_result(selection_result, result_name, r, false)
|
721
767
|
r
|
722
768
|
when "UNION", "INTERFACE"
|
723
769
|
resolved_type_or_lazy = resolve_type(current_type, value)
|
@@ -735,7 +781,7 @@ module GraphQL
|
|
735
781
|
err_class = current_type::UnresolvedTypeError
|
736
782
|
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
737
783
|
schema.type_error(type_error, context)
|
738
|
-
set_result(selection_result, result_name, nil)
|
784
|
+
set_result(selection_result, result_name, nil, false)
|
739
785
|
nil
|
740
786
|
else
|
741
787
|
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
@@ -751,7 +797,7 @@ module GraphQL
|
|
751
797
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
752
798
|
if HALT != continue_value
|
753
799
|
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
754
|
-
set_result(selection_result, result_name, response_hash)
|
800
|
+
set_result(selection_result, result_name, response_hash, true)
|
755
801
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
756
802
|
# There are two possibilities for `gathered_selections`:
|
757
803
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
@@ -769,8 +815,13 @@ module GraphQL
|
|
769
815
|
this_result = response_hash
|
770
816
|
final_result = nil
|
771
817
|
end
|
772
|
-
#
|
773
|
-
|
818
|
+
# reset this mutable state
|
819
|
+
# Unset `result_name` here because it's already included in the new response hash
|
820
|
+
st = get_current_runtime_state
|
821
|
+
st.current_object = continue_value
|
822
|
+
st.current_result_name = nil
|
823
|
+
st.current_result = this_result
|
824
|
+
|
774
825
|
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
775
826
|
evaluate_selections(
|
776
827
|
continue_value,
|
@@ -790,21 +841,21 @@ module GraphQL
|
|
790
841
|
inner_type = current_type.of_type
|
791
842
|
# This is true for objects, unions, and interfaces
|
792
843
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
844
|
+
inner_type_non_null = inner_type.non_null?
|
793
845
|
response_list = GraphQLResultArray.new(result_name, selection_result)
|
794
|
-
response_list.graphql_non_null_list_items =
|
795
|
-
set_result(selection_result, result_name, response_list)
|
846
|
+
response_list.graphql_non_null_list_items = inner_type_non_null
|
847
|
+
set_result(selection_result, result_name, response_list, true)
|
796
848
|
idx = 0
|
797
849
|
list_value = begin
|
798
850
|
value.each do |inner_value|
|
799
|
-
break if dead_result?(response_list)
|
800
851
|
this_idx = idx
|
801
852
|
idx += 1
|
802
853
|
if use_dataloader_job
|
803
854
|
@dataloader.append_job do
|
804
|
-
resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
855
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
805
856
|
end
|
806
857
|
else
|
807
|
-
resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
858
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
808
859
|
end
|
809
860
|
end
|
810
861
|
|
@@ -834,12 +885,14 @@ module GraphQL
|
|
834
885
|
end
|
835
886
|
end
|
836
887
|
|
837
|
-
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
|
838
|
-
|
888
|
+
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
|
889
|
+
st = get_current_runtime_state
|
890
|
+
st.current_result_name = this_idx
|
891
|
+
st.current_result = response_list
|
839
892
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
840
893
|
# This will update `response_list` with the lazy
|
841
894
|
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|
|
842
|
-
continue_value = continue_value(inner_inner_value, owner_type, field,
|
895
|
+
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
843
896
|
if HALT != continue_value
|
844
897
|
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
845
898
|
end
|
@@ -891,21 +944,8 @@ module GraphQL
|
|
891
944
|
true
|
892
945
|
end
|
893
946
|
|
894
|
-
def
|
895
|
-
|
896
|
-
if object
|
897
|
-
ti[:current_object] = object
|
898
|
-
end
|
899
|
-
if field
|
900
|
-
ti[:current_field] = field
|
901
|
-
end
|
902
|
-
if arguments
|
903
|
-
ti[:current_arguments] = arguments
|
904
|
-
end
|
905
|
-
ti[:current_result_name] = result_name
|
906
|
-
if result
|
907
|
-
ti[:current_result] = result
|
908
|
-
end
|
947
|
+
def get_current_runtime_state
|
948
|
+
Thread.current[:__graphql_runtime_info] ||= CurrentState.new
|
909
949
|
end
|
910
950
|
|
911
951
|
# @param obj [Object] Some user-returned value that may want to be batched
|
@@ -917,7 +957,12 @@ module GraphQL
|
|
917
957
|
if lazy?(lazy_obj)
|
918
958
|
orig_result = result
|
919
959
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
920
|
-
|
960
|
+
st = get_current_runtime_state
|
961
|
+
st.current_object = owner_object
|
962
|
+
st.current_field = field
|
963
|
+
st.current_arguments = arguments
|
964
|
+
st.current_result_name = result_name
|
965
|
+
st.current_result = orig_result
|
921
966
|
# Wrap the execution of _this_ method with tracing,
|
922
967
|
# but don't wrap the continuation below
|
923
968
|
inner_obj = begin
|
@@ -943,7 +988,7 @@ module GraphQL
|
|
943
988
|
if eager
|
944
989
|
lazy.value
|
945
990
|
else
|
946
|
-
set_result(result, result_name, lazy)
|
991
|
+
set_result(result, result_name, lazy, false)
|
947
992
|
current_depth = 0
|
948
993
|
while result
|
949
994
|
current_depth += 1
|
@@ -953,7 +998,7 @@ module GraphQL
|
|
953
998
|
lazy
|
954
999
|
end
|
955
1000
|
else
|
956
|
-
|
1001
|
+
# Don't need to reset state here because it _wasn't_ lazy.
|
957
1002
|
yield(lazy_obj)
|
958
1003
|
end
|
959
1004
|
end
|
@@ -968,13 +1013,7 @@ module GraphQL
|
|
968
1013
|
end
|
969
1014
|
|
970
1015
|
def delete_all_interpreter_context
|
971
|
-
|
972
|
-
ti.delete(:current_result)
|
973
|
-
ti.delete(:current_result_name)
|
974
|
-
ti.delete(:current_field)
|
975
|
-
ti.delete(:current_object)
|
976
|
-
ti.delete(:current_arguments)
|
977
|
-
end
|
1016
|
+
Thread.current[:__graphql_runtime_info] = nil
|
978
1017
|
end
|
979
1018
|
|
980
1019
|
def resolve_type(type, value)
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
14
14
|
class << self
|
15
15
|
# Used internally to signal that the query shouldn't be executed
|
16
16
|
# @api private
|
17
|
-
NO_OPERATION =
|
17
|
+
NO_OPERATION = GraphQL::EmptyObjects::EMPTY_HASH
|
18
18
|
|
19
19
|
# @param schema [GraphQL::Schema]
|
20
20
|
# @param queries [Array<GraphQL::Query, Hash>]
|
data/lib/graphql/filter.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/deprecation"
|
3
|
+
|
2
4
|
module GraphQL
|
3
5
|
# @api private
|
4
6
|
class Filter
|
5
|
-
def initialize(only: nil, except: nil)
|
7
|
+
def initialize(only: nil, except: nil, silence_deprecation_warning: false)
|
8
|
+
if !silence_deprecation_warning
|
9
|
+
GraphQL::Deprecation.warn("GraphQL::Filter is deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).")
|
10
|
+
end
|
6
11
|
@only = only
|
7
12
|
@except = except
|
8
13
|
end
|
@@ -17,7 +22,7 @@ module GraphQL
|
|
17
22
|
onlies = [self].concat(Array(only))
|
18
23
|
merged_only = MergedOnly.build(onlies)
|
19
24
|
merged_except = MergedExcept.build(Array(except))
|
20
|
-
self.class.new(only: merged_only, except: merged_except)
|
25
|
+
self.class.new(only: merged_only, except: merged_except, silence_deprecation_warning: true)
|
21
26
|
end
|
22
27
|
|
23
28
|
private
|