graphql 2.0.21 → 2.0.22
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/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
|