graphql 2.4.5 → 2.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +37 -40
- data/lib/graphql/analysis.rb +12 -9
- data/lib/graphql/autoload.rb +1 -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/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -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/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -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 +24 -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 +96 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +38 -15
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/source.rb +18 -6
- data/lib/graphql/dataloader.rb +110 -26
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
- data/lib/graphql/execution/interpreter/runtime.rb +229 -93
- data/lib/graphql/execution/interpreter.rb +15 -24
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +69 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +25 -16
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +16 -5
- data/lib/graphql/language/nodes.rb +8 -1
- data/lib/graphql/language/parser.rb +16 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +6 -10
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +64 -64
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +24 -8
- data/lib/graphql/schema/build_from_definition.rb +113 -54
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +52 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +101 -51
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/input_object.rb +45 -38
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +56 -19
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +79 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +81 -5
- data/lib/graphql/schema/member/has_interfaces.rb +3 -3
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/object.rb +18 -8
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +52 -6
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +71 -14
- data/lib/graphql/schema/visibility/migration.rb +3 -2
- data/lib/graphql/schema/visibility/profile.rb +115 -23
- data/lib/graphql/schema/visibility.rb +49 -32
- data/lib/graphql/schema/warden.rb +23 -2
- data/lib/graphql/schema.rb +333 -68
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- 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/not_single_subscription_error.rb +25 -0
- 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 +6 -2
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- 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 +17 -11
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- 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/active_record_backend.rb +74 -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 +156 -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 +184 -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 +864 -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 +64 -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/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
- data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
- 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 +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +12 -31
- metadata +174 -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
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
def request(value)
|
|
22
22
|
res_key = result_key_for(value)
|
|
23
23
|
if !@results.key?(res_key)
|
|
24
|
-
@pending[res_key] ||= value
|
|
24
|
+
@pending[res_key] ||= normalize_fetch_key(value)
|
|
25
25
|
end
|
|
26
26
|
Dataloader::Request.new(self, value)
|
|
27
27
|
end
|
|
@@ -35,12 +35,24 @@ module GraphQL
|
|
|
35
35
|
value
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# Implement this method if varying values given to {load} (etc) should be consolidated
|
|
39
|
+
# or normalized before being handed off to your {fetch} implementation.
|
|
40
|
+
#
|
|
41
|
+
# This is different than {result_key_for} because _that_ method handles unification inside Dataloader's cache,
|
|
42
|
+
# but this method changes the value passed into {fetch}.
|
|
43
|
+
#
|
|
44
|
+
# @param value [Object] The value passed to {load}, {load_all}, {request}, or {request_all}
|
|
45
|
+
# @return [Object] The value given to {fetch}
|
|
46
|
+
def normalize_fetch_key(value)
|
|
47
|
+
value
|
|
48
|
+
end
|
|
49
|
+
|
|
38
50
|
# @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
|
|
39
51
|
def request_all(values)
|
|
40
52
|
values.each do |v|
|
|
41
53
|
res_key = result_key_for(v)
|
|
42
54
|
if !@results.key?(res_key)
|
|
43
|
-
@pending[res_key] ||= v
|
|
55
|
+
@pending[res_key] ||= normalize_fetch_key(v)
|
|
44
56
|
end
|
|
45
57
|
end
|
|
46
58
|
Dataloader::RequestAll.new(self, values)
|
|
@@ -53,7 +65,7 @@ module GraphQL
|
|
|
53
65
|
if @results.key?(result_key)
|
|
54
66
|
result_for(result_key)
|
|
55
67
|
else
|
|
56
|
-
@pending[result_key] ||= value
|
|
68
|
+
@pending[result_key] ||= normalize_fetch_key(value)
|
|
57
69
|
sync([result_key])
|
|
58
70
|
result_for(result_key)
|
|
59
71
|
end
|
|
@@ -68,7 +80,7 @@ module GraphQL
|
|
|
68
80
|
k = result_key_for(v)
|
|
69
81
|
result_keys << k
|
|
70
82
|
if !@results.key?(k)
|
|
71
|
-
@pending[k] ||= v
|
|
83
|
+
@pending[k] ||= normalize_fetch_key(v)
|
|
72
84
|
pending_keys << k
|
|
73
85
|
end
|
|
74
86
|
}
|
|
@@ -93,14 +105,14 @@ module GraphQL
|
|
|
93
105
|
# Then run the batch and update the cache.
|
|
94
106
|
# @return [void]
|
|
95
107
|
def sync(pending_result_keys)
|
|
96
|
-
@dataloader.yield
|
|
108
|
+
@dataloader.yield(self)
|
|
97
109
|
iterations = 0
|
|
98
110
|
while pending_result_keys.any? { |key| !@results.key?(key) }
|
|
99
111
|
iterations += 1
|
|
100
112
|
if iterations > MAX_ITERATIONS
|
|
101
113
|
raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
|
|
102
114
|
end
|
|
103
|
-
@dataloader.yield
|
|
115
|
+
@dataloader.yield(self)
|
|
104
116
|
end
|
|
105
117
|
nil
|
|
106
118
|
end
|
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}.
|
|
@@ -62,6 +64,7 @@ module GraphQL
|
|
|
62
64
|
@nonblocking = nonblocking
|
|
63
65
|
end
|
|
64
66
|
@fiber_limit = fiber_limit
|
|
67
|
+
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
65
68
|
end
|
|
66
69
|
|
|
67
70
|
# @return [Integer, nil]
|
|
@@ -129,16 +132,19 @@ module GraphQL
|
|
|
129
132
|
# Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
|
|
130
133
|
#
|
|
131
134
|
# @return [void]
|
|
132
|
-
def yield
|
|
135
|
+
def yield(source = Fiber[:__graphql_current_dataloader_source])
|
|
136
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
137
|
+
trace&.dataloader_fiber_yield(source)
|
|
133
138
|
Fiber.yield
|
|
139
|
+
trace&.dataloader_fiber_resume(source)
|
|
134
140
|
nil
|
|
135
141
|
end
|
|
136
142
|
|
|
137
143
|
# @api private Nothing to see here
|
|
138
|
-
def append_job(&job)
|
|
144
|
+
def append_job(callable = nil, &job)
|
|
139
145
|
# Given a block, queue it up to be worked through when `#run` is called.
|
|
140
|
-
# (If the dataloader is already running,
|
|
141
|
-
@pending_jobs.push(job)
|
|
146
|
+
# (If the dataloader is already running, then a Fiber will pick this up later.)
|
|
147
|
+
@pending_jobs.push(callable || job)
|
|
142
148
|
nil
|
|
143
149
|
end
|
|
144
150
|
|
|
@@ -155,6 +161,10 @@ module GraphQL
|
|
|
155
161
|
def run_isolated
|
|
156
162
|
prev_queue = @pending_jobs
|
|
157
163
|
prev_pending_keys = {}
|
|
164
|
+
prev_lazies_at_depth = @lazies_at_depth
|
|
165
|
+
@lazies_at_depth = @lazies_at_depth.dup.clear
|
|
166
|
+
# Clear pending loads but keep already-cached records
|
|
167
|
+
# in case they are useful to the given block.
|
|
158
168
|
@source_cache.each do |source_class, batched_sources|
|
|
159
169
|
batched_sources.each do |batch_args, batched_source_instance|
|
|
160
170
|
if batched_source_instance.pending?
|
|
@@ -174,6 +184,7 @@ module GraphQL
|
|
|
174
184
|
res
|
|
175
185
|
ensure
|
|
176
186
|
@pending_jobs = prev_queue
|
|
187
|
+
@lazies_at_depth = prev_lazies_at_depth
|
|
177
188
|
prev_pending_keys.each do |source_instance, pending|
|
|
178
189
|
pending.each do |key, value|
|
|
179
190
|
if !source_instance.results.key?(key)
|
|
@@ -183,7 +194,9 @@ module GraphQL
|
|
|
183
194
|
end
|
|
184
195
|
end
|
|
185
196
|
|
|
186
|
-
|
|
197
|
+
# @param trace_query_lazy [nil, Execution::Multiplex]
|
|
198
|
+
def run(trace_query_lazy: nil)
|
|
199
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
187
200
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
188
201
|
job_fibers = []
|
|
189
202
|
next_job_fibers = []
|
|
@@ -191,31 +204,21 @@ module GraphQL
|
|
|
191
204
|
next_source_fibers = []
|
|
192
205
|
first_pass = true
|
|
193
206
|
manager = spawn_fiber do
|
|
207
|
+
trace&.begin_dataloader(self)
|
|
194
208
|
while first_pass || !job_fibers.empty?
|
|
195
209
|
first_pass = false
|
|
196
210
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
join_queues(job_fibers, next_job_fibers)
|
|
206
|
-
|
|
207
|
-
while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
208
|
-
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))
|
|
209
|
-
if f.alive?
|
|
210
|
-
finished = run_fiber(f)
|
|
211
|
-
if !finished
|
|
212
|
-
next_source_fibers << f
|
|
213
|
-
end
|
|
214
|
-
end
|
|
211
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
212
|
+
|
|
213
|
+
if !@lazies_at_depth.empty?
|
|
214
|
+
with_trace_query_lazy(trace_query_lazy) do
|
|
215
|
+
run_next_pending_lazies(job_fibers, trace)
|
|
216
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
215
217
|
end
|
|
216
|
-
join_queues(source_fibers, next_source_fibers)
|
|
217
218
|
end
|
|
218
219
|
end
|
|
220
|
+
|
|
221
|
+
trace&.end_dataloader(self)
|
|
219
222
|
end
|
|
220
223
|
|
|
221
224
|
run_fiber(manager)
|
|
@@ -230,6 +233,7 @@ module GraphQL
|
|
|
230
233
|
if !source_fibers.empty?
|
|
231
234
|
raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
|
|
232
235
|
end
|
|
236
|
+
|
|
233
237
|
rescue UncaughtThrowError => e
|
|
234
238
|
throw e.tag, e.value
|
|
235
239
|
end
|
|
@@ -238,6 +242,11 @@ module GraphQL
|
|
|
238
242
|
f.resume
|
|
239
243
|
end
|
|
240
244
|
|
|
245
|
+
# @api private
|
|
246
|
+
def lazy_at_depth(depth, lazy)
|
|
247
|
+
@lazies_at_depth[depth] << lazy
|
|
248
|
+
end
|
|
249
|
+
|
|
241
250
|
def spawn_fiber
|
|
242
251
|
fiber_vars = get_fiber_variables
|
|
243
252
|
Fiber.new(blocking: !@nonblocking) {
|
|
@@ -247,8 +256,77 @@ module GraphQL
|
|
|
247
256
|
}
|
|
248
257
|
end
|
|
249
258
|
|
|
259
|
+
# Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
|
|
260
|
+
# These will be used by {Dataloader::ActiveRecordSource}, {Dataloader::ActiveRecordAssociationSource} and their helper
|
|
261
|
+
# methods, `dataload_record` and `dataload_association`.
|
|
262
|
+
# @param records [Array<ActiveRecord::Base>] Already-loaded records to warm the cache with
|
|
263
|
+
# @param index_by [Symbol] The attribute to use as the cache key. (Should match `find_by:` when using {ActiveRecordSource})
|
|
264
|
+
# @return [void]
|
|
265
|
+
def merge_records(records, index_by: :id)
|
|
266
|
+
records_by_class = Hash.new { |h, k| h[k] = {} }
|
|
267
|
+
records.each do |r|
|
|
268
|
+
records_by_class[r.class][r.public_send(index_by)] = r
|
|
269
|
+
end
|
|
270
|
+
records_by_class.each do |r_class, records|
|
|
271
|
+
with(ActiveRecordSource, r_class).merge(records)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
250
275
|
private
|
|
251
276
|
|
|
277
|
+
def run_next_pending_lazies(job_fibers, trace)
|
|
278
|
+
smallest_depth = nil
|
|
279
|
+
@lazies_at_depth.each_key do |depth_key|
|
|
280
|
+
smallest_depth ||= depth_key
|
|
281
|
+
if depth_key < smallest_depth
|
|
282
|
+
smallest_depth = depth_key
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if smallest_depth
|
|
287
|
+
lazies = @lazies_at_depth.delete(smallest_depth)
|
|
288
|
+
if !lazies.empty?
|
|
289
|
+
lazies.each_with_index do |l, idx|
|
|
290
|
+
append_job { l.value }
|
|
291
|
+
end
|
|
292
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
298
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
|
|
299
|
+
if f.alive?
|
|
300
|
+
finished = run_fiber(f)
|
|
301
|
+
if !finished
|
|
302
|
+
next_job_fibers << f
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
join_queues(job_fibers, next_job_fibers)
|
|
307
|
+
|
|
308
|
+
while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
309
|
+
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
310
|
+
if f.alive?
|
|
311
|
+
finished = run_fiber(f)
|
|
312
|
+
if !finished
|
|
313
|
+
next_source_fibers << f
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
join_queues(source_fibers, next_source_fibers)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def with_trace_query_lazy(multiplex_or_nil, &block)
|
|
322
|
+
if (multiplex = multiplex_or_nil)
|
|
323
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
324
|
+
multiplex.current_trace.execute_query_lazy(query: query, multiplex: multiplex, &block)
|
|
325
|
+
else
|
|
326
|
+
yield
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
252
330
|
def calculate_fiber_limit
|
|
253
331
|
total_fiber_limit = @fiber_limit || Float::INFINITY
|
|
254
332
|
if total_fiber_limit < 4
|
|
@@ -266,17 +344,19 @@ module GraphQL
|
|
|
266
344
|
new_queue.clear
|
|
267
345
|
end
|
|
268
346
|
|
|
269
|
-
def spawn_job_fiber
|
|
347
|
+
def spawn_job_fiber(trace)
|
|
270
348
|
if !@pending_jobs.empty?
|
|
271
349
|
spawn_fiber do
|
|
350
|
+
trace&.dataloader_spawn_execution_fiber(@pending_jobs)
|
|
272
351
|
while job = @pending_jobs.shift
|
|
273
352
|
job.call
|
|
274
353
|
end
|
|
354
|
+
trace&.dataloader_fiber_exit
|
|
275
355
|
end
|
|
276
356
|
end
|
|
277
357
|
end
|
|
278
358
|
|
|
279
|
-
def spawn_source_fiber
|
|
359
|
+
def spawn_source_fiber(trace)
|
|
280
360
|
pending_sources = nil
|
|
281
361
|
@source_cache.each_value do |source_by_batch_params|
|
|
282
362
|
source_by_batch_params.each_value do |source|
|
|
@@ -289,10 +369,14 @@ module GraphQL
|
|
|
289
369
|
|
|
290
370
|
if pending_sources
|
|
291
371
|
spawn_fiber do
|
|
372
|
+
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
292
373
|
pending_sources.each do |source|
|
|
293
374
|
Fiber[:__graphql_current_dataloader_source] = source
|
|
375
|
+
trace&.begin_dataloader_source(source)
|
|
294
376
|
source.run_pending_keys
|
|
377
|
+
trace&.end_dataloader_source(source)
|
|
295
378
|
end
|
|
379
|
+
trace&.dataloader_fiber_exit
|
|
296
380
|
end
|
|
297
381
|
end
|
|
298
382
|
end
|
|
@@ -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
|
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]
|
|
@@ -6,12 +6,17 @@ module GraphQL
|
|
|
6
6
|
module Resolve
|
|
7
7
|
# Continue field results in `results` until there's nothing else to continue.
|
|
8
8
|
# @return [void]
|
|
9
|
+
# @deprecated Call `dataloader.run` instead
|
|
9
10
|
def self.resolve_all(results, dataloader)
|
|
11
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
10
12
|
dataloader.append_job { resolve(results, dataloader) }
|
|
11
13
|
nil
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
# @deprecated Call `dataloader.run` instead
|
|
14
17
|
def self.resolve_each_depth(lazies_at_depth, dataloader)
|
|
18
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
19
|
+
|
|
15
20
|
smallest_depth = nil
|
|
16
21
|
lazies_at_depth.each_key do |depth_key|
|
|
17
22
|
smallest_depth ||= depth_key
|
|
@@ -23,9 +28,9 @@ module GraphQL
|
|
|
23
28
|
if smallest_depth
|
|
24
29
|
lazies = lazies_at_depth.delete(smallest_depth)
|
|
25
30
|
if !lazies.empty?
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
lazies.each do |l|
|
|
32
|
+
dataloader.append_job { l.value }
|
|
33
|
+
end
|
|
29
34
|
# Run lazies _and_ dataloader, see if more are enqueued
|
|
30
35
|
dataloader.run
|
|
31
36
|
resolve_each_depth(lazies_at_depth, dataloader)
|
|
@@ -34,20 +39,9 @@ module GraphQL
|
|
|
34
39
|
nil
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
#
|
|
38
|
-
# continue it until you get a response-ready Ruby value.
|
|
39
|
-
#
|
|
40
|
-
# `results` is one level of _depth_ of a query or multiplex.
|
|
41
|
-
#
|
|
42
|
-
# Resolve all lazy values in that depth before moving on
|
|
43
|
-
# to the next level.
|
|
44
|
-
#
|
|
45
|
-
# It's assumed that the lazies will
|
|
46
|
-
# return {Lazy} instances if there's more work to be done,
|
|
47
|
-
# or return {Hash}/{Array} if the query should be continued.
|
|
48
|
-
#
|
|
49
|
-
# @return [void]
|
|
42
|
+
# @deprecated Call `dataloader.run` instead
|
|
50
43
|
def self.resolve(results, dataloader)
|
|
44
|
+
warn "#{self}.#{__method__} is deprecated; Use `dataloader.run` instead.#{caller(1, 5).map { |l| "\n #{l}"}.join}"
|
|
51
45
|
# There might be pending jobs here that _will_ write lazies
|
|
52
46
|
# into the result hash. We should run them out, so we
|
|
53
47
|
# can be sure that all lazies will be present in the result hashes.
|
|
@@ -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
|
|
@@ -18,31 +21,52 @@ module GraphQL
|
|
|
18
21
|
@graphql_metadata = nil
|
|
19
22
|
@graphql_selections = selections
|
|
20
23
|
@graphql_is_eager = is_eager
|
|
24
|
+
@base_path = nil
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
# TODO test full path in Partial
|
|
28
|
+
attr_writer :base_path
|
|
29
|
+
|
|
23
30
|
def path
|
|
24
31
|
@path ||= build_path([])
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
def build_path(path_array)
|
|
28
35
|
graphql_result_name && path_array.unshift(graphql_result_name)
|
|
29
|
-
|
|
36
|
+
if @graphql_parent
|
|
37
|
+
@graphql_parent.build_path(path_array)
|
|
38
|
+
elsif @base_path
|
|
39
|
+
@base_path + path_array
|
|
40
|
+
else
|
|
41
|
+
path_array
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def depth
|
|
46
|
+
@depth ||= if @graphql_parent
|
|
47
|
+
@graphql_parent.depth + 1
|
|
48
|
+
else
|
|
49
|
+
1
|
|
50
|
+
end
|
|
30
51
|
end
|
|
31
52
|
|
|
32
53
|
attr_accessor :graphql_dead
|
|
33
54
|
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
|
|
55
|
+
:graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
|
|
35
56
|
|
|
36
57
|
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
|
37
58
|
attr_accessor :graphql_result_data
|
|
38
59
|
end
|
|
39
60
|
|
|
40
61
|
class GraphQLResultHash
|
|
41
|
-
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
|
|
62
|
+
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
63
|
super
|
|
43
64
|
@graphql_result_data = {}
|
|
65
|
+
@ordered_result_keys = nil
|
|
44
66
|
end
|
|
45
67
|
|
|
68
|
+
attr_accessor :ordered_result_keys
|
|
69
|
+
|
|
46
70
|
include GraphQLResult
|
|
47
71
|
|
|
48
72
|
attr_accessor :graphql_merged_into
|
|
@@ -60,7 +84,13 @@ module GraphQL
|
|
|
60
84
|
t.set_leaf(key, value)
|
|
61
85
|
end
|
|
62
86
|
|
|
87
|
+
before_size = @graphql_result_data.size
|
|
63
88
|
@graphql_result_data[key] = value
|
|
89
|
+
after_size = @graphql_result_data.size
|
|
90
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
|
91
|
+
fix_result_order
|
|
92
|
+
end
|
|
93
|
+
|
|
64
94
|
# keep this up-to-date if it's been initialized
|
|
65
95
|
@graphql_metadata && @graphql_metadata[key] = value
|
|
66
96
|
|
|
@@ -71,7 +101,13 @@ module GraphQL
|
|
|
71
101
|
if (t = @graphql_merged_into)
|
|
72
102
|
t.set_child_result(key, value)
|
|
73
103
|
end
|
|
104
|
+
before_size = @graphql_result_data.size
|
|
74
105
|
@graphql_result_data[key] = value.graphql_result_data
|
|
106
|
+
after_size = @graphql_result_data.size
|
|
107
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
|
108
|
+
fix_result_order
|
|
109
|
+
end
|
|
110
|
+
|
|
75
111
|
# If we encounter some part of this response that requires metadata tracking,
|
|
76
112
|
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
|
77
113
|
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
|
@@ -121,12 +157,25 @@ module GraphQL
|
|
|
121
157
|
end
|
|
122
158
|
@graphql_merged_into = into_result
|
|
123
159
|
end
|
|
160
|
+
|
|
161
|
+
def fix_result_order
|
|
162
|
+
@ordered_result_keys.each do |k|
|
|
163
|
+
if @graphql_result_data.key?(k)
|
|
164
|
+
@graphql_result_data[k] = @graphql_result_data.delete(k)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# hook for breadth-first implementations to signal when collecting results.
|
|
170
|
+
def collect_result(result_name, result_value)
|
|
171
|
+
false
|
|
172
|
+
end
|
|
124
173
|
end
|
|
125
174
|
|
|
126
175
|
class GraphQLResultArray
|
|
127
176
|
include GraphQLResult
|
|
128
177
|
|
|
129
|
-
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
|
|
178
|
+
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
179
|
super
|
|
131
180
|
@graphql_result_data = []
|
|
132
181
|
end
|
|
@@ -168,6 +217,10 @@ module GraphQL
|
|
|
168
217
|
def values
|
|
169
218
|
(@graphql_metadata || @graphql_result_data)
|
|
170
219
|
end
|
|
220
|
+
|
|
221
|
+
def [](idx)
|
|
222
|
+
(@graphql_metadata || @graphql_result_data)[idx]
|
|
223
|
+
end
|
|
171
224
|
end
|
|
172
225
|
end
|
|
173
226
|
end
|