graphql 2.4.3 → 2.5.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/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +7 -2
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +96 -52
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +6 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +19 -16
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +7 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +22 -32
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +3 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +27 -13
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +77 -40
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +62 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +98 -244
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +178 -38
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +266 -54
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +62 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
data/lib/graphql/dataloader.rb
CHANGED
@@ -4,6 +4,8 @@ require "graphql/dataloader/null_dataloader"
|
|
4
4
|
require "graphql/dataloader/request"
|
5
5
|
require "graphql/dataloader/request_all"
|
6
6
|
require "graphql/dataloader/source"
|
7
|
+
require "graphql/dataloader/active_record_association_source"
|
8
|
+
require "graphql/dataloader/active_record_source"
|
7
9
|
|
8
10
|
module GraphQL
|
9
11
|
# This plugin supports Fiber-based concurrency, along with {GraphQL::Dataloader::Source}.
|
@@ -78,10 +80,7 @@ module GraphQL
|
|
78
80
|
def get_fiber_variables
|
79
81
|
fiber_vars = {}
|
80
82
|
Thread.current.keys.each do |fiber_var_key|
|
81
|
-
|
82
|
-
if fiber_var_key != :__graphql_runtime_info
|
83
|
-
fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
|
84
|
-
end
|
83
|
+
fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
|
85
84
|
end
|
86
85
|
fiber_vars
|
87
86
|
end
|
@@ -132,8 +131,11 @@ module GraphQL
|
|
132
131
|
# Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
|
133
132
|
#
|
134
133
|
# @return [void]
|
135
|
-
def yield
|
134
|
+
def yield(source = Fiber[:__graphql_current_dataloader_source])
|
135
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
136
|
+
trace&.dataloader_fiber_yield(source)
|
136
137
|
Fiber.yield
|
138
|
+
trace&.dataloader_fiber_resume(source)
|
137
139
|
nil
|
138
140
|
end
|
139
141
|
|
@@ -187,6 +189,7 @@ module GraphQL
|
|
187
189
|
end
|
188
190
|
|
189
191
|
def run
|
192
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
190
193
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
191
194
|
job_fibers = []
|
192
195
|
next_job_fibers = []
|
@@ -194,10 +197,11 @@ module GraphQL
|
|
194
197
|
next_source_fibers = []
|
195
198
|
first_pass = true
|
196
199
|
manager = spawn_fiber do
|
197
|
-
|
200
|
+
trace&.begin_dataloader(self)
|
201
|
+
while first_pass || !job_fibers.empty?
|
198
202
|
first_pass = false
|
199
203
|
|
200
|
-
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber)))
|
204
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
|
201
205
|
if f.alive?
|
202
206
|
finished = run_fiber(f)
|
203
207
|
if !finished
|
@@ -207,8 +211,8 @@ module GraphQL
|
|
207
211
|
end
|
208
212
|
join_queues(job_fibers, next_job_fibers)
|
209
213
|
|
210
|
-
while (source_fibers.
|
211
|
-
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))
|
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)))
|
212
216
|
if f.alive?
|
213
217
|
finished = run_fiber(f)
|
214
218
|
if !finished
|
@@ -219,6 +223,8 @@ module GraphQL
|
|
219
223
|
join_queues(source_fibers, next_source_fibers)
|
220
224
|
end
|
221
225
|
end
|
226
|
+
|
227
|
+
trace&.end_dataloader(self)
|
222
228
|
end
|
223
229
|
|
224
230
|
run_fiber(manager)
|
@@ -227,12 +233,13 @@ module GraphQL
|
|
227
233
|
raise "Invariant: Manager fiber didn't terminate properly."
|
228
234
|
end
|
229
235
|
|
230
|
-
if job_fibers.
|
236
|
+
if !job_fibers.empty?
|
231
237
|
raise "Invariant: job fibers should have exited but #{job_fibers.size} remained"
|
232
238
|
end
|
233
|
-
if source_fibers.
|
239
|
+
if !source_fibers.empty?
|
234
240
|
raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
|
235
241
|
end
|
242
|
+
|
236
243
|
rescue UncaughtThrowError => e
|
237
244
|
throw e.tag, e.value
|
238
245
|
end
|
@@ -250,6 +257,22 @@ module GraphQL
|
|
250
257
|
}
|
251
258
|
end
|
252
259
|
|
260
|
+
# Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
|
261
|
+
# These will be used by {Dataloader::ActiveRecordSource}, {Dataloader::ActiveRecordAssociationSource} and their helper
|
262
|
+
# methods, `dataload_record` and `dataload_association`.
|
263
|
+
# @param records [Array<ActiveRecord::Base>] Already-loaded records to warm the cache with
|
264
|
+
# @param index_by [Symbol] The attribute to use as the cache key. (Should match `find_by:` when using {ActiveRecordSource})
|
265
|
+
# @return [void]
|
266
|
+
def merge_records(records, index_by: :id)
|
267
|
+
records_by_class = Hash.new { |h, k| h[k] = {} }
|
268
|
+
records.each do |r|
|
269
|
+
records_by_class[r.class][r.public_send(index_by)] = r
|
270
|
+
end
|
271
|
+
records_by_class.each do |r_class, records|
|
272
|
+
with(ActiveRecordSource, r_class).merge(records)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
253
276
|
private
|
254
277
|
|
255
278
|
def calculate_fiber_limit
|
@@ -269,17 +292,19 @@ module GraphQL
|
|
269
292
|
new_queue.clear
|
270
293
|
end
|
271
294
|
|
272
|
-
def spawn_job_fiber
|
273
|
-
if
|
295
|
+
def spawn_job_fiber(trace)
|
296
|
+
if !@pending_jobs.empty?
|
274
297
|
spawn_fiber do
|
298
|
+
trace&.dataloader_spawn_execution_fiber(@pending_jobs)
|
275
299
|
while job = @pending_jobs.shift
|
276
300
|
job.call
|
277
301
|
end
|
302
|
+
trace&.dataloader_fiber_exit
|
278
303
|
end
|
279
304
|
end
|
280
305
|
end
|
281
306
|
|
282
|
-
def spawn_source_fiber
|
307
|
+
def spawn_source_fiber(trace)
|
283
308
|
pending_sources = nil
|
284
309
|
@source_cache.each_value do |source_by_batch_params|
|
285
310
|
source_by_batch_params.each_value do |source|
|
@@ -292,10 +317,14 @@ module GraphQL
|
|
292
317
|
|
293
318
|
if pending_sources
|
294
319
|
spawn_fiber do
|
320
|
+
trace&.dataloader_spawn_source_fiber(pending_sources)
|
295
321
|
pending_sources.each do |source|
|
296
322
|
Fiber[:__graphql_current_dataloader_source] = source
|
323
|
+
trace&.begin_dataloader_source(source)
|
297
324
|
source.run_pending_keys
|
325
|
+
trace&.end_dataloader_source(source)
|
298
326
|
end
|
327
|
+
trace&.dataloader_fiber_exit
|
299
328
|
end
|
300
329
|
end
|
301
330
|
end
|
data/lib/graphql/dig.rb
CHANGED
@@ -5,7 +5,8 @@ module GraphQL
|
|
5
5
|
# so we can use some of the magic in Schema::InputObject and Interpreter::Arguments
|
6
6
|
# to handle stringified/symbolized keys.
|
7
7
|
#
|
8
|
-
# @param
|
8
|
+
# @param own_key [String, Symbol] A key to retrieve
|
9
|
+
# @param rest_keys [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly
|
9
10
|
# @return [Object]
|
10
11
|
def dig(own_key, *rest_keys)
|
11
12
|
val = self[own_key]
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
22
22
|
|
23
23
|
if smallest_depth
|
24
24
|
lazies = lazies_at_depth.delete(smallest_depth)
|
25
|
-
if lazies.
|
25
|
+
if !lazies.empty?
|
26
26
|
dataloader.append_job {
|
27
27
|
lazies.each(&:value) # resolve these Lazy instances
|
28
28
|
}
|
@@ -55,7 +55,7 @@ module GraphQL
|
|
55
55
|
# these approaches.
|
56
56
|
dataloader.run
|
57
57
|
next_results = []
|
58
|
-
while results.
|
58
|
+
while !results.empty?
|
59
59
|
result_value = results.shift
|
60
60
|
if result_value.is_a?(Runtime::GraphQLResultHash) || result_value.is_a?(Hash)
|
61
61
|
results.concat(result_value.values)
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
if next_results.
|
84
|
+
if !next_results.empty?
|
85
85
|
# Any pending data loader jobs may populate the
|
86
86
|
# resutl arrays or result hashes accumulated in
|
87
87
|
# `next_results``. Run those **to completion**
|
@@ -5,7 +5,10 @@ module GraphQL
|
|
5
5
|
class Interpreter
|
6
6
|
class Runtime
|
7
7
|
module GraphQLResult
|
8
|
-
def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager)
|
8
|
+
def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager, ast_node, graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
|
9
|
+
@ast_node = ast_node
|
10
|
+
@graphql_arguments = graphql_arguments
|
11
|
+
@graphql_field = graphql_field
|
9
12
|
@graphql_parent = parent_result
|
10
13
|
@graphql_application_value = application_value
|
11
14
|
@graphql_result_type = result_type
|
@@ -31,18 +34,21 @@ module GraphQL
|
|
31
34
|
|
32
35
|
attr_accessor :graphql_dead
|
33
36
|
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent,
|
34
|
-
:graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager
|
37
|
+
:graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
|
35
38
|
|
36
39
|
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
37
40
|
attr_accessor :graphql_result_data
|
38
41
|
end
|
39
42
|
|
40
43
|
class GraphQLResultHash
|
41
|
-
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
|
44
|
+
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
|
42
45
|
super
|
43
46
|
@graphql_result_data = {}
|
47
|
+
@ordered_result_keys = nil
|
44
48
|
end
|
45
49
|
|
50
|
+
attr_accessor :ordered_result_keys
|
51
|
+
|
46
52
|
include GraphQLResult
|
47
53
|
|
48
54
|
attr_accessor :graphql_merged_into
|
@@ -60,7 +66,13 @@ module GraphQL
|
|
60
66
|
t.set_leaf(key, value)
|
61
67
|
end
|
62
68
|
|
69
|
+
before_size = @graphql_result_data.size
|
63
70
|
@graphql_result_data[key] = value
|
71
|
+
after_size = @graphql_result_data.size
|
72
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
73
|
+
fix_result_order
|
74
|
+
end
|
75
|
+
|
64
76
|
# keep this up-to-date if it's been initialized
|
65
77
|
@graphql_metadata && @graphql_metadata[key] = value
|
66
78
|
|
@@ -71,7 +83,13 @@ module GraphQL
|
|
71
83
|
if (t = @graphql_merged_into)
|
72
84
|
t.set_child_result(key, value)
|
73
85
|
end
|
86
|
+
before_size = @graphql_result_data.size
|
74
87
|
@graphql_result_data[key] = value.graphql_result_data
|
88
|
+
after_size = @graphql_result_data.size
|
89
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
90
|
+
fix_result_order
|
91
|
+
end
|
92
|
+
|
75
93
|
# If we encounter some part of this response that requires metadata tracking,
|
76
94
|
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
77
95
|
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
@@ -121,12 +139,20 @@ module GraphQL
|
|
121
139
|
end
|
122
140
|
@graphql_merged_into = into_result
|
123
141
|
end
|
142
|
+
|
143
|
+
def fix_result_order
|
144
|
+
@ordered_result_keys.each do |k|
|
145
|
+
if @graphql_result_data.key?(k)
|
146
|
+
@graphql_result_data[k] = @graphql_result_data.delete(k)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
124
150
|
end
|
125
151
|
|
126
152
|
class GraphQLResultArray
|
127
153
|
include GraphQLResult
|
128
154
|
|
129
|
-
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
|
155
|
+
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
|
130
156
|
super
|
131
157
|
@graphql_result_data = []
|
132
158
|
end
|
@@ -168,6 +194,10 @@ module GraphQL
|
|
168
194
|
def values
|
169
195
|
(@graphql_metadata || @graphql_result_data)
|
170
196
|
end
|
197
|
+
|
198
|
+
def [](idx)
|
199
|
+
(@graphql_metadata || @graphql_result_data)[idx]
|
200
|
+
end
|
171
201
|
end
|
172
202
|
end
|
173
203
|
end
|
@@ -74,7 +74,7 @@ module GraphQL
|
|
74
74
|
runtime_object = root_type.wrap(query.root_value, context)
|
75
75
|
runtime_object = schema.sync_lazy(runtime_object)
|
76
76
|
is_eager = root_op_type == "mutation"
|
77
|
-
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
|
77
|
+
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
|
78
78
|
st = get_current_runtime_state
|
79
79
|
st.current_result = @response
|
80
80
|
|
@@ -83,9 +83,11 @@ module GraphQL
|
|
83
83
|
@response = nil
|
84
84
|
else
|
85
85
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
86
|
-
each_gathered_selections(@response) do |selections, is_selection_array|
|
86
|
+
each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
|
87
|
+
@response.ordered_result_keys ||= ordered_result_keys
|
87
88
|
if is_selection_array
|
88
|
-
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
|
89
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
|
90
|
+
selection_response.ordered_result_keys = ordered_result_keys
|
89
91
|
final_response = @response
|
90
92
|
else
|
91
93
|
selection_response = @response
|
@@ -107,17 +109,19 @@ module GraphQL
|
|
107
109
|
end
|
108
110
|
|
109
111
|
def each_gathered_selections(response_hash)
|
110
|
-
|
112
|
+
ordered_result_keys = []
|
113
|
+
gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
|
114
|
+
ordered_result_keys.uniq!
|
111
115
|
if gathered_selections.is_a?(Array)
|
112
116
|
gathered_selections.each do |item|
|
113
|
-
yield(item, true)
|
117
|
+
yield(item, true, ordered_result_keys)
|
114
118
|
end
|
115
119
|
else
|
116
|
-
yield(gathered_selections, false)
|
120
|
+
yield(gathered_selections, false, ordered_result_keys)
|
117
121
|
end
|
118
122
|
end
|
119
123
|
|
120
|
-
def gather_selections(owner_object, owner_type, selections, selections_to_run
|
124
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
|
121
125
|
selections.each do |node|
|
122
126
|
# Skip gathering this if the directive says so
|
123
127
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -126,6 +130,7 @@ module GraphQL
|
|
126
130
|
|
127
131
|
if node.is_a?(GraphQL::Language::Nodes::Field)
|
128
132
|
response_key = node.alias || node.name
|
133
|
+
ordered_result_keys << response_key
|
129
134
|
selections = selections_by_name[response_key]
|
130
135
|
# if there was already a selection of this field,
|
131
136
|
# use an array to hold all selections,
|
@@ -142,7 +147,7 @@ module GraphQL
|
|
142
147
|
end
|
143
148
|
else
|
144
149
|
# This is an InlineFragment or a FragmentSpread
|
145
|
-
if
|
150
|
+
if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
146
151
|
next_selections = {}
|
147
152
|
next_selections[:graphql_directives] = node.directives
|
148
153
|
if selections_to_run
|
@@ -162,14 +167,14 @@ module GraphQL
|
|
162
167
|
type_defn = query.types.type(node.type.name)
|
163
168
|
|
164
169
|
if query.types.possible_types(type_defn).include?(owner_type)
|
165
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
170
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
166
171
|
if !result.equal?(next_selections)
|
167
172
|
selections_to_run = result
|
168
173
|
end
|
169
174
|
end
|
170
175
|
else
|
171
176
|
# it's an untyped fragment, definitely continue
|
172
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
177
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
173
178
|
if !result.equal?(next_selections)
|
174
179
|
selections_to_run = result
|
175
180
|
end
|
@@ -178,7 +183,7 @@ module GraphQL
|
|
178
183
|
fragment_def = query.fragments[node.name]
|
179
184
|
type_defn = query.types.type(fragment_def.type.name)
|
180
185
|
if query.types.possible_types(type_defn).include?(owner_type)
|
181
|
-
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
186
|
+
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
|
182
187
|
if !result.equal?(next_selections)
|
183
188
|
selections_to_run = result
|
184
189
|
end
|
@@ -207,7 +212,6 @@ module GraphQL
|
|
207
212
|
finished_jobs = 0
|
208
213
|
enqueued_jobs = gathered_selections.size
|
209
214
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
210
|
-
|
211
215
|
# Field resolution may pause the fiber,
|
212
216
|
# so it wouldn't get to the `Resolve` call that happens below.
|
213
217
|
# So instead trigger a run from this outer context.
|
@@ -218,8 +222,10 @@ module GraphQL
|
|
218
222
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
219
223
|
)
|
220
224
|
finished_jobs += 1
|
221
|
-
if
|
222
|
-
|
225
|
+
if finished_jobs == enqueued_jobs
|
226
|
+
if target_result
|
227
|
+
selections_result.merge_into(target_result)
|
228
|
+
end
|
223
229
|
end
|
224
230
|
@dataloader.clear_cache
|
225
231
|
}
|
@@ -229,8 +235,10 @@ module GraphQL
|
|
229
235
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
230
236
|
)
|
231
237
|
finished_jobs += 1
|
232
|
-
if
|
233
|
-
|
238
|
+
if finished_jobs == enqueued_jobs
|
239
|
+
if target_result
|
240
|
+
selections_result.merge_into(target_result)
|
241
|
+
end
|
234
242
|
end
|
235
243
|
}
|
236
244
|
end
|
@@ -332,7 +340,7 @@ module GraphQL
|
|
332
340
|
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
333
341
|
end
|
334
342
|
end
|
335
|
-
if extra_args.
|
343
|
+
if !extra_args.empty?
|
336
344
|
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
337
345
|
end
|
338
346
|
resolved_arguments.keyword_arguments
|
@@ -361,7 +369,7 @@ module GraphQL
|
|
361
369
|
end
|
362
370
|
|
363
371
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
364
|
-
if directives.
|
372
|
+
if !directives.empty?
|
365
373
|
# This might be executed in a different context; reset this info
|
366
374
|
runtime_state = get_current_runtime_state
|
367
375
|
runtime_state.current_field = field_defn
|
@@ -371,6 +379,7 @@ module GraphQL
|
|
371
379
|
end
|
372
380
|
# Actually call the field resolver and capture the result
|
373
381
|
app_result = begin
|
382
|
+
@current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
|
374
383
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
375
384
|
field_defn.resolve(object, kwarg_arguments, context)
|
376
385
|
end
|
@@ -383,6 +392,7 @@ module GraphQL
|
|
383
392
|
ex_err
|
384
393
|
end
|
385
394
|
end
|
395
|
+
@current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
|
386
396
|
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|
|
387
397
|
owner_type = selection_result.graphql_result_type
|
388
398
|
return_type = field_defn.type
|
@@ -391,6 +401,8 @@ module GraphQL
|
|
391
401
|
was_scoped = runtime_state.was_authorized_by_scope_items
|
392
402
|
runtime_state.was_authorized_by_scope_items = nil
|
393
403
|
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
|
404
|
+
else
|
405
|
+
nil
|
394
406
|
end
|
395
407
|
end
|
396
408
|
end
|
@@ -463,9 +475,10 @@ module GraphQL
|
|
463
475
|
if is_non_null
|
464
476
|
set_result(selection_result, result_name, nil, false, is_non_null) do
|
465
477
|
# When this comes from a list item, use the parent object:
|
466
|
-
|
478
|
+
is_from_array = selection_result.is_a?(GraphQLResultArray)
|
479
|
+
parent_type = is_from_array ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
467
480
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
468
|
-
err = parent_type::InvalidNullError.new(parent_type, field,
|
481
|
+
err = parent_type::InvalidNullError.new(parent_type, field, ast_node, is_from_array: is_from_array)
|
469
482
|
schema.type_error(err, context)
|
470
483
|
end
|
471
484
|
else
|
@@ -525,7 +538,7 @@ module GraphQL
|
|
525
538
|
end
|
526
539
|
when Array
|
527
540
|
# It's an array full of execution errors; add them all.
|
528
|
-
if value.
|
541
|
+
if !value.empty? && value.all?(GraphQL::ExecutionError)
|
529
542
|
list_type_at_all = (field && (field.type.list?))
|
530
543
|
if selection_result.nil? || !selection_result.graphql_dead
|
531
544
|
value.each_with_index do |error, index|
|
@@ -574,12 +587,22 @@ module GraphQL
|
|
574
587
|
r = begin
|
575
588
|
current_type.coerce_result(value, context)
|
576
589
|
rescue StandardError => err
|
577
|
-
|
590
|
+
query.handle_or_reraise(err)
|
578
591
|
end
|
579
592
|
set_result(selection_result, result_name, r, false, is_non_null)
|
580
593
|
r
|
581
594
|
when "UNION", "INTERFACE"
|
582
|
-
resolved_type_or_lazy =
|
595
|
+
resolved_type_or_lazy = begin
|
596
|
+
resolve_type(current_type, value)
|
597
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
598
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
599
|
+
rescue StandardError => err
|
600
|
+
begin
|
601
|
+
query.handle_or_reraise(err)
|
602
|
+
rescue GraphQL::ExecutionError => ex_err
|
603
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
604
|
+
end
|
605
|
+
end
|
583
606
|
after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
|
584
607
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
585
608
|
resolved_type, resolved_value = resolved_type_result
|
@@ -609,11 +632,13 @@ module GraphQL
|
|
609
632
|
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
|
610
633
|
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
611
634
|
if HALT != continue_value
|
612
|
-
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
635
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
613
636
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
614
|
-
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
637
|
+
each_gathered_selections(response_hash) do |selections, is_selection_array, ordered_result_keys|
|
638
|
+
response_hash.ordered_result_keys ||= ordered_result_keys
|
615
639
|
if is_selection_array
|
616
|
-
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
640
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
|
641
|
+
this_result.ordered_result_keys = ordered_result_keys
|
617
642
|
final_result = response_hash
|
618
643
|
else
|
619
644
|
this_result = response_hash
|
@@ -634,35 +659,43 @@ module GraphQL
|
|
634
659
|
# This is true for objects, unions, and interfaces
|
635
660
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
636
661
|
inner_type_non_null = inner_type.non_null?
|
637
|
-
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
|
662
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
638
663
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
639
664
|
idx = nil
|
640
665
|
list_value = begin
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
666
|
+
begin
|
667
|
+
value.each do |inner_value|
|
668
|
+
idx ||= 0
|
669
|
+
this_idx = idx
|
670
|
+
idx += 1
|
671
|
+
if use_dataloader_job
|
672
|
+
@dataloader.append_job do
|
673
|
+
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)
|
674
|
+
end
|
675
|
+
else
|
647
676
|
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)
|
648
677
|
end
|
649
|
-
else
|
650
|
-
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)
|
651
678
|
end
|
652
|
-
end
|
653
679
|
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
680
|
+
response_list
|
681
|
+
rescue NoMethodError => err
|
682
|
+
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
683
|
+
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
684
|
+
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
685
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
686
|
+
else
|
687
|
+
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
688
|
+
raise
|
689
|
+
end
|
690
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
691
|
+
ex_err
|
692
|
+
rescue StandardError => err
|
693
|
+
begin
|
694
|
+
query.handle_or_reraise(err)
|
695
|
+
rescue GraphQL::ExecutionError => ex_err
|
696
|
+
ex_err
|
697
|
+
end
|
663
698
|
end
|
664
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
665
|
-
ex_err
|
666
699
|
rescue StandardError => err
|
667
700
|
begin
|
668
701
|
query.handle_or_reraise(err)
|
@@ -736,7 +769,7 @@ module GraphQL
|
|
736
769
|
end
|
737
770
|
|
738
771
|
def get_current_runtime_state
|
739
|
-
current_state =
|
772
|
+
current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
|
740
773
|
current_state[@query] ||= CurrentState.new
|
741
774
|
end
|
742
775
|
|
@@ -773,8 +806,10 @@ module GraphQL
|
|
773
806
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
774
807
|
# Wrap the execution of _this_ method with tracing,
|
775
808
|
# but don't wrap the continuation below
|
809
|
+
result = nil
|
776
810
|
inner_obj = begin
|
777
|
-
if trace
|
811
|
+
result = if trace
|
812
|
+
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
778
813
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
779
814
|
schema.sync_lazy(lazy_obj)
|
780
815
|
end
|
@@ -789,6 +824,10 @@ module GraphQL
|
|
789
824
|
rescue GraphQL::ExecutionError => ex_err
|
790
825
|
ex_err
|
791
826
|
end
|
827
|
+
ensure
|
828
|
+
if trace
|
829
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, result)
|
830
|
+
end
|
792
831
|
end
|
793
832
|
yield(inner_obj, runtime_state)
|
794
833
|
end
|
@@ -821,25 +860,30 @@ module GraphQL
|
|
821
860
|
end
|
822
861
|
|
823
862
|
def delete_all_interpreter_context
|
824
|
-
per_query_state =
|
863
|
+
per_query_state = Fiber[:__graphql_runtime_info]
|
825
864
|
if per_query_state
|
826
865
|
per_query_state.delete(@query)
|
827
866
|
if per_query_state.size == 0
|
828
|
-
|
867
|
+
Fiber[:__graphql_runtime_info] = nil
|
829
868
|
end
|
830
869
|
end
|
831
870
|
nil
|
832
871
|
end
|
833
872
|
|
834
873
|
def resolve_type(type, value)
|
874
|
+
@current_trace.begin_resolve_type(type, value, context)
|
835
875
|
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
836
876
|
query.resolve_type(type, value)
|
837
877
|
end
|
878
|
+
@current_trace.end_resolve_type(type, value, context, resolved_type)
|
838
879
|
|
839
880
|
if lazy?(resolved_type)
|
840
881
|
GraphQL::Execution::Lazy.new do
|
882
|
+
@current_trace.begin_resolve_type(type, value, context)
|
841
883
|
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
842
|
-
schema.sync_lazy(resolved_type)
|
884
|
+
rt = schema.sync_lazy(resolved_type)
|
885
|
+
@current_trace.end_resolve_type(type, value, context, rt)
|
886
|
+
rt
|
843
887
|
end
|
844
888
|
end
|
845
889
|
else
|