graphql 2.5.11 → 2.6.10
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/field_extractor.rb +17 -1
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/generators/graphql/templates/schema.erb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +31 -15
- data/lib/graphql/analysis.rb +20 -13
- data/lib/graphql/backtrace/table.rb +10 -1
- data/lib/graphql/current.rb +7 -1
- data/lib/graphql/dashboard/application_controller.rb +45 -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 +14 -73
- data/lib/graphql/dataloader/async_dataloader.rb +336 -60
- data/lib/graphql/dataloader/null_dataloader.rb +48 -10
- data/lib/graphql/dataloader/source.rb +32 -19
- data/lib/graphql/dataloader.rb +107 -39
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/directive_checks.rb +2 -0
- data/lib/graphql/execution/field_resolve_step.rb +804 -0
- data/lib/graphql/execution/finalize.rb +229 -0
- data/lib/graphql/execution/input_values.rb +333 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- 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 +60 -40
- data/lib/graphql/execution/interpreter.rb +17 -23
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +102 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next.rb +98 -0
- data/lib/graphql/execution/prepare_object_step.rb +151 -0
- data/lib/graphql/execution/resolve_type_step.rb +27 -0
- data/lib/graphql/execution/runner.rb +457 -0
- data/lib/graphql/execution/selections_step.rb +95 -0
- data/lib/graphql/execution.rb +15 -4
- data/lib/graphql/execution_error.rb +17 -12
- data/lib/graphql/float_decoding_error.rb +13 -0
- data/lib/graphql/float_encoding_error.rb +28 -0
- 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/schema_type.rb +6 -2
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/block_string.rb +6 -11
- data/lib/graphql/language/cache.rb +84 -14
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +27 -17
- data/lib/graphql/language/nodes.rb +2 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language.rb +32 -17
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +30 -0
- data/lib/graphql/pagination/relation_connection.rb +10 -1
- data/lib/graphql/query/context.rb +10 -3
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query/variable_validation_error.rb +16 -1
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/query.rb +12 -3
- data/lib/graphql/railtie.rb +2 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/argument.rb +13 -1
- data/lib/graphql/schema/build_from_definition.rb +28 -1
- data/lib/graphql/schema/directive/feature.rb +4 -0
- data/lib/graphql/schema/directive/transform.rb +20 -0
- data/lib/graphql/schema/directive.rb +48 -13
- data/lib/graphql/schema/field/connection_extension.rb +4 -37
- data/lib/graphql/schema/field/scope_extension.rb +18 -13
- data/lib/graphql/schema/field.rb +79 -48
- data/lib/graphql/schema/field_extension.rb +11 -8
- data/lib/graphql/schema/has_single_input_argument.rb +24 -13
- data/lib/graphql/schema/input_object.rb +6 -2
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/introspection_system.rb +6 -21
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
- 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/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 +76 -1
- data/lib/graphql/schema/subscription.rb +53 -10
- 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 +38 -5
- data/lib/graphql/schema/validator.rb +9 -0
- data/lib/graphql/schema/visibility/profile.rb +74 -53
- data/lib/graphql/schema/visibility/visit.rb +1 -1
- data/lib/graphql/schema/visibility.rb +32 -24
- data/lib/graphql/schema/wrapper.rb +11 -1
- data/lib/graphql/schema.rb +93 -27
- 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 +351 -248
- 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 +40 -10
- data/lib/graphql/subscriptions/serialize.rb +10 -4
- data/lib/graphql/subscriptions.rb +36 -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 +213 -81
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/types/float.rb +18 -4
- 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 +23 -59
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -58,12 +58,15 @@ module GraphQL
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
|
|
61
|
-
@source_cache = Hash.new { |h, k| h[k] = {} }
|
|
61
|
+
@source_cache = Hash.new { |h, k| h[k] = {} }.compare_by_identity
|
|
62
|
+
@pending_source_set = Set.new.compare_by_identity
|
|
63
|
+
@pending_sources = []
|
|
62
64
|
@pending_jobs = []
|
|
63
65
|
if !nonblocking.nil?
|
|
64
66
|
@nonblocking = nonblocking
|
|
65
67
|
end
|
|
66
68
|
@fiber_limit = fiber_limit
|
|
69
|
+
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
# @return [Integer, nil]
|
|
@@ -107,7 +110,7 @@ module GraphQL
|
|
|
107
110
|
# @param batch_parameters [Array<Object>]
|
|
108
111
|
# @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
|
|
109
112
|
# and cached for the lifetime of this {Multiplex}.
|
|
110
|
-
if RUBY_VERSION < "3" || RUBY_ENGINE
|
|
113
|
+
if (RUBY_ENGINE == "ruby" && RUBY_VERSION < "3") || RUBY_ENGINE == "truffleruby" # truffle-ruby wasn't doing well with the implementation below
|
|
111
114
|
def with(source_class, *batch_args)
|
|
112
115
|
batch_key = source_class.batch_key_for(*batch_args)
|
|
113
116
|
@source_cache[source_class][batch_key] ||= begin
|
|
@@ -140,10 +143,18 @@ module GraphQL
|
|
|
140
143
|
end
|
|
141
144
|
|
|
142
145
|
# @api private Nothing to see here
|
|
143
|
-
def append_job(&job)
|
|
146
|
+
def append_job(callable = nil, &job)
|
|
144
147
|
# 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)
|
|
148
|
+
# (If the dataloader is already running, then a Fiber will pick this up later.)
|
|
149
|
+
@pending_jobs.push(callable || job)
|
|
150
|
+
nil
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @api private
|
|
154
|
+
def queue_pending_source(source)
|
|
155
|
+
if @pending_source_set.add?(source)
|
|
156
|
+
@pending_sources << source
|
|
157
|
+
end
|
|
147
158
|
nil
|
|
148
159
|
end
|
|
149
160
|
|
|
@@ -160,6 +171,10 @@ module GraphQL
|
|
|
160
171
|
def run_isolated
|
|
161
172
|
prev_queue = @pending_jobs
|
|
162
173
|
prev_pending_keys = {}
|
|
174
|
+
prev_lazies_at_depth = @lazies_at_depth
|
|
175
|
+
@lazies_at_depth = @lazies_at_depth.dup.clear
|
|
176
|
+
# Clear pending loads but keep already-cached records
|
|
177
|
+
# in case they are useful to the given block.
|
|
163
178
|
@source_cache.each do |source_class, batched_sources|
|
|
164
179
|
batched_sources.each do |batch_args, batched_source_instance|
|
|
165
180
|
if batched_source_instance.pending?
|
|
@@ -179,16 +194,19 @@ module GraphQL
|
|
|
179
194
|
res
|
|
180
195
|
ensure
|
|
181
196
|
@pending_jobs = prev_queue
|
|
197
|
+
@lazies_at_depth = prev_lazies_at_depth
|
|
182
198
|
prev_pending_keys.each do |source_instance, pending|
|
|
183
199
|
pending.each do |key, value|
|
|
184
|
-
if
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
next if source_instance.results.key?(key)
|
|
201
|
+
|
|
202
|
+
queue_pending_source(source_instance) if source_instance.pending.empty?
|
|
203
|
+
source_instance.pending[key] = value
|
|
187
204
|
end
|
|
188
205
|
end
|
|
189
206
|
end
|
|
190
207
|
|
|
191
|
-
|
|
208
|
+
# @param trace_query_lazy [nil, Execution::Multiplex]
|
|
209
|
+
def run(trace_query_lazy: nil)
|
|
192
210
|
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
193
211
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
194
212
|
job_fibers = []
|
|
@@ -201,26 +219,15 @@ module GraphQL
|
|
|
201
219
|
while first_pass || !job_fibers.empty?
|
|
202
220
|
first_pass = false
|
|
203
221
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
|
222
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
223
|
+
|
|
224
|
+
if !@lazies_at_depth.empty?
|
|
225
|
+
with_trace_query_lazy(trace_query_lazy) do
|
|
226
|
+
if enqueue_next_pending_lazies(@lazies_at_depth)
|
|
227
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
228
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
221
229
|
end
|
|
222
230
|
end
|
|
223
|
-
join_queues(source_fibers, next_source_fibers)
|
|
224
231
|
end
|
|
225
232
|
end
|
|
226
233
|
|
|
@@ -248,6 +255,11 @@ module GraphQL
|
|
|
248
255
|
f.resume
|
|
249
256
|
end
|
|
250
257
|
|
|
258
|
+
# @api private
|
|
259
|
+
def lazy_at_depth(depth, lazy)
|
|
260
|
+
@lazies_at_depth[depth] << lazy
|
|
261
|
+
end
|
|
262
|
+
|
|
251
263
|
def spawn_fiber
|
|
252
264
|
fiber_vars = get_fiber_variables
|
|
253
265
|
Fiber.new(blocking: !@nonblocking) {
|
|
@@ -275,6 +287,54 @@ module GraphQL
|
|
|
275
287
|
|
|
276
288
|
private
|
|
277
289
|
|
|
290
|
+
# Returns true if anything was actually enqueued
|
|
291
|
+
def enqueue_next_pending_lazies(lazies_at_depth)
|
|
292
|
+
smallest_depth = lazies_at_depth.each_key.min
|
|
293
|
+
return false if smallest_depth.nil?
|
|
294
|
+
|
|
295
|
+
lazies = lazies_at_depth.delete(smallest_depth)
|
|
296
|
+
return false if lazies.empty?
|
|
297
|
+
|
|
298
|
+
lazies.each do |lazy|
|
|
299
|
+
append_job { lazy.value }
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
true
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
306
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
|
|
307
|
+
if f.alive?
|
|
308
|
+
finished = run_fiber(f)
|
|
309
|
+
if !finished
|
|
310
|
+
next_job_fibers << f
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
join_queues(job_fibers, next_job_fibers)
|
|
315
|
+
|
|
316
|
+
while (!source_fibers.empty? || !@pending_sources.empty?)
|
|
317
|
+
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)))
|
|
318
|
+
if f.alive?
|
|
319
|
+
finished = run_fiber(f)
|
|
320
|
+
if !finished
|
|
321
|
+
next_source_fibers << f
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
join_queues(source_fibers, next_source_fibers)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def with_trace_query_lazy(multiplex_or_nil, &block)
|
|
330
|
+
if (multiplex = multiplex_or_nil)
|
|
331
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
332
|
+
multiplex.current_trace.execute_query_lazy(query: query, multiplex: multiplex, &block)
|
|
333
|
+
else
|
|
334
|
+
yield
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
278
338
|
def calculate_fiber_limit
|
|
279
339
|
total_fiber_limit = @fiber_limit || Float::INFINITY
|
|
280
340
|
if total_fiber_limit < 4
|
|
@@ -304,21 +364,29 @@ module GraphQL
|
|
|
304
364
|
end
|
|
305
365
|
end
|
|
306
366
|
|
|
307
|
-
def
|
|
308
|
-
pending_sources =
|
|
309
|
-
@
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
367
|
+
def drain_pending_sources
|
|
368
|
+
pending_sources = @pending_sources
|
|
369
|
+
@pending_sources = []
|
|
370
|
+
@pending_source_set.clear
|
|
371
|
+
|
|
372
|
+
pending_sources.select!(&:pending?)
|
|
373
|
+
pending_sources.empty? ? nil : pending_sources
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def dequeue_pending_source
|
|
377
|
+
while (source = @pending_sources.shift)
|
|
378
|
+
@pending_source_set.delete(source)
|
|
379
|
+
return source if source.pending?
|
|
316
380
|
end
|
|
381
|
+
end
|
|
317
382
|
|
|
318
|
-
|
|
383
|
+
def spawn_source_fiber(trace)
|
|
384
|
+
if !@pending_sources.empty?
|
|
319
385
|
spawn_fiber do
|
|
320
|
-
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
321
|
-
|
|
386
|
+
trace&.dataloader_spawn_source_fiber(@pending_sources)
|
|
387
|
+
# This will find sources which were enqueued during `#fetch`:
|
|
388
|
+
while (source = dequeue_pending_source)
|
|
389
|
+
next if !source.pending?
|
|
322
390
|
Fiber[:__graphql_current_dataloader_source] = source
|
|
323
391
|
trace&.begin_dataloader_source(source)
|
|
324
392
|
source.run_pending_keys
|
|
@@ -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
|
|
@@ -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
|