graphql 2.5.22 → 2.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/execution/interpreter/runtime.rb +3 -2
- data/lib/graphql/execution/interpreter.rb +6 -9
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/next/field_resolve_step.rb +93 -61
- data/lib/graphql/execution/next/load_argument_step.rb +5 -1
- data/lib/graphql/execution/next/prepare_object_step.rb +2 -2
- data/lib/graphql/execution/next/runner.rb +48 -26
- data/lib/graphql/execution/next.rb +3 -1
- data/lib/graphql/execution.rb +7 -4
- data/lib/graphql/execution_error.rb +5 -1
- data/lib/graphql/query/context.rb +1 -1
- data/lib/graphql/schema/field.rb +3 -4
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +5 -1
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/resolver.rb +18 -3
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/visibility/profile.rb +68 -49
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +25 -1
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +20 -0
- data/lib/graphql/tracing/perfetto_trace.rb +2 -2
- data/lib/graphql/unauthorized_error.rb +4 -0
- data/lib/graphql/version.rb +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: 724e526bb33dee5a8b39cb04b4d42f2e969e45aafe73581c5bdd02177cfa1347
|
|
4
|
+
data.tar.gz: f211ca87d49c5077b0ae4744ce5956795835a6405a07e8782970cde14d172d7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5d9c249f5d7bbe3ef06070cf52b04586eedf2df06f7c1fc95c6ab47bdc8182218fff2281d76a3f2a18bef4f1ad9c508901e6c7a3f882d28390afdd2f4dca6fd0
|
|
7
|
+
data.tar.gz: 5179a34bc5c34769021fab1dff6384c22f73f776dc994a82f9bce635ddf6ead05ec0a3d2ff37bfcedbc892d87d2d7a82b08eab4e4cf319ee390e2c5a34db76fa
|
|
@@ -603,7 +603,7 @@ module GraphQL
|
|
|
603
603
|
err
|
|
604
604
|
end
|
|
605
605
|
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
606
|
-
elsif GraphQL::Execution::
|
|
606
|
+
elsif value.is_a?(GraphQL::Execution::Skip)
|
|
607
607
|
# It's possible a lazy was already written here
|
|
608
608
|
case selection_result
|
|
609
609
|
when GraphQLResultHash
|
|
@@ -761,7 +761,7 @@ module GraphQL
|
|
|
761
761
|
idx += 1
|
|
762
762
|
if use_dataloader_job
|
|
763
763
|
@dataloader.append_job do
|
|
764
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped,
|
|
764
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, nil)
|
|
765
765
|
end
|
|
766
766
|
else
|
|
767
767
|
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
|
@@ -803,6 +803,7 @@ module GraphQL
|
|
|
803
803
|
end
|
|
804
804
|
|
|
805
805
|
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
806
|
+
runtime_state ||= get_current_runtime_state
|
|
806
807
|
runtime_state.current_result_name = this_idx
|
|
807
808
|
runtime_state.current_result = response_list
|
|
808
809
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
@@ -58,11 +58,6 @@ module GraphQL
|
|
|
58
58
|
# Do as much eager evaluation of the query as possible
|
|
59
59
|
results = []
|
|
60
60
|
queries.each_with_index do |query, idx|
|
|
61
|
-
if query.subscription? && !query.subscription_update?
|
|
62
|
-
subs_namespace = query.context.namespace(:subscriptions)
|
|
63
|
-
subs_namespace[:events] = []
|
|
64
|
-
subs_namespace[:subscriptions] = {}
|
|
65
|
-
end
|
|
66
61
|
multiplex.dataloader.append_job {
|
|
67
62
|
operation = query.selected_operation
|
|
68
63
|
result = if operation.nil? || !query.valid? || !query.context.errors.empty?
|
|
@@ -74,7 +69,9 @@ module GraphQL
|
|
|
74
69
|
# in particular, assign it here:
|
|
75
70
|
runtime = Runtime.new(query: query)
|
|
76
71
|
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
|
77
|
-
|
|
72
|
+
if query.subscription? && !query.subscription_update?
|
|
73
|
+
schema.subscriptions.initialize_subscriptions(query)
|
|
74
|
+
end
|
|
78
75
|
query.current_trace.execute_query(query: query) do
|
|
79
76
|
runtime.run_eager
|
|
80
77
|
end
|
|
@@ -91,9 +88,6 @@ module GraphQL
|
|
|
91
88
|
# Then, find all errors and assign the result to the query object
|
|
92
89
|
results.each_with_index do |data_result, idx|
|
|
93
90
|
query = queries[idx]
|
|
94
|
-
if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
|
|
95
|
-
schema.subscriptions.write_subscription(query, events)
|
|
96
|
-
end
|
|
97
91
|
# Assign the result so that it can be accessed in instrumentation
|
|
98
92
|
query.result_values = if data_result.equal?(NO_OPERATION)
|
|
99
93
|
if !query.valid? || !query.context.errors.empty?
|
|
@@ -103,6 +97,9 @@ module GraphQL
|
|
|
103
97
|
data_result
|
|
104
98
|
end
|
|
105
99
|
else
|
|
100
|
+
if query.subscription?
|
|
101
|
+
schema.subscriptions.finish_subscriptions(query)
|
|
102
|
+
end
|
|
106
103
|
result = {}
|
|
107
104
|
|
|
108
105
|
if !query.context.errors.empty?
|
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
|
38
38
|
# (fewer clauses in a hot `case` block), but now it requires special handling here.
|
|
39
39
|
# I think it's still worth it for the performance win, but if the number of special
|
|
40
40
|
# cases grows, then maybe it's worth rethinking somehow.
|
|
41
|
-
if @value.is_a?(StandardError) &&
|
|
41
|
+
if @value.is_a?(StandardError) && !@value.is_a?(GraphQL::Execution::Skip)
|
|
42
42
|
raise @value
|
|
43
43
|
else
|
|
44
44
|
@value
|
|
@@ -48,59 +48,86 @@ module GraphQL
|
|
|
48
48
|
nil
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def coerce_arguments(argument_owner, ast_arguments_or_hash)
|
|
52
|
-
arg_defns =
|
|
51
|
+
def coerce_arguments(argument_owner, ast_arguments_or_hash, run_loads = true)
|
|
52
|
+
arg_defns = @selections_step.query.types.arguments(argument_owner)
|
|
53
53
|
if arg_defns.empty?
|
|
54
54
|
return EmptyObjects::EMPTY_HASH
|
|
55
55
|
end
|
|
56
56
|
args_hash = {}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
arg_defn = arg_defns.each_value.find { |a|
|
|
61
|
-
a.keyword == key || a.graphql_name == (key_s ||= String(key))
|
|
62
|
-
}
|
|
63
|
-
coerce_argument_value(args_hash, arg_defn, value)
|
|
64
|
-
end
|
|
65
|
-
else
|
|
66
|
-
ast_arguments_or_hash.each { |arg_node|
|
|
67
|
-
arg_defn = arg_defns[arg_node.name]
|
|
68
|
-
coerce_argument_value(args_hash, arg_defn, arg_node.value)
|
|
69
|
-
}
|
|
57
|
+
|
|
58
|
+
if ast_arguments_or_hash.nil? # This can happen with `.trigger`
|
|
59
|
+
return args_hash
|
|
70
60
|
end
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
|
|
62
|
+
arg_inputs_are_h = ast_arguments_or_hash.is_a?(Hash)
|
|
63
|
+
|
|
64
|
+
arg_defns.each do |arg_defn|
|
|
65
|
+
arg_value = nil
|
|
66
|
+
was_found = false
|
|
67
|
+
if arg_inputs_are_h
|
|
68
|
+
ast_arguments_or_hash.each do |key, value|
|
|
69
|
+
if key == arg_defn.keyword || key.to_s == arg_defn.graphql_name
|
|
70
|
+
arg_value = value
|
|
71
|
+
was_found = true
|
|
72
|
+
break
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
ast_arguments_or_hash.each do |arg_node|
|
|
77
|
+
if arg_node.name == arg_defn.graphql_name
|
|
78
|
+
arg_value = arg_node.value
|
|
79
|
+
was_found = true
|
|
80
|
+
break
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if arg_value.is_a?(Language::Nodes::VariableIdentifier)
|
|
86
|
+
vars = @selections_step.query.variables
|
|
87
|
+
arg_value = if vars.key?(arg_value.name)
|
|
88
|
+
vars[arg_value.name]
|
|
89
|
+
elsif vars.key?(arg_value.name.to_sym)
|
|
90
|
+
vars[arg_value.name.to_sym]
|
|
91
|
+
else
|
|
92
|
+
was_found = false
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if !was_found && arg_defn.default_value?
|
|
98
|
+
was_found = true
|
|
99
|
+
arg_value = arg_defn.default_value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if was_found
|
|
103
|
+
coerce_argument_value(args_hash, arg_defn, arg_value, run_loads)
|
|
75
104
|
end
|
|
76
105
|
end
|
|
77
106
|
|
|
78
107
|
args_hash
|
|
79
108
|
end
|
|
80
109
|
|
|
81
|
-
def coerce_argument_value(arguments, arg_defn, arg_value, target_keyword: arg_defn.keyword, as_type: nil)
|
|
110
|
+
def coerce_argument_value(arguments, arg_defn, arg_value, run_loads, target_keyword: run_loads ? arg_defn.keyword : arg_defn.graphql_name, as_type: nil)
|
|
82
111
|
arg_t = as_type || arg_defn.type
|
|
83
112
|
if arg_t.non_null?
|
|
84
113
|
arg_t = arg_t.of_type
|
|
85
114
|
end
|
|
86
115
|
|
|
87
|
-
|
|
116
|
+
if arg_value.is_a?(Language::Nodes::VariableIdentifier)
|
|
88
117
|
vars = @selections_step.query.variables
|
|
89
|
-
if vars.key?(arg_value.name)
|
|
118
|
+
arg_value = if vars.key?(arg_value.name)
|
|
90
119
|
vars[arg_value.name]
|
|
91
120
|
elsif vars.key?(arg_value.name.to_sym)
|
|
92
121
|
vars[arg_value.name.to_sym]
|
|
93
122
|
else
|
|
94
|
-
|
|
123
|
+
nil
|
|
95
124
|
end
|
|
96
|
-
|
|
97
|
-
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if arg_value.is_a?(Language::Nodes::NullValue)
|
|
128
|
+
arg_value = nil
|
|
98
129
|
elsif arg_value.is_a?(Language::Nodes::Enum)
|
|
99
|
-
arg_value.name
|
|
100
|
-
elsif arg_value.is_a?(Language::Nodes::InputObject)
|
|
101
|
-
arg_value.arguments # rubocop:disable Development/ContextIsPassedCop
|
|
102
|
-
else
|
|
103
|
-
arg_value
|
|
130
|
+
arg_value = arg_value.name
|
|
104
131
|
end
|
|
105
132
|
|
|
106
133
|
ctx = @selections_step.query.context
|
|
@@ -111,7 +138,7 @@ module GraphQL
|
|
|
111
138
|
arg_value = Array(arg_value)
|
|
112
139
|
inner_t = arg_t.of_type
|
|
113
140
|
result = Array.new(arg_value.size)
|
|
114
|
-
arg_value.each_with_index { |v, i| coerce_argument_value(result, arg_defn, v, target_keyword: i, as_type: inner_t) }
|
|
141
|
+
arg_value.each_with_index { |v, i| coerce_argument_value(result, arg_defn, v, run_loads, target_keyword: i, as_type: inner_t) }
|
|
115
142
|
result
|
|
116
143
|
end
|
|
117
144
|
elsif arg_t.kind.leaf?
|
|
@@ -125,7 +152,8 @@ module GraphQL
|
|
|
125
152
|
end
|
|
126
153
|
end
|
|
127
154
|
elsif arg_t.kind.input_object?
|
|
128
|
-
|
|
155
|
+
input_obj_vals = arg_value.is_a?(Language::Nodes::InputObject) ? arg_value.arguments : arg_value # rubocop:disable Development/ContextIsPassedCop
|
|
156
|
+
input_obj_args = coerce_arguments(arg_t, input_obj_vals)
|
|
129
157
|
arg_t.new(nil, ruby_kwargs: input_obj_args, context: @selections_step.query.context, defaults_used: nil)
|
|
130
158
|
else
|
|
131
159
|
raise "Unsupported argument value: #{arg_t.to_type_signature} / #{arg_value.class} (#{arg_value.inspect})"
|
|
@@ -145,7 +173,7 @@ module GraphQL
|
|
|
145
173
|
|
|
146
174
|
if arg_value.is_a?(GraphQL::Error)
|
|
147
175
|
@arguments = arg_value
|
|
148
|
-
elsif arg_defn.loads && as_type.nil? && !arg_value.nil?
|
|
176
|
+
elsif run_loads && arg_defn.loads && as_type.nil? && !arg_value.nil?
|
|
149
177
|
# This is for legacy compat:
|
|
150
178
|
load_receiver = if (r = @field_definition.resolver)
|
|
151
179
|
r.new(field: @field_definition, context: @selections_step.query.context, object: nil)
|
|
@@ -252,18 +280,11 @@ module GraphQL
|
|
|
252
280
|
query = @selections_step.query
|
|
253
281
|
field_name = @ast_node.name
|
|
254
282
|
@field_definition = query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}")
|
|
255
|
-
if field_name == "__typename"
|
|
256
|
-
# TODO handle custom introspection
|
|
257
|
-
@field_results = Array.new(@selections_step.objects.size, @parent_type.graphql_name)
|
|
258
|
-
@object_is_authorized = AlwaysAuthorized
|
|
259
|
-
build_results
|
|
260
|
-
return
|
|
261
|
-
end
|
|
262
|
-
|
|
263
283
|
arguments = coerce_arguments(@field_definition, @ast_node.arguments) # rubocop:disable Development/ContextIsPassedCop
|
|
264
284
|
@arguments ||= arguments # may have already been set to an error
|
|
265
285
|
|
|
266
|
-
if @pending_steps.nil? || @pending_steps.size == 0
|
|
286
|
+
if (@pending_steps.nil? || @pending_steps.size == 0) &&
|
|
287
|
+
@field_results.nil? # Make sure the arguments flow didn't already call through
|
|
267
288
|
execute_field
|
|
268
289
|
end
|
|
269
290
|
end
|
|
@@ -323,6 +344,14 @@ module GraphQL
|
|
|
323
344
|
is_authed = @field_definition.authorized?(o, @arguments, ctx)
|
|
324
345
|
if is_authed
|
|
325
346
|
authorized_objects << o
|
|
347
|
+
else
|
|
348
|
+
begin
|
|
349
|
+
err = GraphQL::UnauthorizedFieldError.new(object: o, type: @parent_type, context: ctx, field: @field_definition)
|
|
350
|
+
authorized_objects << query.schema.unauthorized_object(err)
|
|
351
|
+
is_authed = true
|
|
352
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
353
|
+
add_graphql_error(exec_err)
|
|
354
|
+
end
|
|
326
355
|
end
|
|
327
356
|
is_authed
|
|
328
357
|
}
|
|
@@ -616,29 +645,35 @@ module GraphQL
|
|
|
616
645
|
method_receiver = @field_definition.dynamic_introspection ? @field_definition.owner : @parent_type
|
|
617
646
|
case @field_definition.execution_next_mode
|
|
618
647
|
when :resolve_batch
|
|
619
|
-
|
|
620
|
-
method_receiver.public_send(@field_definition.execution_next_mode_key, objects, context)
|
|
621
|
-
else
|
|
648
|
+
begin
|
|
622
649
|
method_receiver.public_send(@field_definition.execution_next_mode_key, objects, context, **args_hash)
|
|
650
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
651
|
+
Array.new(objects.size, exec_err)
|
|
623
652
|
end
|
|
624
653
|
when :resolve_static
|
|
625
|
-
result =
|
|
626
|
-
method_receiver.public_send(@field_definition.execution_next_mode_key, context)
|
|
627
|
-
else
|
|
628
|
-
method_receiver.public_send(@field_definition.execution_next_mode_key, context, **args_hash)
|
|
629
|
-
end
|
|
654
|
+
result = method_receiver.public_send(@field_definition.execution_next_mode_key, context, **args_hash)
|
|
630
655
|
Array.new(objects.size, result)
|
|
631
656
|
when :resolve_each
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
657
|
+
objects.map do |o|
|
|
658
|
+
method_receiver.public_send(@field_definition.execution_next_mode_key, o, context, **args_hash)
|
|
659
|
+
rescue GraphQL::ExecutionError => err
|
|
660
|
+
err
|
|
636
661
|
end
|
|
637
662
|
when :hash_key
|
|
638
663
|
objects.map { |o| o[@field_definition.execution_next_mode_key] }
|
|
639
664
|
when :direct_send
|
|
640
665
|
if args_hash.empty?
|
|
641
|
-
objects.map
|
|
666
|
+
objects.map do |o|
|
|
667
|
+
o.public_send(@field_definition.execution_next_mode_key)
|
|
668
|
+
rescue GraphQL::ExecutionError => err
|
|
669
|
+
err
|
|
670
|
+
rescue StandardError => stderr
|
|
671
|
+
begin
|
|
672
|
+
@selections_step.query.handle_or_reraise(stderr)
|
|
673
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
674
|
+
ex_err
|
|
675
|
+
end
|
|
676
|
+
end
|
|
642
677
|
else
|
|
643
678
|
objects.map { |o| o.public_send(@field_definition.execution_next_mode_key, **args_hash) }
|
|
644
679
|
end
|
|
@@ -684,17 +719,14 @@ module GraphQL
|
|
|
684
719
|
if @field_definition.dynamic_introspection
|
|
685
720
|
obj_inst = @owner.wrap(obj_inst, context)
|
|
686
721
|
end
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
obj_inst.public_send(@field_definition.execution_next_mode_key, **args_hash)
|
|
691
|
-
end
|
|
722
|
+
obj_inst.public_send(@field_definition.execution_next_mode_key, **args_hash)
|
|
723
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
724
|
+
exec_err
|
|
692
725
|
end
|
|
693
726
|
else
|
|
694
727
|
raise "Batching execution for #{path} not implemented (execution_next_mode: #{@execution_next_mode.inspect}); provide `resolve_static:`, `resolve_batch:`, `hash_key:`, `method:`, or use a compatibility plug-in"
|
|
695
728
|
end
|
|
696
729
|
end
|
|
697
|
-
|
|
698
730
|
end
|
|
699
731
|
|
|
700
732
|
class RawValueFieldResolveStep < FieldResolveStep
|
|
@@ -20,7 +20,11 @@ module GraphQL
|
|
|
20
20
|
|
|
21
21
|
def call
|
|
22
22
|
context = @field_resolve_step.selections_step.query.context
|
|
23
|
-
@loaded_value =
|
|
23
|
+
@loaded_value = begin
|
|
24
|
+
@load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
|
|
25
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
26
|
+
context.schema.unauthorized_object(auth_err)
|
|
27
|
+
end
|
|
24
28
|
if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
|
|
25
29
|
runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
|
|
26
30
|
else
|
|
@@ -72,7 +72,7 @@ module GraphQL
|
|
|
72
72
|
begin
|
|
73
73
|
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
74
74
|
@authorized_value = @resolved_type.authorized?(@object, query.context)
|
|
75
|
-
query.current_trace.end_authorized(@
|
|
75
|
+
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
76
76
|
rescue GraphQL::UnauthorizedError => auth_err
|
|
77
77
|
@authorization_error = auth_err
|
|
78
78
|
end
|
|
@@ -83,7 +83,7 @@ module GraphQL
|
|
|
83
83
|
else
|
|
84
84
|
create_result
|
|
85
85
|
end
|
|
86
|
-
rescue GraphQL::
|
|
86
|
+
rescue GraphQL::RuntimeError => err
|
|
87
87
|
@graphql_result[@key] = @field_resolve_step.add_graphql_error(err)
|
|
88
88
|
end
|
|
89
89
|
|
|
@@ -23,9 +23,9 @@ module GraphQL
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def resolve_type(type, object, query)
|
|
26
|
-
query.current_trace.begin_resolve_type(
|
|
26
|
+
query.current_trace.begin_resolve_type(type, object, query.context)
|
|
27
27
|
resolved_type, _ignored_new_value = query.resolve_type(type, object)
|
|
28
|
-
query.current_trace.end_resolve_type(
|
|
28
|
+
query.current_trace.end_resolve_type(type, object, query.context, resolved_type)
|
|
29
29
|
resolved_type
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -140,7 +140,18 @@ module GraphQL
|
|
|
140
140
|
)]
|
|
141
141
|
end
|
|
142
142
|
when "subscription"
|
|
143
|
-
|
|
143
|
+
if !query.subscription_update?
|
|
144
|
+
schema.subscriptions.initialize_subscriptions(query)
|
|
145
|
+
end
|
|
146
|
+
isolated_steps[0] << SelectionsStep.new(
|
|
147
|
+
parent_type: root_type,
|
|
148
|
+
selections: selected_operation.selections,
|
|
149
|
+
objects: [root_value],
|
|
150
|
+
results: [data],
|
|
151
|
+
path: EmptyObjects::EMPTY_ARRAY,
|
|
152
|
+
runner: self,
|
|
153
|
+
query: query,
|
|
154
|
+
)
|
|
144
155
|
else
|
|
145
156
|
raise ArgumentError, "Unhandled operation type: #{operation.operation_type.inspect}"
|
|
146
157
|
end
|
|
@@ -165,6 +176,10 @@ module GraphQL
|
|
|
165
176
|
|
|
166
177
|
queries.each_with_index.map do |query, idx|
|
|
167
178
|
result = results[idx]
|
|
179
|
+
if query.subscription?
|
|
180
|
+
@schema.subscriptions.finish_subscriptions(query)
|
|
181
|
+
end
|
|
182
|
+
|
|
168
183
|
fin_result = if query.context.errors.empty?
|
|
169
184
|
result
|
|
170
185
|
else
|
|
@@ -184,7 +199,8 @@ module GraphQL
|
|
|
184
199
|
res_h
|
|
185
200
|
end
|
|
186
201
|
|
|
187
|
-
|
|
202
|
+
query.result_values = fin_result
|
|
203
|
+
query.result
|
|
188
204
|
end
|
|
189
205
|
end
|
|
190
206
|
ensure
|
|
@@ -241,16 +257,17 @@ module GraphQL
|
|
|
241
257
|
paths_to_check.compact! # root-level auth errors currently come without a path
|
|
242
258
|
# TODO dry with above?
|
|
243
259
|
# This is also where a query-level "Step" would be used?
|
|
244
|
-
selected_operation = query.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
260
|
+
if (selected_operation = query.selected_operation)
|
|
261
|
+
root_type = case selected_operation.operation_type
|
|
262
|
+
when nil, "query"
|
|
263
|
+
query.schema.query
|
|
264
|
+
when "mutation"
|
|
265
|
+
query.schema.mutation
|
|
266
|
+
when "subscription"
|
|
267
|
+
query.schema.subscription
|
|
268
|
+
end
|
|
269
|
+
check_object_result(query, data, root_type, selected_operation.selections, [], [], paths_to_check)
|
|
252
270
|
end
|
|
253
|
-
check_object_result(query, data, root_type, selected_operation.selections, [], [], paths_to_check)
|
|
254
271
|
end
|
|
255
272
|
|
|
256
273
|
def check_object_result(query, result_h, static_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
|
|
@@ -269,22 +286,26 @@ module GraphQL
|
|
|
269
286
|
if (result_type_non_null = result_type.non_null?)
|
|
270
287
|
result_type = result_type.of_type
|
|
271
288
|
end
|
|
289
|
+
|
|
272
290
|
new_result_value = if result_value.is_a?(GraphQL::Error)
|
|
273
291
|
result_value.path = current_result_path.dup
|
|
274
|
-
|
|
292
|
+
result_value.assign_graphql_result(query, result_h, key)
|
|
293
|
+
result_h.key?(key) ? result_h[key] : :unassigned
|
|
275
294
|
else
|
|
276
295
|
if result_type.list?
|
|
277
296
|
check_list_result(query, result_value, result_type.of_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
|
|
278
|
-
elsif result_type.kind.leaf?
|
|
279
|
-
result_value
|
|
280
|
-
else
|
|
297
|
+
elsif !result_type.kind.leaf?
|
|
281
298
|
check_object_result(query, result_value, result_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
|
|
299
|
+
else
|
|
300
|
+
result_value
|
|
282
301
|
end
|
|
283
302
|
end
|
|
284
303
|
|
|
285
304
|
if new_result_value.nil? && result_type_non_null
|
|
286
305
|
return nil
|
|
287
|
-
|
|
306
|
+
elsif :unassigned.equal?(new_result_value)
|
|
307
|
+
# Do nothing
|
|
308
|
+
elsif !new_result_value.equal?(result_value)
|
|
288
309
|
result_h[key] = new_result_value
|
|
289
310
|
end
|
|
290
311
|
end
|
|
@@ -317,24 +338,25 @@ module GraphQL
|
|
|
317
338
|
end
|
|
318
339
|
|
|
319
340
|
new_invalid_null = false
|
|
320
|
-
result_arr.
|
|
341
|
+
result_arr.each_with_index do |result_item, idx|
|
|
321
342
|
current_result_path << idx
|
|
322
343
|
new_result = if result_item.is_a?(GraphQL::Error)
|
|
323
344
|
result_item.path = current_result_path.dup
|
|
324
|
-
|
|
345
|
+
result_item.assign_graphql_result(query, result_arr, idx)
|
|
346
|
+
result_arr[idx]
|
|
325
347
|
elsif inner_type.list?
|
|
326
348
|
check_list_result(query, result_item, inner_type.of_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
|
|
327
|
-
elsif inner_type.kind.leaf?
|
|
328
|
-
result_item
|
|
329
|
-
else
|
|
349
|
+
elsif !inner_type.kind.leaf?
|
|
330
350
|
check_object_result(query, result_item, inner_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
|
|
351
|
+
else
|
|
352
|
+
result_item
|
|
331
353
|
end
|
|
332
354
|
|
|
333
355
|
if new_result.nil? && inner_type_non_null
|
|
334
356
|
new_invalid_null = true
|
|
335
|
-
nil
|
|
336
|
-
|
|
337
|
-
new_result
|
|
357
|
+
result_arr[idx] = nil
|
|
358
|
+
elsif !new_result.equal?(result_item)
|
|
359
|
+
result_arr[idx] = new_result
|
|
338
360
|
end
|
|
339
361
|
ensure
|
|
340
362
|
current_result_path.pop
|
|
@@ -52,7 +52,7 @@ module GraphQL
|
|
|
52
52
|
|
|
53
53
|
def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
54
54
|
queries = query_options.map do |opts|
|
|
55
|
-
case opts
|
|
55
|
+
query = case opts
|
|
56
56
|
when Hash
|
|
57
57
|
schema.query_class.new(schema, nil, **opts)
|
|
58
58
|
when GraphQL::Query, GraphQL::Query::Partial
|
|
@@ -60,6 +60,8 @@ module GraphQL
|
|
|
60
60
|
else
|
|
61
61
|
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
|
|
62
62
|
end
|
|
63
|
+
query.context[:__graphql_execute_next] = true
|
|
64
|
+
query
|
|
63
65
|
end
|
|
64
66
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
65
67
|
runner = Runner.new(multiplex, **schema.execution_next_options)
|
data/lib/graphql/execution.rb
CHANGED
|
@@ -10,10 +10,13 @@ require "graphql/execution/errors"
|
|
|
10
10
|
module GraphQL
|
|
11
11
|
module Execution
|
|
12
12
|
# @api private
|
|
13
|
-
class Skip < GraphQL::
|
|
13
|
+
class Skip < GraphQL::RuntimeError
|
|
14
|
+
attr_accessor :path
|
|
15
|
+
def ast_nodes=(_ignored); end
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
def assign_graphql_result(query, result_data, key)
|
|
18
|
+
result_data.delete(key)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
18
21
|
end
|
|
19
22
|
end
|
|
@@ -6,7 +6,7 @@ module GraphQL
|
|
|
6
6
|
class ExecutionError < GraphQL::RuntimeError
|
|
7
7
|
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
|
|
8
8
|
def ast_node
|
|
9
|
-
ast_nodes
|
|
9
|
+
ast_nodes&.first
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def ast_node=(new_node)
|
|
@@ -36,6 +36,10 @@ module GraphQL
|
|
|
36
36
|
super(message)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def assign_graphql_result(query, result_data, key)
|
|
40
|
+
result_data[key] = nil
|
|
41
|
+
end
|
|
42
|
+
|
|
39
43
|
# @return [Hash] An entry for the response's "errors" key
|
|
40
44
|
def to_h
|
|
41
45
|
hash = {
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -665,10 +665,9 @@ module GraphQL
|
|
|
665
665
|
end
|
|
666
666
|
|
|
667
667
|
def authorizes?(context)
|
|
668
|
-
method(:authorized?).owner != GraphQL::Schema::Field ||
|
|
669
|
-
(args = context.types.arguments(self)) &&
|
|
670
|
-
|
|
671
|
-
)
|
|
668
|
+
method(:authorized?).owner != GraphQL::Schema::Field ||
|
|
669
|
+
((args = context.types.arguments(self)) && (args.any? { |a| a.authorizes?(context) })) ||
|
|
670
|
+
(@resolver_class&.authorizes?(context)) || false
|
|
672
671
|
end
|
|
673
672
|
|
|
674
673
|
def authorized?(object, args, context)
|
data/lib/graphql/schema/list.rb
CHANGED
|
@@ -150,10 +150,14 @@ module GraphQL
|
|
|
150
150
|
|
|
151
151
|
def global_id_field(field_name, **kwargs)
|
|
152
152
|
type = self
|
|
153
|
-
field field_name, "ID", **kwargs, null: false
|
|
153
|
+
field field_name, "ID", **kwargs, null: false, resolve_each: true
|
|
154
154
|
define_method(field_name) do
|
|
155
155
|
context.schema.id_from_object(object, type, context)
|
|
156
156
|
end
|
|
157
|
+
|
|
158
|
+
define_singleton_method(field_name) do |object, context|
|
|
159
|
+
context.schema.id_from_object(object, type, context)
|
|
160
|
+
end
|
|
157
161
|
end
|
|
158
162
|
|
|
159
163
|
# @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
|