graphql 2.0.21 → 2.0.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/backtrace.rb +0 -4
- data/lib/graphql/execution/interpreter/arguments_cache.rb +31 -30
- data/lib/graphql/execution/interpreter/runtime.rb +101 -83
- data/lib/graphql/execution/interpreter.rb +1 -2
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/filter.rb +2 -1
- data/lib/graphql/language/document_from_schema_definition.rb +16 -9
- data/lib/graphql/language/lexer.rb +5 -3
- data/lib/graphql/language/printer.rb +28 -14
- data/lib/graphql/query/context.rb +16 -7
- data/lib/graphql/query/null_context.rb +8 -18
- data/lib/graphql/query.rb +21 -11
- data/lib/graphql/schema/always_visible.rb +10 -0
- data/lib/graphql/schema/argument.rb +5 -5
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +23 -12
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +9 -7
- data/lib/graphql/schema/object.rb +1 -1
- data/lib/graphql/schema/printer.rb +3 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- data/lib/graphql/schema/resolver.rb +4 -4
- data/lib/graphql/schema/timeout.rb +1 -1
- data/lib/graphql/schema/warden.rb +37 -4
- data/lib/graphql/schema.rb +29 -14
- data/lib/graphql/tracing/legacy_trace.rb +5 -1
- data/lib/graphql/tracing/notifications_trace.rb +3 -0
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5aade8e5f88eb3dd8e8d6148aa7e8e00b0a351422b090e02a911158f425f1905
|
4
|
+
data.tar.gz: 1ee1e4529f0b1f68596ce73f2b171624637999f0a1d6fc03f377c46fc7756a78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b9cd861e35c529674bdacb6e1a34864bca6852c030022215ecc028d2b36b20da120be4ee45bb5ac5fb906b856e78dc4cfbbcf55609ad47fc47e6d68422a26b1
|
7
|
+
data.tar.gz: b4afc463a0c9058396f274c9224f5a6b0bca73fefc68c1e6f10e53eea24b95b89ef6d9ddd3e22929d90be69b7c46bd81e6ed134ddab2550cbfc4408aee29b8ef
|
data/lib/graphql/backtrace.rb
CHANGED
@@ -7,49 +7,50 @@ module GraphQL
|
|
7
7
|
def initialize(query)
|
8
8
|
@query = query
|
9
9
|
@dataloader = query.context.dataloader
|
10
|
-
@storage = Hash.new do |h,
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
h3[parent_object] = NO_ARGUMENTS
|
22
|
-
else
|
23
|
-
h3[parent_object]
|
24
|
-
end
|
10
|
+
@storage = Hash.new do |h, argument_owner|
|
11
|
+
args_by_parent = if argument_owner.arguments_statically_coercible?
|
12
|
+
shared_values_cache = {}
|
13
|
+
Hash.new do |h2, ignored_parent_object|
|
14
|
+
h2[ignored_parent_object] = shared_values_cache
|
15
|
+
end
|
16
|
+
else
|
17
|
+
Hash.new do |h2, parent_object|
|
18
|
+
args_by_node = {}
|
19
|
+
args_by_node.compare_by_identity
|
20
|
+
h2[parent_object] = args_by_node
|
25
21
|
end
|
26
22
|
end
|
23
|
+
args_by_parent.compare_by_identity
|
24
|
+
h[argument_owner] = args_by_parent
|
27
25
|
end
|
26
|
+
@storage.compare_by_identity
|
28
27
|
end
|
29
28
|
|
30
29
|
def fetch(ast_node, argument_owner, parent_object)
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
@storage[
|
30
|
+
# This runs eagerly if no block is given
|
31
|
+
@storage[argument_owner][parent_object][ast_node] ||= begin
|
32
|
+
args_hash = self.class.prepare_args_hash(@query, ast_node)
|
33
|
+
kwarg_arguments = argument_owner.coerce_arguments(parent_object, args_hash, @query.context)
|
34
|
+
@query.after_lazy(kwarg_arguments) do |resolved_args|
|
35
|
+
@storage[argument_owner][parent_object][ast_node] = resolved_args
|
37
36
|
end
|
38
37
|
end
|
39
|
-
|
40
|
-
# overridden with an immutable arguments instance.
|
41
|
-
# The first call queues up the job,
|
42
|
-
# then this call fetches the result.
|
43
|
-
# TODO this should be better, find a solution
|
44
|
-
# that works with merging the runtime.rb code
|
45
|
-
@storage[ast_node][argument_owner][parent_object]
|
38
|
+
|
46
39
|
end
|
47
40
|
|
48
41
|
# @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
|
49
42
|
def dataload_for(ast_node, argument_owner, parent_object, &block)
|
50
43
|
# First, normalize all AST or Ruby values to a plain Ruby hash
|
51
|
-
|
52
|
-
|
44
|
+
arg_storage = @storage[argument_owner][parent_object]
|
45
|
+
if (args = arg_storage[ast_node])
|
46
|
+
yield(args)
|
47
|
+
else
|
48
|
+
args_hash = self.class.prepare_args_hash(@query, ast_node)
|
49
|
+
argument_owner.coerce_arguments(parent_object, args_hash, @query.context) do |resolved_args|
|
50
|
+
arg_storage[ast_node] = resolved_args
|
51
|
+
yield(resolved_args)
|
52
|
+
end
|
53
|
+
end
|
53
54
|
nil
|
54
55
|
end
|
55
56
|
|
@@ -22,12 +22,13 @@ module GraphQL
|
|
22
22
|
end
|
23
23
|
|
24
24
|
module GraphQLResult
|
25
|
-
def initialize(result_name, parent_result)
|
25
|
+
def initialize(result_name, parent_result, is_non_null_in_parent)
|
26
26
|
@graphql_parent = parent_result
|
27
27
|
if parent_result && parent_result.graphql_dead
|
28
28
|
@graphql_dead = true
|
29
29
|
end
|
30
30
|
@graphql_result_name = result_name
|
31
|
+
@graphql_is_non_null_in_parent = is_non_null_in_parent
|
31
32
|
# Jump through some hoops to avoid creating this duplicate storage if at all possible.
|
32
33
|
@graphql_metadata = nil
|
33
34
|
end
|
@@ -42,22 +43,14 @@ module GraphQL
|
|
42
43
|
end
|
43
44
|
|
44
45
|
attr_accessor :graphql_dead
|
45
|
-
attr_reader :graphql_parent, :graphql_result_name
|
46
|
-
|
47
|
-
# Although these are used by only one of the Result classes,
|
48
|
-
# it's handy to have the methods implemented on both (even though they just return `nil`)
|
49
|
-
# because it makes it easy to check if anything is assigned.
|
50
|
-
# @return [nil, Array<String>]
|
51
|
-
attr_accessor :graphql_non_null_field_names
|
52
|
-
# @return [nil, true]
|
53
|
-
attr_accessor :graphql_non_null_list_items
|
46
|
+
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
|
54
47
|
|
55
48
|
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
56
49
|
attr_accessor :graphql_result_data
|
57
50
|
end
|
58
51
|
|
59
52
|
class GraphQLResultHash
|
60
|
-
def initialize(_result_name, _parent_result)
|
53
|
+
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
61
54
|
super
|
62
55
|
@graphql_result_data = {}
|
63
56
|
end
|
@@ -145,7 +138,7 @@ module GraphQL
|
|
145
138
|
class GraphQLResultArray
|
146
139
|
include GraphQLResult
|
147
140
|
|
148
|
-
def initialize(_result_name, _parent_result)
|
141
|
+
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
149
142
|
super
|
150
143
|
@graphql_result_data = []
|
151
144
|
end
|
@@ -189,10 +182,6 @@ module GraphQL
|
|
189
182
|
end
|
190
183
|
end
|
191
184
|
|
192
|
-
class GraphQLSelectionSet < Hash
|
193
|
-
attr_accessor :graphql_directives
|
194
|
-
end
|
195
|
-
|
196
185
|
# @return [GraphQL::Query]
|
197
186
|
attr_reader :query
|
198
187
|
|
@@ -204,14 +193,12 @@ module GraphQL
|
|
204
193
|
|
205
194
|
def initialize(query:, lazies_at_depth:)
|
206
195
|
@query = query
|
196
|
+
@current_trace = query.current_trace
|
207
197
|
@dataloader = query.multiplex.dataloader
|
208
198
|
@lazies_at_depth = lazies_at_depth
|
209
199
|
@schema = query.schema
|
210
200
|
@context = query.context
|
211
|
-
@
|
212
|
-
# Start this off empty:
|
213
|
-
Thread.current[:__graphql_runtime_info] = nil
|
214
|
-
@response = GraphQLResultHash.new(nil, nil)
|
201
|
+
@response = GraphQLResultHash.new(nil, nil, false)
|
215
202
|
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
216
203
|
@runtime_directive_names = []
|
217
204
|
noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
|
@@ -225,8 +212,11 @@ module GraphQL
|
|
225
212
|
# Which assumes that MyObject.get_field("myField") will return the same field
|
226
213
|
# during the lifetime of a query
|
227
214
|
@fields_cache = Hash.new { |h, k| h[k] = {} }
|
215
|
+
# this can by by-identity since owners are the same object, but not the sub-hash, which uses strings.
|
216
|
+
@fields_cache.compare_by_identity
|
228
217
|
# { Class => Boolean }
|
229
218
|
@lazy_cache = {}
|
219
|
+
@lazy_cache.compare_by_identity
|
230
220
|
end
|
231
221
|
|
232
222
|
def final_result
|
@@ -275,7 +265,7 @@ module GraphQL
|
|
275
265
|
# directly evaluated and the results can be written right into the main response hash.
|
276
266
|
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
277
267
|
if is_selection_array
|
278
|
-
selection_response = GraphQLResultHash.new(nil, nil)
|
268
|
+
selection_response = GraphQLResultHash.new(nil, nil, false)
|
279
269
|
final_response = @response
|
280
270
|
else
|
281
271
|
selection_response = @response
|
@@ -286,8 +276,11 @@ module GraphQL
|
|
286
276
|
st = get_current_runtime_state
|
287
277
|
st.current_object = query.root_value
|
288
278
|
st.current_result = selection_response
|
289
|
-
|
290
|
-
|
279
|
+
# This is a less-frequent case; use a fast check since it's often not there.
|
280
|
+
if (directives = selections[:graphql_directives])
|
281
|
+
selections.delete(:graphql_directives)
|
282
|
+
end
|
283
|
+
call_method_on_directives(:resolve, object_proxy, directives) do
|
291
284
|
evaluate_selections(
|
292
285
|
object_proxy,
|
293
286
|
root_type,
|
@@ -306,7 +299,7 @@ module GraphQL
|
|
306
299
|
nil
|
307
300
|
end
|
308
301
|
|
309
|
-
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name =
|
302
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
310
303
|
selections.each do |node|
|
311
304
|
# Skip gathering this if the directive says so
|
312
305
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -332,8 +325,8 @@ module GraphQL
|
|
332
325
|
else
|
333
326
|
# This is an InlineFragment or a FragmentSpread
|
334
327
|
if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
335
|
-
next_selections =
|
336
|
-
next_selections
|
328
|
+
next_selections = {}
|
329
|
+
next_selections[:graphql_directives] = node.directives
|
337
330
|
if selections_to_run
|
338
331
|
selections_to_run << next_selections
|
339
332
|
else
|
@@ -350,12 +343,8 @@ module GraphQL
|
|
350
343
|
if node.type
|
351
344
|
type_defn = schema.get_type(node.type.name, context)
|
352
345
|
|
353
|
-
|
354
|
-
|
355
|
-
if t == owner_type
|
356
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
357
|
-
break
|
358
|
-
end
|
346
|
+
if query.warden.possible_types(type_defn).include?(owner_type)
|
347
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
359
348
|
end
|
360
349
|
else
|
361
350
|
# it's an untyped fragment, definitely continue
|
@@ -364,12 +353,8 @@ module GraphQL
|
|
364
353
|
when GraphQL::Language::Nodes::FragmentSpread
|
365
354
|
fragment_def = query.fragments[node.name]
|
366
355
|
type_defn = query.get_type(fragment_def.type.name)
|
367
|
-
|
368
|
-
|
369
|
-
if t == owner_type
|
370
|
-
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
371
|
-
break
|
372
|
-
end
|
356
|
+
if query.warden.possible_types(type_defn).include?(owner_type)
|
357
|
+
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
373
358
|
end
|
374
359
|
else
|
375
360
|
raise "Invariant: unexpected selection class: #{node.class}"
|
@@ -400,6 +385,12 @@ module GraphQL
|
|
400
385
|
selections_result.merge_into(target_result)
|
401
386
|
end
|
402
387
|
}
|
388
|
+
# Field resolution may pause the fiber,
|
389
|
+
# so it wouldn't get to the `Resolve` call that happens below.
|
390
|
+
# So instead trigger a run from this outer context.
|
391
|
+
if is_eager_selection
|
392
|
+
@dataloader.run
|
393
|
+
end
|
403
394
|
end
|
404
395
|
|
405
396
|
selections_result
|
@@ -441,9 +432,6 @@ module GraphQL
|
|
441
432
|
# the field's return type at this path in order
|
442
433
|
# to propagate `null`
|
443
434
|
return_type_non_null = return_type.non_null?
|
444
|
-
if return_type_non_null
|
445
|
-
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
446
|
-
end
|
447
435
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
448
436
|
st = get_current_runtime_state
|
449
437
|
st.current_field = field_defn
|
@@ -474,7 +462,7 @@ module GraphQL
|
|
474
462
|
end
|
475
463
|
|
476
464
|
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
|
477
|
-
after_lazy(arguments,
|
465
|
+
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
478
466
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
479
467
|
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
480
468
|
next
|
@@ -552,7 +540,7 @@ module GraphQL
|
|
552
540
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
553
541
|
# Actually call the field resolver and capture the result
|
554
542
|
app_result = begin
|
555
|
-
|
543
|
+
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
556
544
|
field_defn.resolve(object, kwarg_arguments, context)
|
557
545
|
end
|
558
546
|
rescue GraphQL::ExecutionError => err
|
@@ -564,7 +552,7 @@ module GraphQL
|
|
564
552
|
ex_err
|
565
553
|
end
|
566
554
|
end
|
567
|
-
after_lazy(app_result,
|
555
|
+
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
|
568
556
|
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
569
557
|
if HALT != continue_value
|
570
558
|
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
@@ -588,13 +576,9 @@ module GraphQL
|
|
588
576
|
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
589
577
|
end
|
590
578
|
|
591
|
-
def set_result(selection_result, result_name, value, is_child_result)
|
579
|
+
def set_result(selection_result, result_name, value, is_child_result, is_non_null)
|
592
580
|
if !dead_result?(selection_result)
|
593
|
-
if value.nil? &&
|
594
|
-
( # there are two conditions under which `nil` is not allowed in the response:
|
595
|
-
(selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
|
596
|
-
((nn = selection_result.graphql_non_null_field_names) && nn.include?(result_name)) # this value would be written into a field that doesn't allow nils
|
597
|
-
)
|
581
|
+
if value.nil? && is_non_null
|
598
582
|
# This is an invalid nil that should be propagated
|
599
583
|
# One caller of this method passes a block,
|
600
584
|
# namely when application code returns a `nil` to GraphQL and it doesn't belong there.
|
@@ -604,11 +588,12 @@ module GraphQL
|
|
604
588
|
# TODO the code is trying to tell me something.
|
605
589
|
yield if block_given?
|
606
590
|
parent = selection_result.graphql_parent
|
607
|
-
name_in_parent = selection_result.graphql_result_name
|
608
591
|
if parent.nil? # This is a top-level result hash
|
609
592
|
@response = nil
|
610
593
|
else
|
611
|
-
|
594
|
+
name_in_parent = selection_result.graphql_result_name
|
595
|
+
is_non_null_in_parent = selection_result.graphql_is_non_null_in_parent
|
596
|
+
set_result(parent, name_in_parent, nil, false, is_non_null_in_parent)
|
612
597
|
set_graphql_dead(selection_result)
|
613
598
|
end
|
614
599
|
elsif is_child_result
|
@@ -650,13 +635,13 @@ module GraphQL
|
|
650
635
|
case value
|
651
636
|
when nil
|
652
637
|
if is_non_null
|
653
|
-
set_result(selection_result, result_name, nil, false) do
|
638
|
+
set_result(selection_result, result_name, nil, false, is_non_null) do
|
654
639
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
655
640
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
656
641
|
schema.type_error(err, context)
|
657
642
|
end
|
658
643
|
else
|
659
|
-
set_result(selection_result, result_name, nil, false)
|
644
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
660
645
|
end
|
661
646
|
HALT
|
662
647
|
when GraphQL::Error
|
@@ -669,7 +654,7 @@ module GraphQL
|
|
669
654
|
value.ast_node ||= ast_node
|
670
655
|
context.errors << value
|
671
656
|
if selection_result
|
672
|
-
set_result(selection_result, result_name, nil, false)
|
657
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
673
658
|
end
|
674
659
|
end
|
675
660
|
HALT
|
@@ -723,9 +708,9 @@ module GraphQL
|
|
723
708
|
if selection_result
|
724
709
|
if list_type_at_all
|
725
710
|
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
726
|
-
set_result(selection_result, result_name, result_without_errors, false)
|
711
|
+
set_result(selection_result, result_name, result_without_errors, false, is_non_null)
|
727
712
|
else
|
728
|
-
set_result(selection_result, result_name, nil, false)
|
713
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
729
714
|
end
|
730
715
|
end
|
731
716
|
end
|
@@ -735,7 +720,7 @@ module GraphQL
|
|
735
720
|
end
|
736
721
|
when GraphQL::Execution::Interpreter::RawValue
|
737
722
|
# Write raw value directly to the response without resolving nested objects
|
738
|
-
set_result(selection_result, result_name, value.resolve, false)
|
723
|
+
set_result(selection_result, result_name, value.resolve, false, is_non_null)
|
739
724
|
HALT
|
740
725
|
else
|
741
726
|
value
|
@@ -763,11 +748,11 @@ module GraphQL
|
|
763
748
|
rescue StandardError => err
|
764
749
|
schema.handle_or_reraise(context, err)
|
765
750
|
end
|
766
|
-
set_result(selection_result, result_name, r, false)
|
751
|
+
set_result(selection_result, result_name, r, false, is_non_null)
|
767
752
|
r
|
768
753
|
when "UNION", "INTERFACE"
|
769
754
|
resolved_type_or_lazy = resolve_type(current_type, value)
|
770
|
-
after_lazy(resolved_type_or_lazy,
|
755
|
+
after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
|
771
756
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
772
757
|
resolved_type, resolved_value = resolved_type_result
|
773
758
|
else
|
@@ -781,7 +766,7 @@ module GraphQL
|
|
781
766
|
err_class = current_type::UnresolvedTypeError
|
782
767
|
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
783
768
|
schema.type_error(type_error, context)
|
784
|
-
set_result(selection_result, result_name, nil, false)
|
769
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
785
770
|
nil
|
786
771
|
else
|
787
772
|
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
@@ -793,11 +778,11 @@ module GraphQL
|
|
793
778
|
rescue GraphQL::ExecutionError => err
|
794
779
|
err
|
795
780
|
end
|
796
|
-
after_lazy(object_proxy,
|
781
|
+
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
|
797
782
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
798
783
|
if HALT != continue_value
|
799
|
-
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
800
|
-
set_result(selection_result, result_name, response_hash, true)
|
784
|
+
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
785
|
+
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
801
786
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
802
787
|
# There are two possibilities for `gathered_selections`:
|
803
788
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
@@ -809,7 +794,7 @@ module GraphQL
|
|
809
794
|
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
810
795
|
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
811
796
|
if is_selection_array
|
812
|
-
this_result = GraphQLResultHash.new(result_name, selection_result)
|
797
|
+
this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
813
798
|
final_result = response_hash
|
814
799
|
else
|
815
800
|
this_result = response_hash
|
@@ -822,7 +807,11 @@ module GraphQL
|
|
822
807
|
st.current_result_name = nil
|
823
808
|
st.current_result = this_result
|
824
809
|
|
825
|
-
|
810
|
+
# This is a less-frequent case; use a fast check since it's often not there.
|
811
|
+
if (directives = selections[:graphql_directives])
|
812
|
+
selections.delete(:graphql_directives)
|
813
|
+
end
|
814
|
+
call_method_on_directives(:resolve, continue_value, directives) do
|
826
815
|
evaluate_selections(
|
827
816
|
continue_value,
|
828
817
|
current_type,
|
@@ -842,12 +831,12 @@ module GraphQL
|
|
842
831
|
# This is true for objects, unions, and interfaces
|
843
832
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
844
833
|
inner_type_non_null = inner_type.non_null?
|
845
|
-
response_list = GraphQLResultArray.new(result_name, selection_result)
|
846
|
-
response_list
|
847
|
-
|
848
|
-
idx = 0
|
834
|
+
response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
|
835
|
+
set_result(selection_result, result_name, response_list, true, is_non_null)
|
836
|
+
idx = nil
|
849
837
|
list_value = begin
|
850
838
|
value.each do |inner_value|
|
839
|
+
idx ||= 0
|
851
840
|
this_idx = idx
|
852
841
|
idx += 1
|
853
842
|
if use_dataloader_job
|
@@ -878,8 +867,9 @@ module GraphQL
|
|
878
867
|
ex_err
|
879
868
|
end
|
880
869
|
end
|
881
|
-
|
882
|
-
|
870
|
+
# Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
|
871
|
+
error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
|
872
|
+
continue_value(list_value, owner_type, field, error_is_non_null, ast_node, result_name, selection_result)
|
883
873
|
else
|
884
874
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
885
875
|
end
|
@@ -891,7 +881,7 @@ module GraphQL
|
|
891
881
|
st.current_result = response_list
|
892
882
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
893
883
|
# This will update `response_list` with the lazy
|
894
|
-
after_lazy(inner_value,
|
884
|
+
after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
|
895
885
|
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
896
886
|
if HALT != continue_value
|
897
887
|
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
@@ -945,7 +935,25 @@ module GraphQL
|
|
945
935
|
end
|
946
936
|
|
947
937
|
def get_current_runtime_state
|
948
|
-
Thread.current[:__graphql_runtime_info] ||=
|
938
|
+
current_state = Thread.current[:__graphql_runtime_info] ||= begin
|
939
|
+
per_query_state = {}
|
940
|
+
per_query_state.compare_by_identity
|
941
|
+
per_query_state
|
942
|
+
end
|
943
|
+
|
944
|
+
current_state[@query] ||= CurrentState.new
|
945
|
+
end
|
946
|
+
|
947
|
+
def minimal_after_lazy(value, &block)
|
948
|
+
if lazy?(value)
|
949
|
+
GraphQL::Execution::Lazy.new do
|
950
|
+
result = @schema.sync_lazy(value)
|
951
|
+
# The returned result might also be lazy, so check it, too
|
952
|
+
minimal_after_lazy(result, &block)
|
953
|
+
end
|
954
|
+
else
|
955
|
+
yield(value)
|
956
|
+
end
|
949
957
|
end
|
950
958
|
|
951
959
|
# @param obj [Object] Some user-returned value that may want to be batched
|
@@ -953,7 +961,7 @@ module GraphQL
|
|
953
961
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
954
962
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
955
963
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
956
|
-
def after_lazy(lazy_obj,
|
964
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
957
965
|
if lazy?(lazy_obj)
|
958
966
|
orig_result = result
|
959
967
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
@@ -967,7 +975,7 @@ module GraphQL
|
|
967
975
|
# but don't wrap the continuation below
|
968
976
|
inner_obj = begin
|
969
977
|
if trace
|
970
|
-
|
978
|
+
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
971
979
|
schema.sync_lazy(lazy_obj)
|
972
980
|
end
|
973
981
|
else
|
@@ -988,7 +996,7 @@ module GraphQL
|
|
988
996
|
if eager
|
989
997
|
lazy.value
|
990
998
|
else
|
991
|
-
set_result(result, result_name, lazy, false)
|
999
|
+
set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
|
992
1000
|
current_depth = 0
|
993
1001
|
while result
|
994
1002
|
current_depth += 1
|
@@ -1013,17 +1021,24 @@ module GraphQL
|
|
1013
1021
|
end
|
1014
1022
|
|
1015
1023
|
def delete_all_interpreter_context
|
1016
|
-
Thread.current[:__graphql_runtime_info]
|
1024
|
+
per_query_state = Thread.current[:__graphql_runtime_info]
|
1025
|
+
if per_query_state
|
1026
|
+
per_query_state.delete(@query)
|
1027
|
+
if per_query_state.size == 0
|
1028
|
+
Thread.current[:__graphql_runtime_info] = nil
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
nil
|
1017
1032
|
end
|
1018
1033
|
|
1019
1034
|
def resolve_type(type, value)
|
1020
|
-
resolved_type, resolved_value =
|
1035
|
+
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
1021
1036
|
query.resolve_type(type, value)
|
1022
1037
|
end
|
1023
1038
|
|
1024
1039
|
if lazy?(resolved_type)
|
1025
1040
|
GraphQL::Execution::Lazy.new do
|
1026
|
-
|
1041
|
+
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
1027
1042
|
schema.sync_lazy(resolved_type)
|
1028
1043
|
end
|
1029
1044
|
end
|
@@ -1037,9 +1052,12 @@ module GraphQL
|
|
1037
1052
|
end
|
1038
1053
|
|
1039
1054
|
def lazy?(object)
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1055
|
+
obj_class = object.class
|
1056
|
+
is_lazy = @lazy_cache[obj_class]
|
1057
|
+
if is_lazy.nil?
|
1058
|
+
is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object)
|
1059
|
+
end
|
1060
|
+
is_lazy
|
1043
1061
|
end
|
1044
1062
|
end
|
1045
1063
|
end
|
@@ -87,7 +87,6 @@ module GraphQL
|
|
87
87
|
|
88
88
|
# Then, work through lazy results in a breadth-first way
|
89
89
|
multiplex.dataloader.append_job {
|
90
|
-
tracer = multiplex
|
91
90
|
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
92
91
|
queries = multiplex ? multiplex.queries : [query]
|
93
92
|
final_values = queries.map do |query|
|
@@ -96,7 +95,7 @@ module GraphQL
|
|
96
95
|
runtime ? runtime.final_result : nil
|
97
96
|
end
|
98
97
|
final_values.compact!
|
99
|
-
|
98
|
+
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
100
99
|
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
101
100
|
end
|
102
101
|
queries.each do |query|
|
@@ -55,7 +55,7 @@ module GraphQL
|
|
55
55
|
@arguments
|
56
56
|
else
|
57
57
|
@arguments = if @field
|
58
|
-
@query.
|
58
|
+
@query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
|
59
59
|
args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
|
60
60
|
end
|
61
61
|
else
|
data/lib/graphql/filter.rb
CHANGED
@@ -6,7 +6,8 @@ module GraphQL
|
|
6
6
|
class Filter
|
7
7
|
def initialize(only: nil, except: nil, silence_deprecation_warning: false)
|
8
8
|
if !silence_deprecation_warning
|
9
|
-
|
9
|
+
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
10
|
+
GraphQL::Deprecation.warn("GraphQL::Filter, `only:`, `except:`, and `.merge_filters` are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
10
11
|
end
|
11
12
|
@only = only
|
12
13
|
@except = except
|
@@ -24,17 +24,24 @@ module GraphQL
|
|
24
24
|
@include_built_in_directives = include_built_in_directives
|
25
25
|
@include_one_of = false
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
|
28
|
+
|
29
|
+
@warden = if only || except
|
30
|
+
filter = GraphQL::Filter
|
31
|
+
.new(only: only, except: except)
|
32
|
+
.merge(only: @schema.method(:visible?))
|
33
|
+
GraphQL::Schema::Warden.new(
|
34
|
+
filter,
|
35
|
+
schema: @schema,
|
36
|
+
context: schema_context,
|
37
|
+
)
|
38
|
+
else
|
39
|
+
@schema.warden_class.new(
|
40
|
+
schema: @schema,
|
41
|
+
context: schema_context,
|
42
|
+
)
|
30
43
|
end
|
31
44
|
|
32
|
-
schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
|
33
|
-
@warden = GraphQL::Schema::Warden.new(
|
34
|
-
filter,
|
35
|
-
schema: @schema,
|
36
|
-
context: schema_context,
|
37
|
-
)
|
38
45
|
schema_context.warden = @warden
|
39
46
|
end
|
40
47
|
|