graphql 2.0.21 → 2.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/execution/interpreter/arguments_cache.rb +31 -30
- data/lib/graphql/execution/interpreter/runtime.rb +97 -71
- 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 +13 -9
- data/lib/graphql/language/lexer.rb +5 -3
- data/lib/graphql/query/context.rb +16 -7
- data/lib/graphql/query/null_context.rb +3 -0
- data/lib/graphql/query.rb +16 -6
- 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 +3 -3
- 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/warden.rb +8 -2
- data/lib/graphql/schema.rb +4 -4
- 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 +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2564f5f0788db163950385832a4255c88b47b72ddb45175ec30f0f42c6dc0b04
|
4
|
+
data.tar.gz: c1760c1a43453ece3baed09bde3e30f6012bd4357ec597781e4616a7c67a2bc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e1d1747ceb4450228b9b7cb9b123f61585fe8eea3d6442627b56818f4a146e2b507e5a0b8665e25334a64dca79eb8a089c2aa0ff3e8d710fb6adec080623da0
|
7
|
+
data.tar.gz: f1ec3b93d97f4546184496aed5bc03d5ba1a5af1655aaeeb40ac20461e7c722431a116ca8e1a019d8b7e7eda59e7748f862985ea1dd131b3005ceb9ca3c9fa7c
|
@@ -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
|
@@ -400,6 +393,12 @@ module GraphQL
|
|
400
393
|
selections_result.merge_into(target_result)
|
401
394
|
end
|
402
395
|
}
|
396
|
+
# Field resolution may pause the fiber,
|
397
|
+
# so it wouldn't get to the `Resolve` call that happens below.
|
398
|
+
# So instead trigger a run from this outer context.
|
399
|
+
if is_eager_selection
|
400
|
+
@dataloader.run
|
401
|
+
end
|
403
402
|
end
|
404
403
|
|
405
404
|
selections_result
|
@@ -441,9 +440,6 @@ module GraphQL
|
|
441
440
|
# the field's return type at this path in order
|
442
441
|
# to propagate `null`
|
443
442
|
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
443
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
448
444
|
st = get_current_runtime_state
|
449
445
|
st.current_field = field_defn
|
@@ -474,7 +470,7 @@ module GraphQL
|
|
474
470
|
end
|
475
471
|
|
476
472
|
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,
|
473
|
+
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
474
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
479
475
|
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
480
476
|
next
|
@@ -552,7 +548,7 @@ module GraphQL
|
|
552
548
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
553
549
|
# Actually call the field resolver and capture the result
|
554
550
|
app_result = begin
|
555
|
-
|
551
|
+
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
556
552
|
field_defn.resolve(object, kwarg_arguments, context)
|
557
553
|
end
|
558
554
|
rescue GraphQL::ExecutionError => err
|
@@ -564,7 +560,7 @@ module GraphQL
|
|
564
560
|
ex_err
|
565
561
|
end
|
566
562
|
end
|
567
|
-
after_lazy(app_result,
|
563
|
+
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
564
|
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
569
565
|
if HALT != continue_value
|
570
566
|
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
@@ -588,13 +584,9 @@ module GraphQL
|
|
588
584
|
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
589
585
|
end
|
590
586
|
|
591
|
-
def set_result(selection_result, result_name, value, is_child_result)
|
587
|
+
def set_result(selection_result, result_name, value, is_child_result, is_non_null)
|
592
588
|
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
|
-
)
|
589
|
+
if value.nil? && is_non_null
|
598
590
|
# This is an invalid nil that should be propagated
|
599
591
|
# One caller of this method passes a block,
|
600
592
|
# namely when application code returns a `nil` to GraphQL and it doesn't belong there.
|
@@ -604,11 +596,12 @@ module GraphQL
|
|
604
596
|
# TODO the code is trying to tell me something.
|
605
597
|
yield if block_given?
|
606
598
|
parent = selection_result.graphql_parent
|
607
|
-
name_in_parent = selection_result.graphql_result_name
|
608
599
|
if parent.nil? # This is a top-level result hash
|
609
600
|
@response = nil
|
610
601
|
else
|
611
|
-
|
602
|
+
name_in_parent = selection_result.graphql_result_name
|
603
|
+
is_non_null_in_parent = selection_result.graphql_is_non_null_in_parent
|
604
|
+
set_result(parent, name_in_parent, nil, false, is_non_null_in_parent)
|
612
605
|
set_graphql_dead(selection_result)
|
613
606
|
end
|
614
607
|
elsif is_child_result
|
@@ -650,13 +643,13 @@ module GraphQL
|
|
650
643
|
case value
|
651
644
|
when nil
|
652
645
|
if is_non_null
|
653
|
-
set_result(selection_result, result_name, nil, false) do
|
646
|
+
set_result(selection_result, result_name, nil, false, is_non_null) do
|
654
647
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
655
648
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
656
649
|
schema.type_error(err, context)
|
657
650
|
end
|
658
651
|
else
|
659
|
-
set_result(selection_result, result_name, nil, false)
|
652
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
660
653
|
end
|
661
654
|
HALT
|
662
655
|
when GraphQL::Error
|
@@ -669,7 +662,7 @@ module GraphQL
|
|
669
662
|
value.ast_node ||= ast_node
|
670
663
|
context.errors << value
|
671
664
|
if selection_result
|
672
|
-
set_result(selection_result, result_name, nil, false)
|
665
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
673
666
|
end
|
674
667
|
end
|
675
668
|
HALT
|
@@ -723,9 +716,9 @@ module GraphQL
|
|
723
716
|
if selection_result
|
724
717
|
if list_type_at_all
|
725
718
|
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
726
|
-
set_result(selection_result, result_name, result_without_errors, false)
|
719
|
+
set_result(selection_result, result_name, result_without_errors, false, is_non_null)
|
727
720
|
else
|
728
|
-
set_result(selection_result, result_name, nil, false)
|
721
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
729
722
|
end
|
730
723
|
end
|
731
724
|
end
|
@@ -735,7 +728,7 @@ module GraphQL
|
|
735
728
|
end
|
736
729
|
when GraphQL::Execution::Interpreter::RawValue
|
737
730
|
# Write raw value directly to the response without resolving nested objects
|
738
|
-
set_result(selection_result, result_name, value.resolve, false)
|
731
|
+
set_result(selection_result, result_name, value.resolve, false, is_non_null)
|
739
732
|
HALT
|
740
733
|
else
|
741
734
|
value
|
@@ -763,11 +756,11 @@ module GraphQL
|
|
763
756
|
rescue StandardError => err
|
764
757
|
schema.handle_or_reraise(context, err)
|
765
758
|
end
|
766
|
-
set_result(selection_result, result_name, r, false)
|
759
|
+
set_result(selection_result, result_name, r, false, is_non_null)
|
767
760
|
r
|
768
761
|
when "UNION", "INTERFACE"
|
769
762
|
resolved_type_or_lazy = resolve_type(current_type, value)
|
770
|
-
after_lazy(resolved_type_or_lazy,
|
763
|
+
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
764
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
772
765
|
resolved_type, resolved_value = resolved_type_result
|
773
766
|
else
|
@@ -781,7 +774,7 @@ module GraphQL
|
|
781
774
|
err_class = current_type::UnresolvedTypeError
|
782
775
|
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
783
776
|
schema.type_error(type_error, context)
|
784
|
-
set_result(selection_result, result_name, nil, false)
|
777
|
+
set_result(selection_result, result_name, nil, false, is_non_null)
|
785
778
|
nil
|
786
779
|
else
|
787
780
|
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 +786,11 @@ module GraphQL
|
|
793
786
|
rescue GraphQL::ExecutionError => err
|
794
787
|
err
|
795
788
|
end
|
796
|
-
after_lazy(object_proxy,
|
789
|
+
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
790
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
798
791
|
if HALT != continue_value
|
799
|
-
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
800
|
-
set_result(selection_result, result_name, response_hash, true)
|
792
|
+
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
793
|
+
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
801
794
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
802
795
|
# There are two possibilities for `gathered_selections`:
|
803
796
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
@@ -809,7 +802,7 @@ module GraphQL
|
|
809
802
|
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
810
803
|
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
811
804
|
if is_selection_array
|
812
|
-
this_result = GraphQLResultHash.new(result_name, selection_result)
|
805
|
+
this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
813
806
|
final_result = response_hash
|
814
807
|
else
|
815
808
|
this_result = response_hash
|
@@ -822,7 +815,11 @@ module GraphQL
|
|
822
815
|
st.current_result_name = nil
|
823
816
|
st.current_result = this_result
|
824
817
|
|
825
|
-
|
818
|
+
# This is a less-frequent case; use a fast check since it's often not there.
|
819
|
+
if (directives = selections[:graphql_directives])
|
820
|
+
selections.delete(:graphql_directives)
|
821
|
+
end
|
822
|
+
call_method_on_directives(:resolve, continue_value, directives) do
|
826
823
|
evaluate_selections(
|
827
824
|
continue_value,
|
828
825
|
current_type,
|
@@ -842,12 +839,12 @@ module GraphQL
|
|
842
839
|
# This is true for objects, unions, and interfaces
|
843
840
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
844
841
|
inner_type_non_null = inner_type.non_null?
|
845
|
-
response_list = GraphQLResultArray.new(result_name, selection_result)
|
846
|
-
response_list
|
847
|
-
|
848
|
-
idx = 0
|
842
|
+
response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
|
843
|
+
set_result(selection_result, result_name, response_list, true, is_non_null)
|
844
|
+
idx = nil
|
849
845
|
list_value = begin
|
850
846
|
value.each do |inner_value|
|
847
|
+
idx ||= 0
|
851
848
|
this_idx = idx
|
852
849
|
idx += 1
|
853
850
|
if use_dataloader_job
|
@@ -878,8 +875,9 @@ module GraphQL
|
|
878
875
|
ex_err
|
879
876
|
end
|
880
877
|
end
|
881
|
-
|
882
|
-
|
878
|
+
# Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
|
879
|
+
error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
|
880
|
+
continue_value(list_value, owner_type, field, error_is_non_null, ast_node, result_name, selection_result)
|
883
881
|
else
|
884
882
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
885
883
|
end
|
@@ -891,7 +889,7 @@ module GraphQL
|
|
891
889
|
st.current_result = response_list
|
892
890
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
893
891
|
# This will update `response_list` with the lazy
|
894
|
-
after_lazy(inner_value,
|
892
|
+
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
893
|
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
896
894
|
if HALT != continue_value
|
897
895
|
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
@@ -945,7 +943,25 @@ module GraphQL
|
|
945
943
|
end
|
946
944
|
|
947
945
|
def get_current_runtime_state
|
948
|
-
Thread.current[:__graphql_runtime_info] ||=
|
946
|
+
current_state = Thread.current[:__graphql_runtime_info] ||= begin
|
947
|
+
per_query_state = {}
|
948
|
+
per_query_state.compare_by_identity
|
949
|
+
per_query_state
|
950
|
+
end
|
951
|
+
|
952
|
+
current_state[@query] ||= CurrentState.new
|
953
|
+
end
|
954
|
+
|
955
|
+
def minimal_after_lazy(value, &block)
|
956
|
+
if lazy?(value)
|
957
|
+
GraphQL::Execution::Lazy.new do
|
958
|
+
result = @schema.sync_lazy(value)
|
959
|
+
# The returned result might also be lazy, so check it, too
|
960
|
+
minimal_after_lazy(result, &block)
|
961
|
+
end
|
962
|
+
else
|
963
|
+
yield(value)
|
964
|
+
end
|
949
965
|
end
|
950
966
|
|
951
967
|
# @param obj [Object] Some user-returned value that may want to be batched
|
@@ -953,7 +969,7 @@ module GraphQL
|
|
953
969
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
954
970
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
955
971
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
956
|
-
def after_lazy(lazy_obj,
|
972
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
957
973
|
if lazy?(lazy_obj)
|
958
974
|
orig_result = result
|
959
975
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
@@ -967,7 +983,7 @@ module GraphQL
|
|
967
983
|
# but don't wrap the continuation below
|
968
984
|
inner_obj = begin
|
969
985
|
if trace
|
970
|
-
|
986
|
+
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
971
987
|
schema.sync_lazy(lazy_obj)
|
972
988
|
end
|
973
989
|
else
|
@@ -988,7 +1004,7 @@ module GraphQL
|
|
988
1004
|
if eager
|
989
1005
|
lazy.value
|
990
1006
|
else
|
991
|
-
set_result(result, result_name, lazy, false)
|
1007
|
+
set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
|
992
1008
|
current_depth = 0
|
993
1009
|
while result
|
994
1010
|
current_depth += 1
|
@@ -1013,17 +1029,24 @@ module GraphQL
|
|
1013
1029
|
end
|
1014
1030
|
|
1015
1031
|
def delete_all_interpreter_context
|
1016
|
-
Thread.current[:__graphql_runtime_info]
|
1032
|
+
per_query_state = Thread.current[:__graphql_runtime_info]
|
1033
|
+
if per_query_state
|
1034
|
+
per_query_state.delete(@query)
|
1035
|
+
if per_query_state.size == 0
|
1036
|
+
Thread.current[:__graphql_runtime_info] = nil
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
nil
|
1017
1040
|
end
|
1018
1041
|
|
1019
1042
|
def resolve_type(type, value)
|
1020
|
-
resolved_type, resolved_value =
|
1043
|
+
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
1021
1044
|
query.resolve_type(type, value)
|
1022
1045
|
end
|
1023
1046
|
|
1024
1047
|
if lazy?(resolved_type)
|
1025
1048
|
GraphQL::Execution::Lazy.new do
|
1026
|
-
|
1049
|
+
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
1027
1050
|
schema.sync_lazy(resolved_type)
|
1028
1051
|
end
|
1029
1052
|
end
|
@@ -1037,9 +1060,12 @@ module GraphQL
|
|
1037
1060
|
end
|
1038
1061
|
|
1039
1062
|
def lazy?(object)
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1063
|
+
obj_class = object.class
|
1064
|
+
is_lazy = @lazy_cache[obj_class]
|
1065
|
+
if is_lazy.nil?
|
1066
|
+
is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object)
|
1067
|
+
end
|
1068
|
+
is_lazy
|
1043
1069
|
end
|
1044
1070
|
end
|
1045
1071
|
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,21 @@ 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
|
+
GraphQL::Schema::Warden.new(schema: @schema, context: schema_context)
|
30
40
|
end
|
31
41
|
|
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
42
|
schema_context.warden = @warden
|
39
43
|
end
|
40
44
|
|
@@ -95,12 +95,14 @@ module GraphQL
|
|
95
95
|
previous_token: nil,
|
96
96
|
}
|
97
97
|
|
98
|
-
|
99
|
-
|
98
|
+
value = string.dup.force_encoding(Encoding::UTF_8)
|
99
|
+
|
100
|
+
unless value.valid_encoding?
|
101
|
+
emit(:BAD_UNICODE_ESCAPE, 0, 0, meta, value)
|
100
102
|
return meta[:tokens]
|
101
103
|
end
|
102
104
|
|
103
|
-
scan = StringScanner.new
|
105
|
+
scan = StringScanner.new value
|
104
106
|
|
105
107
|
while !scan.eos?
|
106
108
|
pos = scan.pos
|
@@ -227,7 +227,8 @@ module GraphQL
|
|
227
227
|
current_path
|
228
228
|
else
|
229
229
|
(current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
|
230
|
-
(current_runtime_state
|
230
|
+
(query_runtime_state = current_runtime_state[@query]) &&
|
231
|
+
(query_runtime_state.public_send(key))
|
231
232
|
end
|
232
233
|
else
|
233
234
|
# not found
|
@@ -237,10 +238,12 @@ module GraphQL
|
|
237
238
|
|
238
239
|
def current_path
|
239
240
|
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
240
|
-
|
241
|
-
|
241
|
+
query_runtime_state = current_runtime_state && current_runtime_state[@query]
|
242
|
+
|
243
|
+
path = query_runtime_state &&
|
244
|
+
(result = query_runtime_state.current_result) &&
|
242
245
|
(result.path)
|
243
|
-
if path && (rn =
|
246
|
+
if path && (rn = query_runtime_state.current_result_name)
|
244
247
|
path = path.dup
|
245
248
|
path.push(rn)
|
246
249
|
end
|
@@ -260,7 +263,8 @@ module GraphQL
|
|
260
263
|
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
261
264
|
if RUNTIME_METADATA_KEYS.include?(key)
|
262
265
|
(runtime = Thread.current[:__graphql_runtime_info]) &&
|
263
|
-
(runtime
|
266
|
+
(query_runtime_state = runtime[@query]) &&
|
267
|
+
(query_runtime_state.public_send(key))
|
264
268
|
elsif @scoped_context.key?(key)
|
265
269
|
scoped_context[key]
|
266
270
|
elsif @provided_values.key?(key)
|
@@ -277,8 +281,13 @@ module GraphQL
|
|
277
281
|
def dig(key, *other_keys)
|
278
282
|
if RUNTIME_METADATA_KEYS.include?(key)
|
279
283
|
(current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
|
280
|
-
(
|
281
|
-
obj.
|
284
|
+
(query_runtime_state = current_runtime_state[@query]) &&
|
285
|
+
(obj = query_runtime_state.public_send(key)) &&
|
286
|
+
if other_keys.empty?
|
287
|
+
obj
|
288
|
+
else
|
289
|
+
obj.dig(*other_keys)
|
290
|
+
end
|
282
291
|
elsif @scoped_context.key?(key)
|
283
292
|
@scoped_context.dig(key, *other_keys)
|
284
293
|
else
|
data/lib/graphql/query.rb
CHANGED
@@ -87,7 +87,9 @@ module GraphQL
|
|
87
87
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
88
88
|
variables ||= {}
|
89
89
|
@schema = schema
|
90
|
-
|
90
|
+
if only || except
|
91
|
+
merge_filters(except: except, only: only)
|
92
|
+
end
|
91
93
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
92
94
|
@warden = warden
|
93
95
|
@subscription_topic = subscription_topic
|
@@ -151,11 +153,6 @@ module GraphQL
|
|
151
153
|
|
152
154
|
@result_values = nil
|
153
155
|
@executed = false
|
154
|
-
|
155
|
-
# TODO add a general way to define schema-level filters
|
156
|
-
if @schema.respond_to?(:visible?)
|
157
|
-
merge_filters(only: @schema.method(:visible?))
|
158
|
-
end
|
159
156
|
end
|
160
157
|
|
161
158
|
# If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
|
@@ -347,6 +344,7 @@ module GraphQL
|
|
347
344
|
if @prepared_ast
|
348
345
|
raise "Can't add filters after preparing the query"
|
349
346
|
else
|
347
|
+
@filter ||= @schema.default_filter
|
350
348
|
@filter = @filter.merge(only: only, except: except)
|
351
349
|
end
|
352
350
|
nil
|
@@ -361,6 +359,18 @@ module GraphQL
|
|
361
359
|
schema.handle_or_reraise(context, err)
|
362
360
|
end
|
363
361
|
|
362
|
+
def after_lazy(value, &block)
|
363
|
+
if !defined?(@runtime_instance)
|
364
|
+
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
365
|
+
end
|
366
|
+
|
367
|
+
if @runtime_instance
|
368
|
+
@runtime_instance.minimal_after_lazy(value, &block)
|
369
|
+
else
|
370
|
+
@schema.after_lazy(value, &block)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
364
374
|
private
|
365
375
|
|
366
376
|
def find_operation(operations, operation_name)
|
@@ -198,8 +198,8 @@ module GraphQL
|
|
198
198
|
|
199
199
|
def statically_coercible?
|
200
200
|
return @statically_coercible if defined?(@statically_coercible)
|
201
|
-
|
202
|
-
@statically_coercible =
|
201
|
+
requires_parent_object = @prepare.is_a?(String) || @prepare.is_a?(Symbol) || @own_validators
|
202
|
+
@statically_coercible = !requires_parent_object
|
203
203
|
end
|
204
204
|
|
205
205
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
@@ -264,7 +264,7 @@ module GraphQL
|
|
264
264
|
|
265
265
|
# If this isn't lazy, then the block returns eagerly and assigns the result here
|
266
266
|
# If it _is_ lazy, then we write the lazy to the hash, then update it later
|
267
|
-
argument_values[arg_key] = context.
|
267
|
+
argument_values[arg_key] = context.query.after_lazy(coerced_value) do |resolved_coerced_value|
|
268
268
|
owner.validate_directive_argument(self, resolved_coerced_value)
|
269
269
|
prepared_value = begin
|
270
270
|
prepare_value(parent_object, resolved_coerced_value, context: context)
|
@@ -281,7 +281,7 @@ module GraphQL
|
|
281
281
|
end
|
282
282
|
|
283
283
|
maybe_loaded_value = loaded_value || prepared_value
|
284
|
-
context.
|
284
|
+
context.query.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
|
285
285
|
# TODO code smell to access such a deeply-nested constant in a distant module
|
286
286
|
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
|
287
287
|
value: resolved_loaded_value,
|
@@ -303,7 +303,7 @@ module GraphQL
|
|
303
303
|
else
|
304
304
|
load_method_owner.public_send(arg_load_method, coerced_value)
|
305
305
|
end
|
306
|
-
context.
|
306
|
+
context.query.after_lazy(custom_loaded_value) do |custom_value|
|
307
307
|
if loads
|
308
308
|
if type.list?
|
309
309
|
loaded_values = custom_value.each_with_index.map { |custom_val, idx|
|
@@ -34,7 +34,7 @@ module GraphQL
|
|
34
34
|
@graphql_name = graphql_name.to_s
|
35
35
|
GraphQL::NameValidator.validate!(@graphql_name)
|
36
36
|
@description = desc || description
|
37
|
-
@value = value
|
37
|
+
@value = value == NOT_CONFIGURED ? @graphql_name : value
|
38
38
|
if deprecation_reason
|
39
39
|
self.deprecation_reason = deprecation_reason
|
40
40
|
end
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
# rename some inputs to avoid conflicts inside the block
|
27
27
|
maybe_lazy = value
|
28
28
|
value = nil
|
29
|
-
context.
|
29
|
+
context.query.after_lazy(maybe_lazy) do |resolved_value|
|
30
30
|
value = resolved_value
|
31
31
|
if value.is_a? GraphQL::ExecutionError
|
32
32
|
# This isn't even going to work because context doesn't have ast_node anymore
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -235,7 +235,7 @@ module GraphQL
|
|
235
235
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
236
236
|
|
237
237
|
@description = description
|
238
|
-
@type = @owner_type = @own_validators = @own_directives = @own_arguments = nil # these will be prepared later if necessary
|
238
|
+
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
239
239
|
|
240
240
|
self.deprecation_reason = deprecation_reason
|
241
241
|
|
@@ -661,7 +661,7 @@ module GraphQL
|
|
661
661
|
|
662
662
|
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
663
663
|
|
664
|
-
query_ctx.
|
664
|
+
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
665
665
|
if is_authorized
|
666
666
|
with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
|
667
667
|
method_args = ruby_kwargs
|
@@ -833,7 +833,7 @@ ERR
|
|
833
833
|
extended_args = extended[:args]
|
834
834
|
memos = extended[:memos] || EMPTY_HASH
|
835
835
|
|
836
|
-
ctx.
|
836
|
+
ctx.query.after_lazy(value) do |resolved_value|
|
837
837
|
idx = 0
|
838
838
|
@extensions.each do |ext|
|
839
839
|
memo = memos[idx]
|
@@ -211,7 +211,7 @@ module GraphQL
|
|
211
211
|
|
212
212
|
arguments = coerce_arguments(nil, value, ctx)
|
213
213
|
|
214
|
-
ctx.
|
214
|
+
ctx.query.after_lazy(arguments) do |resolved_arguments|
|
215
215
|
if resolved_arguments.is_a?(GraphQL::Error)
|
216
216
|
raise resolved_arguments
|
217
217
|
else
|
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
52
|
def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
|
53
53
|
argument = get_argument("#{arg_defn.graphql_name}")
|
54
|
-
(context || self.context).
|
54
|
+
(context || self.context).query.after_lazy(values) do |values2|
|
55
55
|
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
|
56
56
|
end
|
57
57
|
end
|
@@ -320,9 +320,11 @@ module GraphQL
|
|
320
320
|
end
|
321
321
|
|
322
322
|
def arguments_statically_coercible?
|
323
|
-
|
324
|
-
|
325
|
-
|
323
|
+
if defined?(@arguments_statically_coercible) && !@arguments_statically_coercible.nil?
|
324
|
+
@arguments_statically_coercible
|
325
|
+
else
|
326
|
+
@arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
|
327
|
+
end
|
326
328
|
end
|
327
329
|
|
328
330
|
module ArgumentClassAccessor
|
@@ -363,7 +365,7 @@ module GraphQL
|
|
363
365
|
end
|
364
366
|
|
365
367
|
def authorize_application_object(argument, id, context, loaded_application_object)
|
366
|
-
context.
|
368
|
+
context.query.after_lazy(loaded_application_object) do |application_object|
|
367
369
|
if application_object.nil?
|
368
370
|
err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
369
371
|
load_application_object_failed(err)
|
@@ -371,7 +373,7 @@ module GraphQL
|
|
371
373
|
# Double-check that the located object is actually of this type
|
372
374
|
# (Don't want to allow arbitrary access to objects this way)
|
373
375
|
maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
|
374
|
-
context.
|
376
|
+
context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
|
375
377
|
if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
|
376
378
|
application_object_type, application_object = resolve_type_result
|
377
379
|
else
|
@@ -386,7 +388,7 @@ module GraphQL
|
|
386
388
|
# This object was loaded successfully
|
387
389
|
# and resolved to the right type,
|
388
390
|
# now apply the `.authorized?` class method if there is one
|
389
|
-
context.
|
391
|
+
context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
|
390
392
|
if authed
|
391
393
|
application_object
|
392
394
|
else
|
@@ -57,12 +57,14 @@ module GraphQL
|
|
57
57
|
query_root = Class.new(GraphQL::Schema::Object) do
|
58
58
|
graphql_name "Root"
|
59
59
|
field :throwaway_field, String
|
60
|
+
def self.visible?(ctx)
|
61
|
+
false
|
62
|
+
end
|
60
63
|
end
|
61
64
|
schema = Class.new(GraphQL::Schema) { query(query_root) }
|
62
65
|
|
63
66
|
introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
64
67
|
schema,
|
65
|
-
except: ->(member, _) { member.graphql_name == "Root" },
|
66
68
|
include_introspection_types: true,
|
67
69
|
include_built_in_directives: true,
|
68
70
|
).document
|
@@ -60,7 +60,7 @@ module GraphQL
|
|
60
60
|
super()
|
61
61
|
end
|
62
62
|
|
63
|
-
context.
|
63
|
+
context.query.after_lazy(return_value) do |return_hash|
|
64
64
|
# It might be an error
|
65
65
|
if return_hash.is_a?(Hash)
|
66
66
|
return_hash[:client_mutation_id] = client_mutation_id
|
@@ -70,7 +70,7 @@ module GraphQL
|
|
70
70
|
else
|
71
71
|
ready?
|
72
72
|
end
|
73
|
-
context.
|
73
|
+
context.query.after_lazy(ready_val) do |is_ready, ready_early_return|
|
74
74
|
if ready_early_return
|
75
75
|
if is_ready != false
|
76
76
|
raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{ready_early_return.inspect}]"
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
81
81
|
# Then call each prepare hook, which may return a different value
|
82
82
|
# for that argument, or may return a lazy object
|
83
83
|
load_arguments_val = load_arguments(args)
|
84
|
-
context.
|
84
|
+
context.query.after_lazy(load_arguments_val) do |loaded_args|
|
85
85
|
@prepared_arguments = loaded_args
|
86
86
|
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
87
87
|
# Then call `authorized?`, which may raise or may return a lazy object
|
@@ -90,7 +90,7 @@ module GraphQL
|
|
90
90
|
else
|
91
91
|
authorized?
|
92
92
|
end
|
93
|
-
context.
|
93
|
+
context.query.after_lazy(authorized_val) do |(authorized_result, early_return)|
|
94
94
|
# If the `authorized?` returned two values, `false, early_return`,
|
95
95
|
# then use the early return value instead of continuing
|
96
96
|
if early_return
|
@@ -185,7 +185,7 @@ module GraphQL
|
|
185
185
|
if arg_defn
|
186
186
|
prepped_value = prepared_args[key] = arg_defn.load_and_authorize_value(self, value, context)
|
187
187
|
if context.schema.lazy?(prepped_value)
|
188
|
-
prepare_lazies << context.
|
188
|
+
prepare_lazies << context.query.after_lazy(prepped_value) do |finished_prepped_value|
|
189
189
|
prepared_args[key] = finished_prepped_value
|
190
190
|
end
|
191
191
|
end
|
@@ -90,14 +90,20 @@ module GraphQL
|
|
90
90
|
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
91
91
|
# @param context [GraphQL::Query::Context]
|
92
92
|
# @param schema [GraphQL::Schema]
|
93
|
-
def initialize(filter, context:, schema:)
|
93
|
+
def initialize(filter = nil, context:, schema:)
|
94
94
|
@schema = schema
|
95
95
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
96
96
|
@query = @schema.query
|
97
97
|
@mutation = @schema.mutation
|
98
98
|
@subscription = @schema.subscription
|
99
99
|
@context = context
|
100
|
-
@visibility_cache =
|
100
|
+
@visibility_cache = if filter
|
101
|
+
read_through { |m| filter.call(m, context) }
|
102
|
+
else
|
103
|
+
read_through { |m| schema.visible?(m, context) }
|
104
|
+
end
|
105
|
+
|
106
|
+
@visibility_cache.compare_by_identity
|
101
107
|
# Initialize all ivars to improve object shape consistency:
|
102
108
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
103
109
|
@visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
|
data/lib/graphql/schema.rb
CHANGED
@@ -244,11 +244,13 @@ module GraphQL
|
|
244
244
|
end
|
245
245
|
|
246
246
|
def default_filter
|
247
|
-
GraphQL::Filter.new(except: default_mask
|
247
|
+
GraphQL::Filter.new(except: default_mask)
|
248
248
|
end
|
249
249
|
|
250
250
|
def default_mask(new_mask = nil)
|
251
251
|
if new_mask
|
252
|
+
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
253
|
+
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask 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}")
|
252
254
|
@own_default_mask = new_mask
|
253
255
|
else
|
254
256
|
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
@@ -988,9 +990,7 @@ module GraphQL
|
|
988
990
|
end
|
989
991
|
|
990
992
|
def new_trace(**options)
|
991
|
-
|
992
|
-
options = trace_options.merge(options)
|
993
|
-
end
|
993
|
+
options = trace_options.merge(options)
|
994
994
|
trace_mode = if (target = options[:query] || options[:multiplex]) && target.context[:backtrace]
|
995
995
|
:default_backtrace
|
996
996
|
else
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "graphql/tracing/platform_trace"
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
module Tracing
|
5
7
|
# This implementation forwards events to a notification handler (i.e.
|
@@ -20,6 +22,7 @@ module GraphQL
|
|
20
22
|
"validate" => "validate.graphql",
|
21
23
|
"analyze_multiplex" => "analyze_multiplex.graphql",
|
22
24
|
"analyze_query" => "analyze_query.graphql",
|
25
|
+
"execute_multiplex" => "execute_multiplex.graphql",
|
23
26
|
"execute_query" => "execute_query.graphql",
|
24
27
|
"execute_query_lazy" => "execute_query_lazy.graphql",
|
25
28
|
"execute_field" => "execute_field.graphql",
|
data/lib/graphql/tracing.rb
CHANGED
@@ -14,6 +14,7 @@ require "graphql/tracing/statsd_tracing"
|
|
14
14
|
require "graphql/tracing/prometheus_tracing"
|
15
15
|
|
16
16
|
# New Tracing:
|
17
|
+
require "graphql/tracing/active_support_notifications_trace"
|
17
18
|
require "graphql/tracing/platform_trace"
|
18
19
|
require "graphql/tracing/appoptics_trace"
|
19
20
|
require "graphql/tracing/appsignal_trace"
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -44,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
|
|
44
44
|
|
45
45
|
## Getting Involved
|
46
46
|
|
47
|
-
- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9)
|
47
|
+
- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9).
|
48
48
|
- __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
|
49
49
|
- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -622,7 +622,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
622
622
|
- !ruby/object:Gem::Version
|
623
623
|
version: '0'
|
624
624
|
requirements: []
|
625
|
-
rubygems_version: 3.4.
|
625
|
+
rubygems_version: 3.4.13
|
626
626
|
signing_key:
|
627
627
|
specification_version: 4
|
628
628
|
summary: A GraphQL language and runtime for Ruby
|