graphql 2.4.3 → 2.5.2
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/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 +6 -1
- 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/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +94 -51
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +1 -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 +5 -15
- 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 +6 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +19 -23
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- 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/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +60 -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 +95 -243
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +93 -44
- 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 +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- 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
|
@@ -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
|
@@ -465,7 +477,7 @@ module GraphQL
|
|
465
477
|
# When this comes from a list item, use the parent object:
|
466
478
|
parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
467
479
|
# 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,
|
480
|
+
err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
|
469
481
|
schema.type_error(err, context)
|
470
482
|
end
|
471
483
|
else
|
@@ -525,7 +537,7 @@ module GraphQL
|
|
525
537
|
end
|
526
538
|
when Array
|
527
539
|
# It's an array full of execution errors; add them all.
|
528
|
-
if value.
|
540
|
+
if !value.empty? && value.all?(GraphQL::ExecutionError)
|
529
541
|
list_type_at_all = (field && (field.type.list?))
|
530
542
|
if selection_result.nil? || !selection_result.graphql_dead
|
531
543
|
value.each_with_index do |error, index|
|
@@ -574,12 +586,22 @@ module GraphQL
|
|
574
586
|
r = begin
|
575
587
|
current_type.coerce_result(value, context)
|
576
588
|
rescue StandardError => err
|
577
|
-
|
589
|
+
query.handle_or_reraise(err)
|
578
590
|
end
|
579
591
|
set_result(selection_result, result_name, r, false, is_non_null)
|
580
592
|
r
|
581
593
|
when "UNION", "INTERFACE"
|
582
|
-
resolved_type_or_lazy =
|
594
|
+
resolved_type_or_lazy = begin
|
595
|
+
resolve_type(current_type, value)
|
596
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
597
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
598
|
+
rescue StandardError => err
|
599
|
+
begin
|
600
|
+
query.handle_or_reraise(err)
|
601
|
+
rescue GraphQL::ExecutionError => ex_err
|
602
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
603
|
+
end
|
604
|
+
end
|
583
605
|
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
606
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
585
607
|
resolved_type, resolved_value = resolved_type_result
|
@@ -609,11 +631,13 @@ module GraphQL
|
|
609
631
|
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
632
|
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
611
633
|
if HALT != continue_value
|
612
|
-
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
634
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
613
635
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
614
|
-
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
636
|
+
each_gathered_selections(response_hash) do |selections, is_selection_array, ordered_result_keys|
|
637
|
+
response_hash.ordered_result_keys ||= ordered_result_keys
|
615
638
|
if is_selection_array
|
616
|
-
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
639
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
|
640
|
+
this_result.ordered_result_keys = ordered_result_keys
|
617
641
|
final_result = response_hash
|
618
642
|
else
|
619
643
|
this_result = response_hash
|
@@ -634,35 +658,43 @@ module GraphQL
|
|
634
658
|
# This is true for objects, unions, and interfaces
|
635
659
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
636
660
|
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)
|
661
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
638
662
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
639
663
|
idx = nil
|
640
664
|
list_value = begin
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
665
|
+
begin
|
666
|
+
value.each do |inner_value|
|
667
|
+
idx ||= 0
|
668
|
+
this_idx = idx
|
669
|
+
idx += 1
|
670
|
+
if use_dataloader_job
|
671
|
+
@dataloader.append_job do
|
672
|
+
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)
|
673
|
+
end
|
674
|
+
else
|
647
675
|
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
676
|
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
677
|
end
|
652
|
-
end
|
653
678
|
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
679
|
+
response_list
|
680
|
+
rescue NoMethodError => err
|
681
|
+
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
682
|
+
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
683
|
+
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
684
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
685
|
+
else
|
686
|
+
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
687
|
+
raise
|
688
|
+
end
|
689
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
690
|
+
ex_err
|
691
|
+
rescue StandardError => err
|
692
|
+
begin
|
693
|
+
query.handle_or_reraise(err)
|
694
|
+
rescue GraphQL::ExecutionError => ex_err
|
695
|
+
ex_err
|
696
|
+
end
|
663
697
|
end
|
664
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
665
|
-
ex_err
|
666
698
|
rescue StandardError => err
|
667
699
|
begin
|
668
700
|
query.handle_or_reraise(err)
|
@@ -736,7 +768,7 @@ module GraphQL
|
|
736
768
|
end
|
737
769
|
|
738
770
|
def get_current_runtime_state
|
739
|
-
current_state =
|
771
|
+
current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
|
740
772
|
current_state[@query] ||= CurrentState.new
|
741
773
|
end
|
742
774
|
|
@@ -773,8 +805,10 @@ module GraphQL
|
|
773
805
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
774
806
|
# Wrap the execution of _this_ method with tracing,
|
775
807
|
# but don't wrap the continuation below
|
808
|
+
result = nil
|
776
809
|
inner_obj = begin
|
777
|
-
if trace
|
810
|
+
result = if trace
|
811
|
+
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
778
812
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
779
813
|
schema.sync_lazy(lazy_obj)
|
780
814
|
end
|
@@ -789,6 +823,10 @@ module GraphQL
|
|
789
823
|
rescue GraphQL::ExecutionError => ex_err
|
790
824
|
ex_err
|
791
825
|
end
|
826
|
+
ensure
|
827
|
+
if trace
|
828
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, result)
|
829
|
+
end
|
792
830
|
end
|
793
831
|
yield(inner_obj, runtime_state)
|
794
832
|
end
|
@@ -821,25 +859,30 @@ module GraphQL
|
|
821
859
|
end
|
822
860
|
|
823
861
|
def delete_all_interpreter_context
|
824
|
-
per_query_state =
|
862
|
+
per_query_state = Fiber[:__graphql_runtime_info]
|
825
863
|
if per_query_state
|
826
864
|
per_query_state.delete(@query)
|
827
865
|
if per_query_state.size == 0
|
828
|
-
|
866
|
+
Fiber[:__graphql_runtime_info] = nil
|
829
867
|
end
|
830
868
|
end
|
831
869
|
nil
|
832
870
|
end
|
833
871
|
|
834
872
|
def resolve_type(type, value)
|
873
|
+
@current_trace.begin_resolve_type(type, value, context)
|
835
874
|
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
836
875
|
query.resolve_type(type, value)
|
837
876
|
end
|
877
|
+
@current_trace.end_resolve_type(type, value, context, resolved_type)
|
838
878
|
|
839
879
|
if lazy?(resolved_type)
|
840
880
|
GraphQL::Execution::Lazy.new do
|
881
|
+
@current_trace.begin_resolve_type(type, value, context)
|
841
882
|
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
842
|
-
schema.sync_lazy(resolved_type)
|
883
|
+
rt = schema.sync_lazy(resolved_type)
|
884
|
+
@current_trace.end_resolve_type(type, value, context, rt)
|
885
|
+
rt
|
843
886
|
end
|
844
887
|
end
|
845
888
|
else
|