graphql 2.4.5 → 2.5.0
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 +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 +5 -0
- 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 +17 -5
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/dataloader.rb +37 -5
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +71 -34
- data/lib/graphql/execution/interpreter.rb +12 -3
- 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/lexer.rb +7 -3
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/query.rb +7 -13
- data/lib/graphql/railtie.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.rb +1 -1
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field.rb +2 -2
- data/lib/graphql/schema/input_object.rb +16 -16
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +13 -5
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/has_interfaces.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 +17 -8
- data/lib/graphql/schema/resolver.rb +6 -5
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +11 -5
- data/lib/graphql/schema/visibility.rb +14 -9
- data/lib/graphql/schema/warden.rb +14 -1
- data/lib/graphql/schema.rb +63 -35
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- 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/validator.rb +6 -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/testing/helpers.rb +5 -2
- 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 +285 -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 +63 -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 +1 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +4 -29
- metadata +144 -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}.
|
|
@@ -129,8 +131,11 @@ module GraphQL
|
|
|
129
131
|
# Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
|
|
130
132
|
#
|
|
131
133
|
# @return [void]
|
|
132
|
-
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)
|
|
133
137
|
Fiber.yield
|
|
138
|
+
trace&.dataloader_fiber_resume(source)
|
|
134
139
|
nil
|
|
135
140
|
end
|
|
136
141
|
|
|
@@ -184,6 +189,7 @@ module GraphQL
|
|
|
184
189
|
end
|
|
185
190
|
|
|
186
191
|
def run
|
|
192
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
187
193
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
188
194
|
job_fibers = []
|
|
189
195
|
next_job_fibers = []
|
|
@@ -191,10 +197,11 @@ module GraphQL
|
|
|
191
197
|
next_source_fibers = []
|
|
192
198
|
first_pass = true
|
|
193
199
|
manager = spawn_fiber do
|
|
200
|
+
trace&.begin_dataloader(self)
|
|
194
201
|
while first_pass || !job_fibers.empty?
|
|
195
202
|
first_pass = false
|
|
196
203
|
|
|
197
|
-
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))))
|
|
198
205
|
if f.alive?
|
|
199
206
|
finished = run_fiber(f)
|
|
200
207
|
if !finished
|
|
@@ -205,7 +212,7 @@ module GraphQL
|
|
|
205
212
|
join_queues(job_fibers, next_job_fibers)
|
|
206
213
|
|
|
207
214
|
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))
|
|
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)))
|
|
209
216
|
if f.alive?
|
|
210
217
|
finished = run_fiber(f)
|
|
211
218
|
if !finished
|
|
@@ -216,6 +223,8 @@ module GraphQL
|
|
|
216
223
|
join_queues(source_fibers, next_source_fibers)
|
|
217
224
|
end
|
|
218
225
|
end
|
|
226
|
+
|
|
227
|
+
trace&.end_dataloader(self)
|
|
219
228
|
end
|
|
220
229
|
|
|
221
230
|
run_fiber(manager)
|
|
@@ -230,6 +239,7 @@ module GraphQL
|
|
|
230
239
|
if !source_fibers.empty?
|
|
231
240
|
raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
|
|
232
241
|
end
|
|
242
|
+
|
|
233
243
|
rescue UncaughtThrowError => e
|
|
234
244
|
throw e.tag, e.value
|
|
235
245
|
end
|
|
@@ -247,6 +257,22 @@ module GraphQL
|
|
|
247
257
|
}
|
|
248
258
|
end
|
|
249
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
|
+
|
|
250
276
|
private
|
|
251
277
|
|
|
252
278
|
def calculate_fiber_limit
|
|
@@ -266,17 +292,19 @@ module GraphQL
|
|
|
266
292
|
new_queue.clear
|
|
267
293
|
end
|
|
268
294
|
|
|
269
|
-
def spawn_job_fiber
|
|
295
|
+
def spawn_job_fiber(trace)
|
|
270
296
|
if !@pending_jobs.empty?
|
|
271
297
|
spawn_fiber do
|
|
298
|
+
trace&.dataloader_spawn_execution_fiber(@pending_jobs)
|
|
272
299
|
while job = @pending_jobs.shift
|
|
273
300
|
job.call
|
|
274
301
|
end
|
|
302
|
+
trace&.dataloader_fiber_exit
|
|
275
303
|
end
|
|
276
304
|
end
|
|
277
305
|
end
|
|
278
306
|
|
|
279
|
-
def spawn_source_fiber
|
|
307
|
+
def spawn_source_fiber(trace)
|
|
280
308
|
pending_sources = nil
|
|
281
309
|
@source_cache.each_value do |source_by_batch_params|
|
|
282
310
|
source_by_batch_params.each_value do |source|
|
|
@@ -289,10 +317,14 @@ module GraphQL
|
|
|
289
317
|
|
|
290
318
|
if pending_sources
|
|
291
319
|
spawn_fiber do
|
|
320
|
+
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
292
321
|
pending_sources.each do |source|
|
|
293
322
|
Fiber[:__graphql_current_dataloader_source] = source
|
|
323
|
+
trace&.begin_dataloader_source(source)
|
|
294
324
|
source.run_pending_keys
|
|
325
|
+
trace&.end_dataloader_source(source)
|
|
295
326
|
end
|
|
327
|
+
trace&.dataloader_fiber_exit
|
|
296
328
|
end
|
|
297
329
|
end
|
|
298
330
|
end
|
|
@@ -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,14 +34,14 @@ 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 = {}
|
|
44
47
|
end
|
|
@@ -126,7 +129,7 @@ module GraphQL
|
|
|
126
129
|
class GraphQLResultArray
|
|
127
130
|
include GraphQLResult
|
|
128
131
|
|
|
129
|
-
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
|
|
132
|
+
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
133
|
super
|
|
131
134
|
@graphql_result_data = []
|
|
132
135
|
end
|
|
@@ -168,6 +171,10 @@ module GraphQL
|
|
|
168
171
|
def values
|
|
169
172
|
(@graphql_metadata || @graphql_result_data)
|
|
170
173
|
end
|
|
174
|
+
|
|
175
|
+
def [](idx)
|
|
176
|
+
(@graphql_metadata || @graphql_result_data)[idx]
|
|
177
|
+
end
|
|
171
178
|
end
|
|
172
179
|
end
|
|
173
180
|
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
|
|
|
@@ -85,7 +85,7 @@ module GraphQL
|
|
|
85
85
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
|
86
86
|
each_gathered_selections(@response) do |selections, is_selection_array|
|
|
87
87
|
if is_selection_array
|
|
88
|
-
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
|
|
88
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
|
|
89
89
|
final_response = @response
|
|
90
90
|
else
|
|
91
91
|
selection_response = @response
|
|
@@ -218,8 +218,10 @@ module GraphQL
|
|
|
218
218
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
219
219
|
)
|
|
220
220
|
finished_jobs += 1
|
|
221
|
-
if
|
|
222
|
-
|
|
221
|
+
if finished_jobs == enqueued_jobs
|
|
222
|
+
if target_result
|
|
223
|
+
selections_result.merge_into(target_result)
|
|
224
|
+
end
|
|
223
225
|
end
|
|
224
226
|
@dataloader.clear_cache
|
|
225
227
|
}
|
|
@@ -229,8 +231,10 @@ module GraphQL
|
|
|
229
231
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
230
232
|
)
|
|
231
233
|
finished_jobs += 1
|
|
232
|
-
if
|
|
233
|
-
|
|
234
|
+
if finished_jobs == enqueued_jobs
|
|
235
|
+
if target_result
|
|
236
|
+
selections_result.merge_into(target_result)
|
|
237
|
+
end
|
|
234
238
|
end
|
|
235
239
|
}
|
|
236
240
|
end
|
|
@@ -371,6 +375,7 @@ module GraphQL
|
|
|
371
375
|
end
|
|
372
376
|
# Actually call the field resolver and capture the result
|
|
373
377
|
app_result = begin
|
|
378
|
+
@current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
|
|
374
379
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
|
375
380
|
field_defn.resolve(object, kwarg_arguments, context)
|
|
376
381
|
end
|
|
@@ -383,6 +388,7 @@ module GraphQL
|
|
|
383
388
|
ex_err
|
|
384
389
|
end
|
|
385
390
|
end
|
|
391
|
+
@current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
|
|
386
392
|
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
393
|
owner_type = selection_result.graphql_result_type
|
|
388
394
|
return_type = field_defn.type
|
|
@@ -391,6 +397,8 @@ module GraphQL
|
|
|
391
397
|
was_scoped = runtime_state.was_authorized_by_scope_items
|
|
392
398
|
runtime_state.was_authorized_by_scope_items = nil
|
|
393
399
|
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)
|
|
400
|
+
else
|
|
401
|
+
nil
|
|
394
402
|
end
|
|
395
403
|
end
|
|
396
404
|
end
|
|
@@ -465,7 +473,7 @@ module GraphQL
|
|
|
465
473
|
# When this comes from a list item, use the parent object:
|
|
466
474
|
parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
|
467
475
|
# 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,
|
|
476
|
+
err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
|
|
469
477
|
schema.type_error(err, context)
|
|
470
478
|
end
|
|
471
479
|
else
|
|
@@ -574,12 +582,22 @@ module GraphQL
|
|
|
574
582
|
r = begin
|
|
575
583
|
current_type.coerce_result(value, context)
|
|
576
584
|
rescue StandardError => err
|
|
577
|
-
|
|
585
|
+
query.handle_or_reraise(err)
|
|
578
586
|
end
|
|
579
587
|
set_result(selection_result, result_name, r, false, is_non_null)
|
|
580
588
|
r
|
|
581
589
|
when "UNION", "INTERFACE"
|
|
582
|
-
resolved_type_or_lazy =
|
|
590
|
+
resolved_type_or_lazy = begin
|
|
591
|
+
resolve_type(current_type, value)
|
|
592
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
|
593
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
594
|
+
rescue StandardError => err
|
|
595
|
+
begin
|
|
596
|
+
query.handle_or_reraise(err)
|
|
597
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
598
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
583
601
|
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
602
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
|
585
603
|
resolved_type, resolved_value = resolved_type_result
|
|
@@ -609,11 +627,11 @@ module GraphQL
|
|
|
609
627
|
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
628
|
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
|
611
629
|
if HALT != continue_value
|
|
612
|
-
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
|
630
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
|
613
631
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
|
614
632
|
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
|
615
633
|
if is_selection_array
|
|
616
|
-
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
|
634
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
|
|
617
635
|
final_result = response_hash
|
|
618
636
|
else
|
|
619
637
|
this_result = response_hash
|
|
@@ -634,35 +652,43 @@ module GraphQL
|
|
|
634
652
|
# This is true for objects, unions, and interfaces
|
|
635
653
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
|
636
654
|
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)
|
|
655
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
|
638
656
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
|
639
657
|
idx = nil
|
|
640
658
|
list_value = begin
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
659
|
+
begin
|
|
660
|
+
value.each do |inner_value|
|
|
661
|
+
idx ||= 0
|
|
662
|
+
this_idx = idx
|
|
663
|
+
idx += 1
|
|
664
|
+
if use_dataloader_job
|
|
665
|
+
@dataloader.append_job do
|
|
666
|
+
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)
|
|
667
|
+
end
|
|
668
|
+
else
|
|
647
669
|
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
670
|
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
671
|
end
|
|
652
|
-
end
|
|
653
672
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
673
|
+
response_list
|
|
674
|
+
rescue NoMethodError => err
|
|
675
|
+
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
|
676
|
+
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
|
677
|
+
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
|
678
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
|
679
|
+
else
|
|
680
|
+
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
|
681
|
+
raise
|
|
682
|
+
end
|
|
683
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
|
684
|
+
ex_err
|
|
685
|
+
rescue StandardError => err
|
|
686
|
+
begin
|
|
687
|
+
query.handle_or_reraise(err)
|
|
688
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
689
|
+
ex_err
|
|
690
|
+
end
|
|
663
691
|
end
|
|
664
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
|
665
|
-
ex_err
|
|
666
692
|
rescue StandardError => err
|
|
667
693
|
begin
|
|
668
694
|
query.handle_or_reraise(err)
|
|
@@ -773,8 +799,10 @@ module GraphQL
|
|
|
773
799
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
774
800
|
# Wrap the execution of _this_ method with tracing,
|
|
775
801
|
# but don't wrap the continuation below
|
|
802
|
+
result = nil
|
|
776
803
|
inner_obj = begin
|
|
777
|
-
if trace
|
|
804
|
+
result = if trace
|
|
805
|
+
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
|
778
806
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
|
779
807
|
schema.sync_lazy(lazy_obj)
|
|
780
808
|
end
|
|
@@ -789,6 +817,10 @@ module GraphQL
|
|
|
789
817
|
rescue GraphQL::ExecutionError => ex_err
|
|
790
818
|
ex_err
|
|
791
819
|
end
|
|
820
|
+
ensure
|
|
821
|
+
if trace
|
|
822
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, result)
|
|
823
|
+
end
|
|
792
824
|
end
|
|
793
825
|
yield(inner_obj, runtime_state)
|
|
794
826
|
end
|
|
@@ -832,14 +864,19 @@ module GraphQL
|
|
|
832
864
|
end
|
|
833
865
|
|
|
834
866
|
def resolve_type(type, value)
|
|
867
|
+
@current_trace.begin_resolve_type(type, value, context)
|
|
835
868
|
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
|
836
869
|
query.resolve_type(type, value)
|
|
837
870
|
end
|
|
871
|
+
@current_trace.end_resolve_type(type, value, context, resolved_type)
|
|
838
872
|
|
|
839
873
|
if lazy?(resolved_type)
|
|
840
874
|
GraphQL::Execution::Lazy.new do
|
|
875
|
+
@current_trace.begin_resolve_type(type, value, context)
|
|
841
876
|
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
|
842
|
-
schema.sync_lazy(resolved_type)
|
|
877
|
+
rt = schema.sync_lazy(resolved_type)
|
|
878
|
+
@current_trace.end_resolve_type(type, value, context, rt)
|
|
879
|
+
rt
|
|
843
880
|
end
|
|
844
881
|
end
|
|
845
882
|
else
|
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
|
23
23
|
# @return [Array<GraphQL::Query::Result>] One result per query
|
|
24
24
|
def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
|
25
25
|
queries = query_options.map do |opts|
|
|
26
|
-
case opts
|
|
26
|
+
query = case opts
|
|
27
27
|
when Hash
|
|
28
28
|
schema.query_class.new(schema, nil, **opts)
|
|
29
29
|
when GraphQL::Query
|
|
@@ -31,11 +31,15 @@ module GraphQL
|
|
|
31
31
|
else
|
|
32
32
|
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
|
|
33
33
|
end
|
|
34
|
+
query
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
return GraphQL::EmptyObjects::EMPTY_ARRAY if queries.empty?
|
|
38
|
+
|
|
36
39
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
40
|
+
trace = multiplex.current_trace
|
|
37
41
|
Fiber[:__graphql_current_multiplex] = multiplex
|
|
38
|
-
|
|
42
|
+
trace.execute_multiplex(multiplex: multiplex) do
|
|
39
43
|
schema = multiplex.schema
|
|
40
44
|
queries = multiplex.queries
|
|
41
45
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
@@ -44,7 +48,10 @@ module GraphQL
|
|
|
44
48
|
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
|
45
49
|
end
|
|
46
50
|
|
|
51
|
+
trace.begin_analyze_multiplex(multiplex, multiplex_analyzers)
|
|
47
52
|
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
|
53
|
+
trace.end_analyze_multiplex(multiplex, multiplex_analyzers)
|
|
54
|
+
|
|
48
55
|
begin
|
|
49
56
|
# Since this is basically the batching context,
|
|
50
57
|
# share it for a whole multiplex
|
|
@@ -53,7 +60,9 @@ module GraphQL
|
|
|
53
60
|
results = []
|
|
54
61
|
queries.each_with_index do |query, idx|
|
|
55
62
|
if query.subscription? && !query.subscription_update?
|
|
56
|
-
query.context.namespace(:subscriptions)
|
|
63
|
+
subs_namespace = query.context.namespace(:subscriptions)
|
|
64
|
+
subs_namespace[:events] = []
|
|
65
|
+
subs_namespace[:subscriptions] = {}
|
|
57
66
|
end
|
|
58
67
|
multiplex.dataloader.append_job {
|
|
59
68
|
operation = query.selected_operation
|
|
@@ -32,14 +32,10 @@ module GraphQL
|
|
|
32
32
|
@queries = queries
|
|
33
33
|
@queries.each { |q| q.multiplex = self }
|
|
34
34
|
@context = context
|
|
35
|
-
@current_trace = @context[:trace] || schema.new_trace(multiplex: self)
|
|
36
35
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
|
37
36
|
@tracers = schema.tracers + (context[:tracers] || [])
|
|
38
|
-
# Support `context: {backtrace: true}`
|
|
39
|
-
if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
|
40
|
-
@tracers << GraphQL::Backtrace::Tracer
|
|
41
|
-
end
|
|
42
37
|
@max_complexity = max_complexity
|
|
38
|
+
@current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
|
|
43
39
|
end
|
|
44
40
|
end
|
|
45
41
|
end
|
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
|
7
7
|
"a __DirectiveLocation describes one such possible adjacencies."
|
|
8
8
|
|
|
9
9
|
GraphQL::Schema::Directive::LOCATIONS.each do |location|
|
|
10
|
-
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
|
|
10
|
+
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
|
|
11
11
|
end
|
|
12
12
|
introspection true
|
|
13
13
|
end
|
|
@@ -2,33 +2,23 @@
|
|
|
2
2
|
module GraphQL
|
|
3
3
|
# Raised automatically when a field's resolve function returns `nil`
|
|
4
4
|
# for a non-null field.
|
|
5
|
-
class InvalidNullError < GraphQL::
|
|
5
|
+
class InvalidNullError < GraphQL::Error
|
|
6
6
|
# @return [GraphQL::BaseType] The owner of {#field}
|
|
7
7
|
attr_reader :parent_type
|
|
8
8
|
|
|
9
9
|
# @return [GraphQL::Field] The field which failed to return a value
|
|
10
10
|
attr_reader :field
|
|
11
11
|
|
|
12
|
-
# @return [
|
|
13
|
-
attr_reader :
|
|
12
|
+
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
|
|
13
|
+
attr_reader :ast_node
|
|
14
14
|
|
|
15
|
-
def initialize(parent_type, field,
|
|
15
|
+
def initialize(parent_type, field, ast_node)
|
|
16
16
|
@parent_type = parent_type
|
|
17
17
|
@field = field
|
|
18
|
-
@
|
|
18
|
+
@ast_node = ast_node
|
|
19
19
|
super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
# @return [Hash] An entry for the response's "errors" key
|
|
23
|
-
def to_h
|
|
24
|
-
{ "message" => message }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# @deprecated always false
|
|
28
|
-
def parent_error?
|
|
29
|
-
false
|
|
30
|
-
end
|
|
31
|
-
|
|
32
22
|
class << self
|
|
33
23
|
attr_accessor :parent_class
|
|
34
24
|
|
|
@@ -13,17 +13,21 @@ module GraphQL
|
|
|
13
13
|
@pos = nil
|
|
14
14
|
@max_tokens = max_tokens || Float::INFINITY
|
|
15
15
|
@tokens_count = 0
|
|
16
|
+
@finished = false
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
def
|
|
19
|
-
@
|
|
19
|
+
def finished?
|
|
20
|
+
@finished
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
attr_reader :pos, :tokens_count
|
|
23
24
|
|
|
24
25
|
def advance
|
|
25
26
|
@scanner.skip(IGNORE_REGEXP)
|
|
26
|
-
|
|
27
|
+
if @scanner.eos?
|
|
28
|
+
@finished = true
|
|
29
|
+
return false
|
|
30
|
+
end
|
|
27
31
|
@tokens_count += 1
|
|
28
32
|
if @tokens_count > @max_tokens
|
|
29
33
|
raise_parse_error("This query is too large to execute.")
|
|
@@ -141,6 +141,8 @@ module GraphQL
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
class << self
|
|
144
|
+
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
|
145
|
+
|
|
144
146
|
# Add a default `#visit_method` and `#children_method_name` using the class name
|
|
145
147
|
def inherited(child_class)
|
|
146
148
|
super
|
|
@@ -343,6 +345,7 @@ module GraphQL
|
|
|
343
345
|
RUBY
|
|
344
346
|
end
|
|
345
347
|
end
|
|
348
|
+
# rubocop:enable Development/NoEvalCop
|
|
346
349
|
end
|
|
347
350
|
end
|
|
348
351
|
|
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
|
110
110
|
# Only ignored characters is not a valid document
|
|
111
111
|
raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @graphql_str)
|
|
112
112
|
end
|
|
113
|
-
while !@lexer.
|
|
113
|
+
while !@lexer.finished?
|
|
114
114
|
defns << definition
|
|
115
115
|
end
|
|
116
116
|
Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
|
|
@@ -161,7 +161,7 @@ module GraphQL
|
|
|
161
161
|
expect_token(:VAR_SIGN)
|
|
162
162
|
var_name = parse_name
|
|
163
163
|
expect_token(:COLON)
|
|
164
|
-
var_type = self.type
|
|
164
|
+
var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
|
|
165
165
|
default_value = if at?(:EQUALS)
|
|
166
166
|
advance_token
|
|
167
167
|
value
|
|
@@ -488,26 +488,33 @@ module GraphQL
|
|
|
488
488
|
end
|
|
489
489
|
|
|
490
490
|
def type
|
|
491
|
-
|
|
491
|
+
parsed_type = case token_name
|
|
492
492
|
when :IDENTIFIER
|
|
493
493
|
parse_type_name
|
|
494
494
|
when :LBRACKET
|
|
495
495
|
list_type
|
|
496
|
+
else
|
|
497
|
+
nil
|
|
496
498
|
end
|
|
497
499
|
|
|
498
|
-
if at?(:BANG)
|
|
499
|
-
|
|
500
|
+
if at?(:BANG) && parsed_type
|
|
501
|
+
parsed_type = Nodes::NonNullType.new(pos: pos, of_type: parsed_type, source: self)
|
|
500
502
|
expect_token(:BANG)
|
|
501
503
|
end
|
|
502
|
-
|
|
504
|
+
parsed_type
|
|
503
505
|
end
|
|
504
506
|
|
|
505
507
|
def list_type
|
|
506
508
|
loc = pos
|
|
507
509
|
expect_token(:LBRACKET)
|
|
508
|
-
|
|
510
|
+
inner_type = self.type
|
|
511
|
+
parsed_list_type = if inner_type
|
|
512
|
+
Nodes::ListType.new(pos: loc, of_type: inner_type, source: self)
|
|
513
|
+
else
|
|
514
|
+
nil
|
|
515
|
+
end
|
|
509
516
|
expect_token(:RBRACKET)
|
|
510
|
-
|
|
517
|
+
parsed_list_type
|
|
511
518
|
end
|
|
512
519
|
|
|
513
520
|
def parse_operation_type
|