graphql 2.6.1 → 2.6.3
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/analysis/query_complexity.rb +29 -13
- data/lib/graphql/backtrace/table.rb +10 -1
- data/lib/graphql/current.rb +7 -1
- data/lib/graphql/dataloader.rb +1 -1
- data/lib/graphql/execution/directive_checks.rb +2 -0
- data/lib/graphql/execution/field_resolve_step.rb +178 -65
- data/lib/graphql/execution/finalize.rb +21 -8
- data/lib/graphql/execution/input_values.rb +110 -38
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
- data/lib/graphql/execution/interpreter/runtime.rb +36 -15
- data/lib/graphql/execution/load_argument_step.rb +41 -3
- data/lib/graphql/execution/next.rb +20 -12
- data/lib/graphql/execution/prepare_object_step.rb +24 -5
- data/lib/graphql/execution/resolve_type_step.rb +27 -0
- data/lib/graphql/execution/runner.rb +65 -30
- data/lib/graphql/execution/selections_step.rb +1 -1
- data/lib/graphql/execution.rb +8 -1
- data/lib/graphql/execution_error.rb +6 -12
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +6 -2
- data/lib/graphql/language/lexer.rb +1 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language.rb +8 -2
- data/lib/graphql/pagination/connections.rb +1 -3
- data/lib/graphql/query.rb +2 -2
- data/lib/graphql/schema/argument.rb +3 -3
- data/lib/graphql/schema/directive/feature.rb +4 -0
- data/lib/graphql/schema/directive/transform.rb +20 -0
- data/lib/graphql/schema/has_single_input_argument.rb +24 -13
- data/lib/graphql/schema/input_object.rb +4 -0
- data/lib/graphql/schema/interface.rb +1 -1
- data/lib/graphql/schema/introspection_system.rb +6 -21
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/ractor_shareable.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
- data/lib/graphql/schema/resolver.rb +0 -7
- data/lib/graphql/schema/subscription.rb +53 -8
- data/lib/graphql/schema/timeout.rb +2 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
- data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
- data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/format_validator.rb +3 -3
- data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/length_validator.rb +6 -6
- data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
- data/lib/graphql/schema/validator/required_validator.rb +6 -4
- data/lib/graphql/schema/validator.rb +9 -0
- data/lib/graphql/schema/visibility/profile.rb +6 -4
- data/lib/graphql/schema/visibility/visit.rb +1 -1
- data/lib/graphql/schema/visibility.rb +30 -22
- data/lib/graphql/schema.rb +31 -10
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +6 -0
- data/lib/graphql/subscriptions/event.rb +0 -1
- data/lib/graphql/tracing/perfetto_trace.rb +5 -3
- data/lib/graphql/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16abc9c2a5eda0da251dbe7e14433241bc7d038d2527926832ceb29336fb857a
|
|
4
|
+
data.tar.gz: c89bd2a4b340ca30bfa7b823f51e4671972355d54bd56fdaed598c13a415c3b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6452d2a517c502b4a8934582d060885a5f6462de244a1f331433f1076dc57a879cdd081e147f70925064a99607e0794fcf6eadad7a3f4a9111a5ab979c552554
|
|
7
|
+
data.tar.gz: b6a0219606d905fc885ab2192596d1acb1d92bce407defc751bfafb1a7199e9c32275c1d3202f3246251e72b80554698b1f54920f76178f1ddc81b41d628bacd
|
|
@@ -9,6 +9,8 @@ module GraphQL
|
|
|
9
9
|
super
|
|
10
10
|
@skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields
|
|
11
11
|
@complexities_on_type_by_query = {}
|
|
12
|
+
@intersect_cache = Hash.new { |h, k| h[k] = {}.compare_by_identity }.compare_by_identity
|
|
13
|
+
@possible_types_cache = {}.compare_by_identity
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
# Override this method to use the complexity result
|
|
@@ -159,8 +161,22 @@ module GraphQL
|
|
|
159
161
|
|
|
160
162
|
def types_intersect?(query, a, b)
|
|
161
163
|
return true if a == b
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
|
|
165
|
+
if a.object_id < b.object_id
|
|
166
|
+
first_cache = @intersect_cache[a]
|
|
167
|
+
second_key = b
|
|
168
|
+
else
|
|
169
|
+
first_cache = @intersect_cache[b]
|
|
170
|
+
second_key = a
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if first_cache.key?(second_key)
|
|
174
|
+
first_cache[second_key]
|
|
175
|
+
else
|
|
176
|
+
a_types = @possible_types_cache[a] ||= query.types.possible_types(a).to_set
|
|
177
|
+
b_types = @possible_types_cache[b] ||= query.types.possible_types(b).to_set
|
|
178
|
+
first_cache[second_key] = a_types.intersect?(b_types)
|
|
179
|
+
end
|
|
164
180
|
end
|
|
165
181
|
|
|
166
182
|
# A hook which is called whenever a field's max complexity is calculated.
|
|
@@ -175,18 +191,16 @@ module GraphQL
|
|
|
175
191
|
# @param inner_selections [Array<Hash<String, ScopedTypeComplexity>>] Field selections for a scope
|
|
176
192
|
# @return [Integer] Total complexity value for all these selections in the parent scope
|
|
177
193
|
def merged_max_complexity(query, inner_selections)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
194
|
+
child_scopes_by_key = {}
|
|
195
|
+
inner_selections.each do |inner_selection|
|
|
196
|
+
inner_selection.each do |k, v|
|
|
197
|
+
scopes = child_scopes_by_key[k] ||= []
|
|
198
|
+
scopes << v
|
|
199
|
+
end
|
|
182
200
|
end
|
|
183
|
-
|
|
184
201
|
# Add up the total cost for each unique field name's coalesced selections
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# all keys come with at least one scope.
|
|
188
|
-
child_scopes = inner_selections.filter_map { _1[field_key] }
|
|
189
|
-
|
|
202
|
+
total = 0
|
|
203
|
+
child_scopes_by_key.each do |field_key, child_scopes|
|
|
190
204
|
# Compute maximum possible cost of child selections;
|
|
191
205
|
# composites merge their maximums, while leaf scopes are always zero.
|
|
192
206
|
# FieldsWillMerge validation assures all scopes are uniformly composite or leaf.
|
|
@@ -214,8 +228,10 @@ module GraphQL
|
|
|
214
228
|
child_complexity: maximum_children_cost,
|
|
215
229
|
)
|
|
216
230
|
|
|
217
|
-
total
|
|
231
|
+
total += maximum_cost
|
|
218
232
|
end
|
|
233
|
+
|
|
234
|
+
total
|
|
219
235
|
end
|
|
220
236
|
|
|
221
237
|
def legacy_merged_max_complexity(query, inner_selections)
|
|
@@ -90,7 +90,16 @@ module GraphQL
|
|
|
90
90
|
|
|
91
91
|
if ast_node
|
|
92
92
|
field_defn = query.get_field(result.graphql_result_type, ast_node.name)
|
|
93
|
-
args =
|
|
93
|
+
args = begin
|
|
94
|
+
if (cached_args = query.arguments_cache.cached_arguments_for(ast_node, field_defn))
|
|
95
|
+
cached_args.to_h
|
|
96
|
+
else
|
|
97
|
+
EmptyObjects::EMPTY_HASH
|
|
98
|
+
end
|
|
99
|
+
rescue StandardError => err
|
|
100
|
+
"Failed to load arguments, #{err.class}: #{err.message}"
|
|
101
|
+
end
|
|
102
|
+
|
|
94
103
|
field_path = field_defn.path
|
|
95
104
|
if ast_node.alias
|
|
96
105
|
field_path += " as #{ast_node.alias}"
|
data/lib/graphql/current.rb
CHANGED
|
@@ -41,7 +41,13 @@ module GraphQL
|
|
|
41
41
|
# @see GraphQL::Field#path for a string identifying this field
|
|
42
42
|
# @return [GraphQL::Field, nil] The currently-running field, if there is one.
|
|
43
43
|
def self.field
|
|
44
|
-
Fiber[:__graphql_runtime_info]
|
|
44
|
+
if (interpreter_info = Fiber[:__graphql_runtime_info])
|
|
45
|
+
interpreter_info.values&.first&.current_field
|
|
46
|
+
elsif (field = Fiber[:__graphql_current_field])
|
|
47
|
+
field
|
|
48
|
+
else
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
# @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -108,7 +108,7 @@ module GraphQL
|
|
|
108
108
|
# @param batch_parameters [Array<Object>]
|
|
109
109
|
# @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
|
|
110
110
|
# and cached for the lifetime of this {Multiplex}.
|
|
111
|
-
if
|
|
111
|
+
if (RUBY_ENGINE == "ruby" && RUBY_ENGINE < "3") || RUBY_ENGINE == "truffleruby" # truffle-ruby wasn't doing well with the implementation below
|
|
112
112
|
def with(source_class, *batch_args)
|
|
113
113
|
batch_key = source_class.batch_key_for(*batch_args)
|
|
114
114
|
@source_cache[source_class][batch_key] ||= begin
|
|
@@ -18,11 +18,13 @@ module GraphQL
|
|
|
18
18
|
case name
|
|
19
19
|
when SKIP
|
|
20
20
|
args = query.arguments_for(directive_ast_node, directive_defn)
|
|
21
|
+
next if args.is_a?(GraphQL::ExecutionError)
|
|
21
22
|
if args[:if] == true
|
|
22
23
|
return false
|
|
23
24
|
end
|
|
24
25
|
when INCLUDE
|
|
25
26
|
args = query.arguments_for(directive_ast_node, directive_defn)
|
|
27
|
+
next if args.is_a?(GraphQL::ExecutionError)
|
|
26
28
|
if args[:if] == false
|
|
27
29
|
return false
|
|
28
30
|
end
|
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
@finish_extension_idx = nil
|
|
22
22
|
@was_scoped = nil
|
|
23
23
|
@pending_steps = nil
|
|
24
|
-
@post_processors = @directive_finalizers = nil
|
|
24
|
+
@arguments_without_loads = @post_processors = @directive_finalizers = nil
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
attr_reader :ast_node, :key, :parent_type, :selections_step, :runner,
|
|
@@ -50,11 +50,14 @@ module GraphQL
|
|
|
50
50
|
|
|
51
51
|
def value
|
|
52
52
|
query = @selections_step.query
|
|
53
|
+
set_current_field
|
|
53
54
|
query.current_trace.begin_execute_field(@field_definition, @arguments, @field_results, query)
|
|
54
55
|
sync(@field_results)
|
|
55
56
|
query.current_trace.end_execute_field(@field_definition, @arguments, @field_results, query, @field_results)
|
|
56
57
|
@runner.add_step(self)
|
|
57
58
|
true
|
|
59
|
+
ensure
|
|
60
|
+
set_current_field(nil)
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def sync(lazy)
|
|
@@ -69,13 +72,14 @@ module GraphQL
|
|
|
69
72
|
err
|
|
70
73
|
rescue StandardError => stderr
|
|
71
74
|
begin
|
|
72
|
-
@selections_step.query.handle_or_reraise(stderr)
|
|
75
|
+
@selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
73
76
|
rescue GraphQL::ExecutionError => ex_err
|
|
74
77
|
ex_err
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
def call
|
|
82
|
+
set_current_field if @field_definition
|
|
79
83
|
if @enqueued_authorization
|
|
80
84
|
enqueue_next_steps
|
|
81
85
|
elsif @finish_extension_idx
|
|
@@ -94,46 +98,73 @@ module GraphQL
|
|
|
94
98
|
else
|
|
95
99
|
raise
|
|
96
100
|
end
|
|
101
|
+
ensure
|
|
102
|
+
set_current_field(nil)
|
|
97
103
|
end
|
|
98
104
|
|
|
99
105
|
def add_graphql_error(err)
|
|
100
106
|
err.path = path
|
|
101
|
-
err.
|
|
107
|
+
if err.ast_node.nil?
|
|
108
|
+
err.ast_nodes = ast_nodes
|
|
109
|
+
end
|
|
102
110
|
@selections_step.query.context.add_error(err)
|
|
103
111
|
err
|
|
104
112
|
end
|
|
105
113
|
|
|
114
|
+
def build_errors_result(errors, single_error)
|
|
115
|
+
first_error = errors.nil? ? single_error : errors.pop
|
|
116
|
+
@field_results = error_instance_array(@selections_step.objects.size, first_error)
|
|
117
|
+
if errors
|
|
118
|
+
errors.each do |e|
|
|
119
|
+
add_graphql_error(e)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
@results ||= @selections_step.results
|
|
123
|
+
build_results
|
|
124
|
+
end
|
|
125
|
+
|
|
106
126
|
def build_arguments
|
|
107
127
|
query = @selections_step.query
|
|
108
128
|
field_name = @ast_node.name
|
|
109
|
-
@field_definition = query.types.field(@parent_type, field_name) || raise("
|
|
110
|
-
|
|
111
|
-
@arguments
|
|
129
|
+
@field_definition = query.types.field(@parent_type, field_name) || raise(GraphQL::Error, "No field definition found for #{@parent_type.to_type_signature}.#{ast_node.name} (at #{@ast_node.position})")
|
|
130
|
+
set_current_field
|
|
131
|
+
@arguments, errors = @runner.input_values[query].argument_values(@field_definition, @ast_node.arguments, self) # rubocop:disable Development/ContextIsPassedCop
|
|
132
|
+
if errors
|
|
133
|
+
build_errors_result(errors, nil)
|
|
134
|
+
return
|
|
135
|
+
end
|
|
112
136
|
|
|
113
137
|
if (@pending_steps.nil? || @pending_steps.size == 0) &&
|
|
114
138
|
@field_results.nil? # Make sure the arguments flow didn't already call through
|
|
115
139
|
execute_field
|
|
116
140
|
end
|
|
141
|
+
ensure
|
|
142
|
+
set_current_field(nil)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Used for compatibility in Schema::Subscription
|
|
146
|
+
def arguments_without_loads
|
|
147
|
+
if @arguments_without_loads.nil?
|
|
148
|
+
@arguments_without_loads, _errors = @runner.input_values[@selections_step.query].argument_values(@field_definition, ast_node.arguments, nil)
|
|
149
|
+
end
|
|
150
|
+
@arguments_without_loads
|
|
117
151
|
end
|
|
118
152
|
|
|
119
153
|
def execute_field
|
|
120
154
|
objects = @selections_step.objects
|
|
121
|
-
@results = @selections_step.results
|
|
122
|
-
# TODO not as good because only one error?
|
|
123
155
|
if @arguments.is_a?(GraphQL::RuntimeError)
|
|
124
|
-
|
|
125
|
-
build_results
|
|
156
|
+
build_errors_result(nil, @arguments)
|
|
126
157
|
return
|
|
127
158
|
end
|
|
128
159
|
|
|
160
|
+
@results = @selections_step.results
|
|
129
161
|
query = @selections_step.query
|
|
130
162
|
ctx = query.context
|
|
131
163
|
if (v = @field_definition.validators).any? # rubocop:disable Development/NoneWithoutBlockCop
|
|
132
164
|
begin
|
|
133
165
|
Schema::Validator.validate!(v, nil, ctx, @arguments)
|
|
134
166
|
rescue GraphQL::RuntimeError => err
|
|
135
|
-
|
|
136
|
-
build_results
|
|
167
|
+
build_errors_result(nil, err)
|
|
137
168
|
return
|
|
138
169
|
end
|
|
139
170
|
end
|
|
@@ -160,25 +191,40 @@ module GraphQL
|
|
|
160
191
|
end
|
|
161
192
|
|
|
162
193
|
if @field_definition.dynamic_introspection
|
|
163
|
-
|
|
164
|
-
objects = @selections_step.graphql_objects
|
|
194
|
+
objects = @selections_step.graphql_objects.map { |o| @field_definition.owner.wrap(o, ctx) }
|
|
165
195
|
end
|
|
166
196
|
|
|
167
|
-
if @runner.
|
|
197
|
+
if @runner.authorizes?(@field_definition, ctx)
|
|
168
198
|
authorized_objects = []
|
|
169
199
|
authorized_results = []
|
|
170
200
|
l = objects.size
|
|
171
201
|
i = 0
|
|
172
202
|
while i < l
|
|
173
203
|
o = objects[i]
|
|
174
|
-
|
|
204
|
+
err = nil
|
|
205
|
+
begin
|
|
206
|
+
field_authed = @field_definition.authorized?(o, @arguments, ctx)
|
|
207
|
+
if @runner.resolves_lazies && @runner.lazy?(field_authed)
|
|
208
|
+
# TODO batch this properly...
|
|
209
|
+
field_authed = sync(field_authed)
|
|
210
|
+
end
|
|
211
|
+
rescue GraphQL::UnauthorizedFieldError => field_auth_err
|
|
212
|
+
err = field_auth_err
|
|
213
|
+
err.field ||= @field_definition
|
|
214
|
+
field_authed = false
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
if field_authed
|
|
175
218
|
authorized_results << @results[i]
|
|
176
219
|
authorized_objects << o
|
|
177
220
|
else
|
|
178
221
|
begin
|
|
179
|
-
err
|
|
180
|
-
|
|
181
|
-
|
|
222
|
+
err ||= GraphQL::UnauthorizedFieldError.new(object: o, type: @parent_type, context: ctx, field: @field_definition)
|
|
223
|
+
new_obj = query.schema.unauthorized_field(err)
|
|
224
|
+
if !new_obj.nil?
|
|
225
|
+
authorized_objects << new_obj
|
|
226
|
+
authorized_results << @results[i]
|
|
227
|
+
end
|
|
182
228
|
rescue GraphQL::ExecutionError => exec_err
|
|
183
229
|
add_graphql_error(exec_err)
|
|
184
230
|
end
|
|
@@ -220,27 +266,37 @@ module GraphQL
|
|
|
220
266
|
if directives
|
|
221
267
|
directives.each do |dir_node|
|
|
222
268
|
if (dir_defn = @runner.runtime_directives[dir_node.name])
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
dir_args, errors = @runner.input_values[query].argument_values(dir_defn, dir_node.arguments, self) # rubocop:disable Development/ContextIsPassedCop
|
|
270
|
+
if errors
|
|
271
|
+
@results.each { |r| r.delete(@key) }
|
|
272
|
+
errors.each { |e| e.ast_node = dir_node }
|
|
273
|
+
build_errors_result(errors, nil)
|
|
274
|
+
return
|
|
275
|
+
else
|
|
276
|
+
begin
|
|
277
|
+
dir_defn.validate!(dir_args, query.context)
|
|
278
|
+
if !(result = dir_defn.resolve_field(ast_nodes, @parent_type, field_definition, authorized_objects, dir_args, ctx)).nil?
|
|
279
|
+
if result.is_a?(Finalizer)
|
|
280
|
+
result.path = path
|
|
281
|
+
@directive_finalizers ||= []
|
|
282
|
+
@directive_finalizers << result
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if result.is_a?(PostProcessor)
|
|
286
|
+
@post_processors ||= []
|
|
287
|
+
@post_processors << result
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
if result.is_a?(HaltExecution)
|
|
291
|
+
@directive_finalizers&.each { |f|
|
|
292
|
+
@selections_step.results.each { |r| @runner.add_finalizer(query, r, key, f) }
|
|
293
|
+
}
|
|
294
|
+
return
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
rescue GraphQL::RuntimeError => err
|
|
298
|
+
err.ast_node = dir_node
|
|
299
|
+
raise
|
|
244
300
|
end
|
|
245
301
|
end
|
|
246
302
|
end
|
|
@@ -267,10 +323,20 @@ module GraphQL
|
|
|
267
323
|
|
|
268
324
|
if any_lazy_results?
|
|
269
325
|
@runner.dataloader.lazy_at_depth(path.size, self)
|
|
270
|
-
elsif has_extensions
|
|
271
|
-
finish_extensions
|
|
272
326
|
elsif @pending_steps.nil? || @pending_steps.empty?
|
|
273
|
-
|
|
327
|
+
if has_extensions
|
|
328
|
+
finish_extensions
|
|
329
|
+
else
|
|
330
|
+
build_results
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
rescue GraphQL::ExecutionError => err
|
|
334
|
+
add_graphql_error(err)
|
|
335
|
+
rescue StandardError => stderr
|
|
336
|
+
begin
|
|
337
|
+
@selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
338
|
+
rescue GraphQL::ExecutionError => err
|
|
339
|
+
add_graphql_error(err)
|
|
274
340
|
end
|
|
275
341
|
end
|
|
276
342
|
|
|
@@ -312,6 +378,8 @@ module GraphQL
|
|
|
312
378
|
conn.was_authorized_by_scope_items = @was_scoped
|
|
313
379
|
end
|
|
314
380
|
conn
|
|
381
|
+
rescue GraphQL::RuntimeError => err
|
|
382
|
+
err
|
|
315
383
|
end
|
|
316
384
|
when Schema::Field::ScopeExtension
|
|
317
385
|
if @was_scoped.nil?
|
|
@@ -390,9 +458,16 @@ module GraphQL
|
|
|
390
458
|
end
|
|
391
459
|
|
|
392
460
|
def finish_leaf_result(result_h, key, field_result, return_type, ctx)
|
|
393
|
-
final_field_result =
|
|
461
|
+
final_field_result = build_leaf_result(field_result, return_type, ctx, false)
|
|
462
|
+
|
|
463
|
+
@directive_finalizers&.each { |f| @runner.add_finalizer(ctx.query, result_h, key, f) }
|
|
464
|
+
result_h[@key] = final_field_result
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def build_leaf_result(field_result, return_type, ctx, is_from_array)
|
|
468
|
+
if field_result.nil?
|
|
394
469
|
if return_type.non_null?
|
|
395
|
-
add_non_null_error(
|
|
470
|
+
add_non_null_error(is_from_array)
|
|
396
471
|
else
|
|
397
472
|
nil
|
|
398
473
|
end
|
|
@@ -403,13 +478,16 @@ module GraphQL
|
|
|
403
478
|
field_result.path = path
|
|
404
479
|
@runner.add_finalizer(ctx.query, result_h, key, field_result)
|
|
405
480
|
end
|
|
481
|
+
elsif return_type.list?
|
|
482
|
+
if return_type.non_null?
|
|
483
|
+
return_type = return_type.of_type
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
inner_type = return_type.of_type
|
|
487
|
+
field_result.map { |item| build_leaf_result(item, inner_type, ctx, true) }
|
|
406
488
|
else
|
|
407
|
-
# TODO `nil`s in [T!] types aren't handled
|
|
408
489
|
return_type.coerce_result(field_result, ctx)
|
|
409
490
|
end
|
|
410
|
-
|
|
411
|
-
@directive_finalizers&.each { |f| @runner.add_finalizer(ctx.query, result_h, key, f) }
|
|
412
|
-
result_h[@key] = final_field_result
|
|
413
491
|
end
|
|
414
492
|
|
|
415
493
|
def enqueue_next_steps
|
|
@@ -427,7 +505,17 @@ module GraphQL
|
|
|
427
505
|
if (object_type = @runner.runtime_type_at[result])
|
|
428
506
|
# OK
|
|
429
507
|
else
|
|
430
|
-
|
|
508
|
+
query.current_trace.begin_resolve_type(@static_type, next_object, query.context)
|
|
509
|
+
object_type = ResolveTypeStep.resolve_type(@static_type, next_object, query)
|
|
510
|
+
if object_type.is_a?(Array)
|
|
511
|
+
object_type, next_object = object_type
|
|
512
|
+
end
|
|
513
|
+
if @runner.resolves_lazies && @runner.lazy?(object_type)
|
|
514
|
+
# TODO batch this
|
|
515
|
+
object_type, next_object = sync(object_type)
|
|
516
|
+
end
|
|
517
|
+
ResolveTypeStep.assert_valid_resolved_type(@static_type, object_type, next_object, self)
|
|
518
|
+
query.current_trace.end_resolve_type(@static_type, next_object, query.context, object_type)
|
|
431
519
|
@runner.runtime_type_at[result] = object_type
|
|
432
520
|
end
|
|
433
521
|
next_objects_by_type[object_type] << next_object
|
|
@@ -469,10 +557,14 @@ module GraphQL
|
|
|
469
557
|
end
|
|
470
558
|
|
|
471
559
|
def add_non_null_error(is_from_array)
|
|
472
|
-
err = InvalidNullError.new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path)
|
|
560
|
+
err = @parent_type::InvalidNullError.new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path)
|
|
473
561
|
@runner.schema.type_error(err, @selections_step.query.context)
|
|
474
562
|
end
|
|
475
563
|
|
|
564
|
+
def set_current_field(new_value = @field_definition)
|
|
565
|
+
Fiber[:__graphql_current_field] = new_value
|
|
566
|
+
end
|
|
567
|
+
|
|
476
568
|
private
|
|
477
569
|
|
|
478
570
|
def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
|
|
@@ -507,13 +599,13 @@ module GraphQL
|
|
|
507
599
|
i += 1
|
|
508
600
|
end
|
|
509
601
|
elsif @runner.resolves_lazies || (
|
|
510
|
-
@
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
602
|
+
@static_type.kind.object? ?
|
|
603
|
+
@runner.authorizes?(@static_type, @selections_step.query.context) :
|
|
604
|
+
(
|
|
605
|
+
(runtime_type, _ignored_new_value = ResolveTypeStep.resolve_type(@static_type, field_result, @selections_step.query)) &&
|
|
606
|
+
(@runner.runtime_type_at[graphql_result] = runtime_type) &&
|
|
607
|
+
@runner.authorizes?(runtime_type, @selections_step.query.context)
|
|
608
|
+
))
|
|
517
609
|
obj_step = PrepareObjectStep.new(
|
|
518
610
|
object: field_result,
|
|
519
611
|
runner: @runner,
|
|
@@ -539,26 +631,46 @@ module GraphQL
|
|
|
539
631
|
end
|
|
540
632
|
|
|
541
633
|
def resolve_batch(objects, context, args_hash)
|
|
542
|
-
|
|
634
|
+
dyn_ins = @field_definition.dynamic_introspection
|
|
635
|
+
method_receiver = dyn_ins ? @field_definition.owner : @parent_type
|
|
543
636
|
case @field_definition.execution_mode
|
|
544
637
|
when :resolve_batch
|
|
545
638
|
begin
|
|
546
639
|
method_receiver.public_send(@field_definition.execution_mode_key, objects, context, **args_hash)
|
|
547
640
|
rescue GraphQL::ExecutionError => exec_err
|
|
548
|
-
|
|
641
|
+
error_instance_array(objects.size, exec_err)
|
|
642
|
+
rescue StandardError => stderr
|
|
643
|
+
begin
|
|
644
|
+
context.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
645
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
646
|
+
error_instance_array(objects.size, exec_err)
|
|
647
|
+
end
|
|
549
648
|
end
|
|
550
649
|
when :resolve_static
|
|
551
650
|
result = begin
|
|
552
651
|
method_receiver.public_send(@field_definition.execution_mode_key, context, **args_hash)
|
|
553
652
|
rescue GraphQL::ExecutionError => err
|
|
554
653
|
err
|
|
654
|
+
rescue StandardError => stderr
|
|
655
|
+
begin
|
|
656
|
+
context.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
657
|
+
rescue GraphQL::ExecutionError => err
|
|
658
|
+
err
|
|
659
|
+
end
|
|
555
660
|
end
|
|
556
661
|
Array.new(objects.size, result)
|
|
557
662
|
when :resolve_each
|
|
558
663
|
objects.map do |o|
|
|
559
|
-
|
|
664
|
+
passed_in_obj = dyn_ins ? o.object : o
|
|
665
|
+
method_receiver.public_send(@field_definition.execution_mode_key, passed_in_obj, context, **args_hash)
|
|
560
666
|
rescue GraphQL::ExecutionError => err
|
|
561
667
|
err
|
|
668
|
+
rescue StandardError => stderr
|
|
669
|
+
begin
|
|
670
|
+
context.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: o)
|
|
671
|
+
rescue GraphQL::ExecutionError => err
|
|
672
|
+
err
|
|
673
|
+
end
|
|
562
674
|
end
|
|
563
675
|
when :hash_key
|
|
564
676
|
k = @field_definition.execution_mode_key
|
|
@@ -571,7 +683,7 @@ module GraphQL
|
|
|
571
683
|
err
|
|
572
684
|
rescue StandardError => stderr
|
|
573
685
|
begin
|
|
574
|
-
@selections_step.query.handle_or_reraise(stderr)
|
|
686
|
+
@selections_step.query.handle_or_reraise(stderr, object: o, field: @field_definition, arguments: args_hash)
|
|
575
687
|
rescue GraphQL::ExecutionError => ex_err
|
|
576
688
|
ex_err
|
|
577
689
|
end
|
|
@@ -615,9 +727,6 @@ module GraphQL
|
|
|
615
727
|
results
|
|
616
728
|
when :resolve_legacy_instance_method
|
|
617
729
|
@selections_step.graphql_objects.map do |obj_inst|
|
|
618
|
-
if @field_definition.dynamic_introspection
|
|
619
|
-
obj_inst = @owner.wrap(obj_inst, context)
|
|
620
|
-
end
|
|
621
730
|
obj_inst.public_send(@field_definition.execution_mode_key, **args_hash)
|
|
622
731
|
rescue GraphQL::ExecutionError => exec_err
|
|
623
732
|
exec_err
|
|
@@ -626,6 +735,10 @@ module GraphQL
|
|
|
626
735
|
raise "Batching execution for #{path} not implemented (execution_mode: #{@execution_mode.inspect}); provide `resolve_static:`, `resolve_batch:`, `hash_key:`, `method:`, or use a compatibility plug-in"
|
|
627
736
|
end
|
|
628
737
|
end
|
|
738
|
+
|
|
739
|
+
def error_instance_array(size, err_prototype)
|
|
740
|
+
Array.new(size) { err_prototype.dup }
|
|
741
|
+
end
|
|
629
742
|
end
|
|
630
743
|
end
|
|
631
744
|
end
|
|
@@ -117,7 +117,7 @@ module GraphQL
|
|
|
117
117
|
@current_exec_path << key
|
|
118
118
|
@current_result_path << key
|
|
119
119
|
|
|
120
|
-
field_defn = @query.context.types.field(parent_type, ast_selection.name) || raise("Invariant: No field found for #{
|
|
120
|
+
field_defn = @query.context.types.field(parent_type, ast_selection.name) || raise("Invariant: No field found for #{parent_type.to_type_signature}.#{ast_selection.name}")
|
|
121
121
|
result_type = field_defn.type
|
|
122
122
|
if (result_type_non_null = result_type.non_null?)
|
|
123
123
|
result_type = result_type.of_type
|
|
@@ -154,12 +154,14 @@ module GraphQL
|
|
|
154
154
|
@runner.type_condition_applies?(@query.context, static_type_at_result, t.name)
|
|
155
155
|
)
|
|
156
156
|
result_h = check_object_result(result_h, parent_type, ast_selection.selections)
|
|
157
|
+
return nil if result_h.nil?
|
|
157
158
|
end
|
|
158
159
|
when Language::Nodes::FragmentSpread
|
|
159
160
|
fragment_defn = @query.document.definitions.find { |defn| defn.is_a?(Language::Nodes::FragmentDefinition) && defn.name == ast_selection.name }
|
|
160
161
|
static_type_at_result = @static_type_at[result_h]
|
|
161
162
|
if static_type_at_result && @runner.type_condition_applies?(@query.context, static_type_at_result, fragment_defn.type.name)
|
|
162
163
|
result_h = check_object_result(result_h, parent_type, fragment_defn.selections)
|
|
164
|
+
return nil if result_h.nil?
|
|
163
165
|
end
|
|
164
166
|
end
|
|
165
167
|
end
|
|
@@ -181,11 +183,20 @@ module GraphQL
|
|
|
181
183
|
return result_arr if @finalizers_count == 0
|
|
182
184
|
end
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
effective_idx = -1
|
|
187
|
+
result_arr.each_with_index do |result_item, before_idx|
|
|
188
|
+
effective_idx += 1
|
|
189
|
+
@current_result_path << before_idx
|
|
190
|
+
new_result = if (f = finalizers(result_arr, before_idx))
|
|
191
|
+
before_size = result_arr.size
|
|
192
|
+
run_finalizers(@current_result_path.dup, f, result_arr, effective_idx)
|
|
193
|
+
after_size = result_arr.size
|
|
194
|
+
if after_size < before_size
|
|
195
|
+
effective_idx -= 1
|
|
196
|
+
:unassigned
|
|
197
|
+
else
|
|
198
|
+
result_arr[effective_idx]
|
|
199
|
+
end
|
|
189
200
|
elsif inner_type.list? && result_item
|
|
190
201
|
check_list_result(result_item, inner_type.of_type, ast_selections)
|
|
191
202
|
elsif !inner_type.kind.leaf? && result_item
|
|
@@ -196,10 +207,12 @@ module GraphQL
|
|
|
196
207
|
|
|
197
208
|
if new_result.nil? && inner_type_non_null
|
|
198
209
|
new_invalid_null = true
|
|
199
|
-
result_arr[
|
|
210
|
+
result_arr[effective_idx] = nil
|
|
211
|
+
break if @finalizers_count == 0
|
|
212
|
+
elsif :unassigned.equal?(new_result)
|
|
200
213
|
break if @finalizers_count == 0
|
|
201
214
|
elsif !new_result.equal?(result_item)
|
|
202
|
-
result_arr[
|
|
215
|
+
result_arr[effective_idx] = new_result
|
|
203
216
|
break if @finalizers_count == 0
|
|
204
217
|
end
|
|
205
218
|
ensure
|