graphql 2.0.21 → 2.0.23
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.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
|
|