graphql 2.5.11 → 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/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +2 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
- data/lib/graphql/dashboard.rb +11 -73
- data/lib/graphql/dataloader/async_dataloader.rb +22 -11
- data/lib/graphql/dataloader/null_dataloader.rb +48 -10
- data/lib/graphql/dataloader.rb +75 -23
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/interpreter/resolve.rb +7 -13
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +24 -18
- data/lib/graphql/execution/interpreter.rb +8 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next/field_resolve_step.rb +743 -0
- data/lib/graphql/execution/next/load_argument_step.rb +64 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +411 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +72 -0
- data/lib/graphql/execution.rb +8 -4
- data/lib/graphql/execution_error.rb +17 -10
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +4 -3
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/schema/argument.rb +12 -0
- data/lib/graphql/schema/build_from_definition.rb +10 -1
- data/lib/graphql/schema/directive.rb +22 -4
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +79 -48
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +43 -14
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +37 -0
- data/lib/graphql/schema/member/has_fields.rb +86 -5
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/resolver.rb +60 -1
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/validator/required_validator.rb +33 -2
- data/lib/graphql/schema/visibility/profile.rb +68 -49
- data/lib/graphql/schema/visibility.rb +3 -3
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +53 -10
- 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 +14 -4
- 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/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +25 -1
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +21 -1
- data/lib/graphql/testing/helpers.rb +12 -9
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace.rb +70 -7
- data/lib/graphql/tracing/perfetto_trace.rb +209 -79
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +8 -2
- metadata +17 -3
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -64,6 +64,7 @@ module GraphQL
|
|
|
64
64
|
@nonblocking = nonblocking
|
|
65
65
|
end
|
|
66
66
|
@fiber_limit = fiber_limit
|
|
67
|
+
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
# @return [Integer, nil]
|
|
@@ -140,10 +141,10 @@ module GraphQL
|
|
|
140
141
|
end
|
|
141
142
|
|
|
142
143
|
# @api private Nothing to see here
|
|
143
|
-
def append_job(&job)
|
|
144
|
+
def append_job(callable = nil, &job)
|
|
144
145
|
# Given a block, queue it up to be worked through when `#run` is called.
|
|
145
|
-
# (If the dataloader is already running,
|
|
146
|
-
@pending_jobs.push(job)
|
|
146
|
+
# (If the dataloader is already running, then a Fiber will pick this up later.)
|
|
147
|
+
@pending_jobs.push(callable || job)
|
|
147
148
|
nil
|
|
148
149
|
end
|
|
149
150
|
|
|
@@ -160,6 +161,10 @@ module GraphQL
|
|
|
160
161
|
def run_isolated
|
|
161
162
|
prev_queue = @pending_jobs
|
|
162
163
|
prev_pending_keys = {}
|
|
164
|
+
prev_lazies_at_depth = @lazies_at_depth
|
|
165
|
+
@lazies_at_depth = @lazies_at_depth.dup.clear
|
|
166
|
+
# Clear pending loads but keep already-cached records
|
|
167
|
+
# in case they are useful to the given block.
|
|
163
168
|
@source_cache.each do |source_class, batched_sources|
|
|
164
169
|
batched_sources.each do |batch_args, batched_source_instance|
|
|
165
170
|
if batched_source_instance.pending?
|
|
@@ -179,6 +184,7 @@ module GraphQL
|
|
|
179
184
|
res
|
|
180
185
|
ensure
|
|
181
186
|
@pending_jobs = prev_queue
|
|
187
|
+
@lazies_at_depth = prev_lazies_at_depth
|
|
182
188
|
prev_pending_keys.each do |source_instance, pending|
|
|
183
189
|
pending.each do |key, value|
|
|
184
190
|
if !source_instance.results.key?(key)
|
|
@@ -188,7 +194,8 @@ module GraphQL
|
|
|
188
194
|
end
|
|
189
195
|
end
|
|
190
196
|
|
|
191
|
-
|
|
197
|
+
# @param trace_query_lazy [nil, Execution::Multiplex]
|
|
198
|
+
def run(trace_query_lazy: nil)
|
|
192
199
|
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
193
200
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
194
201
|
job_fibers = []
|
|
@@ -201,26 +208,13 @@ module GraphQL
|
|
|
201
208
|
while first_pass || !job_fibers.empty?
|
|
202
209
|
first_pass = false
|
|
203
210
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
join_queues(job_fibers, next_job_fibers)
|
|
213
|
-
|
|
214
|
-
while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
215
|
-
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
216
|
-
if f.alive?
|
|
217
|
-
finished = run_fiber(f)
|
|
218
|
-
if !finished
|
|
219
|
-
next_source_fibers << f
|
|
220
|
-
end
|
|
221
|
-
end
|
|
211
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
212
|
+
|
|
213
|
+
if !@lazies_at_depth.empty?
|
|
214
|
+
with_trace_query_lazy(trace_query_lazy) do
|
|
215
|
+
run_next_pending_lazies(job_fibers, trace)
|
|
216
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
222
217
|
end
|
|
223
|
-
join_queues(source_fibers, next_source_fibers)
|
|
224
218
|
end
|
|
225
219
|
end
|
|
226
220
|
|
|
@@ -248,6 +242,11 @@ module GraphQL
|
|
|
248
242
|
f.resume
|
|
249
243
|
end
|
|
250
244
|
|
|
245
|
+
# @api private
|
|
246
|
+
def lazy_at_depth(depth, lazy)
|
|
247
|
+
@lazies_at_depth[depth] << lazy
|
|
248
|
+
end
|
|
249
|
+
|
|
251
250
|
def spawn_fiber
|
|
252
251
|
fiber_vars = get_fiber_variables
|
|
253
252
|
Fiber.new(blocking: !@nonblocking) {
|
|
@@ -275,6 +274,59 @@ module GraphQL
|
|
|
275
274
|
|
|
276
275
|
private
|
|
277
276
|
|
|
277
|
+
def run_next_pending_lazies(job_fibers, trace)
|
|
278
|
+
smallest_depth = nil
|
|
279
|
+
@lazies_at_depth.each_key do |depth_key|
|
|
280
|
+
smallest_depth ||= depth_key
|
|
281
|
+
if depth_key < smallest_depth
|
|
282
|
+
smallest_depth = depth_key
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if smallest_depth
|
|
287
|
+
lazies = @lazies_at_depth.delete(smallest_depth)
|
|
288
|
+
if !lazies.empty?
|
|
289
|
+
lazies.each_with_index do |l, idx|
|
|
290
|
+
append_job { l.value }
|
|
291
|
+
end
|
|
292
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
298
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
|
|
299
|
+
if f.alive?
|
|
300
|
+
finished = run_fiber(f)
|
|
301
|
+
if !finished
|
|
302
|
+
next_job_fibers << f
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
join_queues(job_fibers, next_job_fibers)
|
|
307
|
+
|
|
308
|
+
while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
309
|
+
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
310
|
+
if f.alive?
|
|
311
|
+
finished = run_fiber(f)
|
|
312
|
+
if !finished
|
|
313
|
+
next_source_fibers << f
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
join_queues(source_fibers, next_source_fibers)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def with_trace_query_lazy(multiplex_or_nil, &block)
|
|
322
|
+
if (multiplex = multiplex_or_nil)
|
|
323
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
324
|
+
multiplex.current_trace.execute_query_lazy(query: query, multiplex: multiplex, &block)
|
|
325
|
+
else
|
|
326
|
+
yield
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
278
330
|
def calculate_fiber_limit
|
|
279
331
|
total_fiber_limit = @fiber_limit || Float::INFINITY
|
|
280
332
|
if total_fiber_limit < 4
|
|
@@ -10,7 +10,7 @@ module GraphQL
|
|
|
10
10
|
|
|
11
11
|
def initialize(value)
|
|
12
12
|
@date_value = value
|
|
13
|
-
super("Date cannot be parsed: #{value}. \nDate must be
|
|
13
|
+
super("Date cannot be parsed: #{value}. \nDate must be able to be parsed as a Ruby Date object.")
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -6,12 +6,17 @@ module GraphQL
|
|
|
6
6
|
module Resolve
|
|
7
7
|
# Continue field results in `results` until there's nothing else to continue.
|
|
8
8
|
# @return [void]
|
|
9
|
+
# @deprecated Call `dataloader.run` instead
|
|
9
10
|
def self.resolve_all(results, dataloader)
|
|
11
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
10
12
|
dataloader.append_job { resolve(results, dataloader) }
|
|
11
13
|
nil
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
# @deprecated Call `dataloader.run` instead
|
|
14
17
|
def self.resolve_each_depth(lazies_at_depth, dataloader)
|
|
18
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
19
|
+
|
|
15
20
|
smallest_depth = nil
|
|
16
21
|
lazies_at_depth.each_key do |depth_key|
|
|
17
22
|
smallest_depth ||= depth_key
|
|
@@ -34,20 +39,9 @@ module GraphQL
|
|
|
34
39
|
nil
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
#
|
|
38
|
-
# continue it until you get a response-ready Ruby value.
|
|
39
|
-
#
|
|
40
|
-
# `results` is one level of _depth_ of a query or multiplex.
|
|
41
|
-
#
|
|
42
|
-
# Resolve all lazy values in that depth before moving on
|
|
43
|
-
# to the next level.
|
|
44
|
-
#
|
|
45
|
-
# It's assumed that the lazies will
|
|
46
|
-
# return {Lazy} instances if there's more work to be done,
|
|
47
|
-
# or return {Hash}/{Array} if the query should be continued.
|
|
48
|
-
#
|
|
49
|
-
# @return [void]
|
|
42
|
+
# @deprecated Call `dataloader.run` instead
|
|
50
43
|
def self.resolve(results, dataloader)
|
|
44
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
51
45
|
# There might be pending jobs here that _will_ write lazies
|
|
52
46
|
# into the result hash. We should run them out, so we
|
|
53
47
|
# can be sure that all lazies will be present in the result hashes.
|
|
@@ -42,6 +42,14 @@ module GraphQL
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def depth
|
|
46
|
+
@depth ||= if @graphql_parent
|
|
47
|
+
@graphql_parent.depth + 1
|
|
48
|
+
else
|
|
49
|
+
1
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
attr_accessor :graphql_dead
|
|
46
54
|
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent,
|
|
47
55
|
:graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
|
|
@@ -157,6 +165,11 @@ module GraphQL
|
|
|
157
165
|
end
|
|
158
166
|
end
|
|
159
167
|
end
|
|
168
|
+
|
|
169
|
+
# hook for breadth-first implementations to signal when collecting results.
|
|
170
|
+
def collect_result(result_name, result_value)
|
|
171
|
+
false
|
|
172
|
+
end
|
|
160
173
|
end
|
|
161
174
|
|
|
162
175
|
class GraphQLResultArray
|
|
@@ -35,11 +35,10 @@ module GraphQL
|
|
|
35
35
|
# @return [GraphQL::Query::Context]
|
|
36
36
|
attr_reader :context
|
|
37
37
|
|
|
38
|
-
def initialize(query
|
|
38
|
+
def initialize(query:)
|
|
39
39
|
@query = query
|
|
40
40
|
@current_trace = query.current_trace
|
|
41
41
|
@dataloader = query.multiplex.dataloader
|
|
42
|
-
@lazies_at_depth = lazies_at_depth
|
|
43
42
|
@schema = query.schema
|
|
44
43
|
@context = query.context
|
|
45
44
|
@response = nil
|
|
@@ -365,6 +364,10 @@ module GraphQL
|
|
|
365
364
|
else
|
|
366
365
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
|
367
366
|
runtime_state = get_current_runtime_state # This might be in a different fiber
|
|
367
|
+
runtime_state.current_field = field_defn
|
|
368
|
+
runtime_state.current_arguments = resolved_arguments
|
|
369
|
+
runtime_state.current_result_name = result_name
|
|
370
|
+
runtime_state.current_result = selections_result
|
|
368
371
|
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
|
|
369
372
|
end
|
|
370
373
|
end
|
|
@@ -373,6 +376,8 @@ module GraphQL
|
|
|
373
376
|
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
374
377
|
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
|
|
375
378
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
|
379
|
+
next if selection_result.collect_result(result_name, resolved_arguments)
|
|
380
|
+
|
|
376
381
|
return_type_non_null = field_defn.type.non_null?
|
|
377
382
|
continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
378
383
|
next
|
|
@@ -446,7 +451,7 @@ module GraphQL
|
|
|
446
451
|
}
|
|
447
452
|
end
|
|
448
453
|
|
|
449
|
-
|
|
454
|
+
call_method_on_directives(:resolve, object, directives) do
|
|
450
455
|
if !directives.empty?
|
|
451
456
|
# This might be executed in a different context; reset this info
|
|
452
457
|
runtime_state = get_current_runtime_state
|
|
@@ -472,6 +477,8 @@ module GraphQL
|
|
|
472
477
|
end
|
|
473
478
|
@current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
|
|
474
479
|
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
|
480
|
+
next if selection_result.collect_result(result_name, inner_result)
|
|
481
|
+
|
|
475
482
|
owner_type = selection_result.graphql_result_type
|
|
476
483
|
return_type = field_defn.type
|
|
477
484
|
continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
|
@@ -488,7 +495,7 @@ module GraphQL
|
|
|
488
495
|
# all of its child fields before moving on to the next root mutation field.
|
|
489
496
|
# (Subselections of this mutation will still be resolved level-by-level.)
|
|
490
497
|
if selection_result.graphql_is_eager
|
|
491
|
-
|
|
498
|
+
@dataloader.run
|
|
492
499
|
end
|
|
493
500
|
end
|
|
494
501
|
|
|
@@ -596,7 +603,7 @@ module GraphQL
|
|
|
596
603
|
err
|
|
597
604
|
end
|
|
598
605
|
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
599
|
-
elsif GraphQL::Execution::
|
|
606
|
+
elsif value.is_a?(GraphQL::Execution::Skip)
|
|
600
607
|
# It's possible a lazy was already written here
|
|
601
608
|
case selection_result
|
|
602
609
|
when GraphQLResultHash
|
|
@@ -667,7 +674,11 @@ module GraphQL
|
|
|
667
674
|
rescue GraphQL::ExecutionError => ex_err
|
|
668
675
|
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
669
676
|
rescue StandardError => err
|
|
670
|
-
|
|
677
|
+
begin
|
|
678
|
+
query.handle_or_reraise(err)
|
|
679
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
680
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
681
|
+
end
|
|
671
682
|
end
|
|
672
683
|
set_result(selection_result, result_name, r, false, is_non_null)
|
|
673
684
|
r
|
|
@@ -750,7 +761,7 @@ module GraphQL
|
|
|
750
761
|
idx += 1
|
|
751
762
|
if use_dataloader_job
|
|
752
763
|
@dataloader.append_job do
|
|
753
|
-
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)
|
|
754
765
|
end
|
|
755
766
|
else
|
|
756
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)
|
|
@@ -792,6 +803,7 @@ module GraphQL
|
|
|
792
803
|
end
|
|
793
804
|
|
|
794
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
|
|
795
807
|
runtime_state.current_result_name = this_idx
|
|
796
808
|
runtime_state.current_result = response_list
|
|
797
809
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
@@ -879,7 +891,6 @@ module GraphQL
|
|
|
879
891
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
|
880
892
|
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
|
881
893
|
if lazy?(lazy_obj)
|
|
882
|
-
orig_result = result
|
|
883
894
|
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
|
884
895
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
|
885
896
|
# This block might be called in a new fiber;
|
|
@@ -889,13 +900,13 @@ module GraphQL
|
|
|
889
900
|
runtime_state.current_field = field
|
|
890
901
|
runtime_state.current_arguments = arguments
|
|
891
902
|
runtime_state.current_result_name = result_name
|
|
892
|
-
runtime_state.current_result =
|
|
903
|
+
runtime_state.current_result = result
|
|
893
904
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
894
905
|
# Wrap the execution of _this_ method with tracing,
|
|
895
906
|
# but don't wrap the continuation below
|
|
896
|
-
|
|
907
|
+
sync_result = nil
|
|
897
908
|
inner_obj = begin
|
|
898
|
-
|
|
909
|
+
sync_result = if trace
|
|
899
910
|
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
|
900
911
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
|
901
912
|
schema.sync_lazy(lazy_obj)
|
|
@@ -913,7 +924,7 @@ module GraphQL
|
|
|
913
924
|
end
|
|
914
925
|
ensure
|
|
915
926
|
if trace
|
|
916
|
-
@current_trace.end_execute_field(field, owner_object, arguments, query,
|
|
927
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, sync_result)
|
|
917
928
|
end
|
|
918
929
|
end
|
|
919
930
|
yield(inner_obj, runtime_state)
|
|
@@ -923,12 +934,7 @@ module GraphQL
|
|
|
923
934
|
lazy.value
|
|
924
935
|
else
|
|
925
936
|
set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
|
|
926
|
-
|
|
927
|
-
while result
|
|
928
|
-
current_depth += 1
|
|
929
|
-
result = result.graphql_parent
|
|
930
|
-
end
|
|
931
|
-
@lazies_at_depth[current_depth] << lazy
|
|
937
|
+
@dataloader.lazy_at_depth(result.depth, lazy)
|
|
932
938
|
lazy
|
|
933
939
|
end
|
|
934
940
|
else
|
|
@@ -42,7 +42,6 @@ module GraphQL
|
|
|
42
42
|
trace.execute_multiplex(multiplex: multiplex) do
|
|
43
43
|
schema = multiplex.schema
|
|
44
44
|
queries = multiplex.queries
|
|
45
|
-
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
46
45
|
multiplex_analyzers = schema.multiplex_analyzers
|
|
47
46
|
if multiplex.max_complexity
|
|
48
47
|
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
|
@@ -59,11 +58,6 @@ module GraphQL
|
|
|
59
58
|
# Do as much eager evaluation of the query as possible
|
|
60
59
|
results = []
|
|
61
60
|
queries.each_with_index do |query, idx|
|
|
62
|
-
if query.subscription? && !query.subscription_update?
|
|
63
|
-
subs_namespace = query.context.namespace(:subscriptions)
|
|
64
|
-
subs_namespace[:events] = []
|
|
65
|
-
subs_namespace[:subscriptions] = {}
|
|
66
|
-
end
|
|
67
61
|
multiplex.dataloader.append_job {
|
|
68
62
|
operation = query.selected_operation
|
|
69
63
|
result = if operation.nil? || !query.valid? || !query.context.errors.empty?
|
|
@@ -73,38 +67,27 @@ module GraphQL
|
|
|
73
67
|
# Although queries in a multiplex _share_ an Interpreter instance,
|
|
74
68
|
# they also have another item of state, which is private to that query
|
|
75
69
|
# in particular, assign it here:
|
|
76
|
-
runtime = Runtime.new(query: query
|
|
70
|
+
runtime = Runtime.new(query: query)
|
|
77
71
|
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
|
78
|
-
|
|
72
|
+
if query.subscription? && !query.subscription_update?
|
|
73
|
+
schema.subscriptions.initialize_subscriptions(query)
|
|
74
|
+
end
|
|
79
75
|
query.current_trace.execute_query(query: query) do
|
|
80
76
|
runtime.run_eager
|
|
81
77
|
end
|
|
82
78
|
rescue GraphQL::ExecutionError => err
|
|
83
79
|
query.context.errors << err
|
|
84
|
-
NO_OPERATION
|
|
85
80
|
end
|
|
86
81
|
end
|
|
87
82
|
results[idx] = result
|
|
88
83
|
}
|
|
89
84
|
end
|
|
90
85
|
|
|
91
|
-
multiplex.dataloader.run
|
|
92
|
-
|
|
93
|
-
# Then, work through lazy results in a breadth-first way
|
|
94
|
-
multiplex.dataloader.append_job {
|
|
95
|
-
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
96
|
-
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
|
97
|
-
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
|
98
|
-
end
|
|
99
|
-
}
|
|
100
|
-
multiplex.dataloader.run
|
|
86
|
+
multiplex.dataloader.run(trace_query_lazy: multiplex)
|
|
101
87
|
|
|
102
88
|
# Then, find all errors and assign the result to the query object
|
|
103
89
|
results.each_with_index do |data_result, idx|
|
|
104
90
|
query = queries[idx]
|
|
105
|
-
if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
|
|
106
|
-
schema.subscriptions.write_subscription(query, events)
|
|
107
|
-
end
|
|
108
91
|
# Assign the result so that it can be accessed in instrumentation
|
|
109
92
|
query.result_values = if data_result.equal?(NO_OPERATION)
|
|
110
93
|
if !query.valid? || !query.context.errors.empty?
|
|
@@ -114,6 +97,9 @@ module GraphQL
|
|
|
114
97
|
data_result
|
|
115
98
|
end
|
|
116
99
|
else
|
|
100
|
+
if query.subscription?
|
|
101
|
+
schema.subscriptions.finish_subscriptions(query)
|
|
102
|
+
end
|
|
117
103
|
result = {}
|
|
118
104
|
|
|
119
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
|
|
@@ -33,7 +33,7 @@ module GraphQL
|
|
|
33
33
|
@queries.each { |q| q.multiplex = self }
|
|
34
34
|
@context = context
|
|
35
35
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
|
36
|
-
@tracers = schema.tracers + (context[:tracers] ||
|
|
36
|
+
@tracers = schema.tracers + (context[:tracers] || EmptyObjects::EMPTY_ARRAY)
|
|
37
37
|
@max_complexity = max_complexity
|
|
38
38
|
@current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
|
|
39
39
|
@logger = nil
|