graphql 2.6.10 → 2.6.12
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/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader.rb +7 -2
- data/lib/graphql/execution/field_resolve_step.rb +18 -7
- data/lib/graphql/execution/interpreter/runtime.rb +6 -2
- data/lib/graphql/execution/runner.rb +21 -17
- data/lib/graphql/language/parser.rb +8 -7
- data/lib/graphql/pagination/array_connection.rb +5 -1
- data/lib/graphql/pagination/relation_connection.rb +23 -3
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/query.rb +2 -1
- data/lib/graphql/schema/directive.rb +5 -0
- data/lib/graphql/schema/member/has_arguments.rb +1 -1
- data/lib/graphql/schema/visibility/profile.rb +53 -2
- data/lib/graphql/static_validation/base_visitor.rb +11 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +40 -5
- data/lib/graphql/subscriptions/serialize.rb +32 -5
- data/lib/graphql/tracing/sentry_trace.rb +13 -1
- data/lib/graphql/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24a0e699fe2f72dd869173b2bc6aa96ec8d40e738a293bc56b9176debca03861
|
|
4
|
+
data.tar.gz: 18b16bf0c467f5f8dc0c00ea2bcf2fbaf53283edc9f09a3789918523090a97ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74a34839ced3a300d20ca370cb37a7c88c56345ecb056e9b9caa7a424fdb7573c5a014b80a32772cc2a5eb40d83e568c5d0875d93b029b1663dda9547fc06ba1
|
|
7
|
+
data.tar.gz: 65dcfea9802c9fb682e6595ef2d2d224a256c0650ff24fa57774eac420b27e594b3c432a104d76c8e033c659a608940661ea4503e0737f63d7839ff36f5f4405
|
|
@@ -254,6 +254,8 @@ module GraphQL
|
|
|
254
254
|
task = Async::Task.new do |root_task|
|
|
255
255
|
run.root_task = root_task
|
|
256
256
|
root_task.graphql_async_dataloader_run = run
|
|
257
|
+
# Tasks inherit the spawning fiber's storage; give this run its own execution state.
|
|
258
|
+
Fiber[:__graphql_runtime_info] = nil
|
|
257
259
|
set_fiber_variables(fiber_vars)
|
|
258
260
|
|
|
259
261
|
while first_pass || run.running? || !jobs.empty?
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -262,11 +262,14 @@ module GraphQL
|
|
|
262
262
|
|
|
263
263
|
def spawn_fiber
|
|
264
264
|
fiber_vars = get_fiber_variables
|
|
265
|
-
Fiber.new(blocking: !@nonblocking)
|
|
265
|
+
Fiber.new(blocking: !@nonblocking) do
|
|
266
|
+
# Fibers inherit the spawning fiber's storage; give each one its own execution state.
|
|
267
|
+
Fiber[:__graphql_runtime_info] = nil
|
|
266
268
|
set_fiber_variables(fiber_vars)
|
|
267
269
|
yield
|
|
270
|
+
ensure
|
|
268
271
|
cleanup_fiber
|
|
269
|
-
|
|
272
|
+
end
|
|
270
273
|
end
|
|
271
274
|
|
|
272
275
|
# Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
|
|
@@ -359,6 +362,7 @@ module GraphQL
|
|
|
359
362
|
while job = @pending_jobs.shift
|
|
360
363
|
job.call
|
|
361
364
|
end
|
|
365
|
+
ensure
|
|
362
366
|
trace&.dataloader_fiber_exit
|
|
363
367
|
end
|
|
364
368
|
end
|
|
@@ -392,6 +396,7 @@ module GraphQL
|
|
|
392
396
|
source.run_pending_keys
|
|
393
397
|
trace&.end_dataloader_source(source)
|
|
394
398
|
end
|
|
399
|
+
ensure
|
|
395
400
|
trace&.dataloader_fiber_exit
|
|
396
401
|
end
|
|
397
402
|
end
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
module GraphQL
|
|
3
3
|
module Execution
|
|
4
4
|
class FieldResolveStep
|
|
5
|
+
# Only these built-in fields on Relay connection and edge types return already-scoped items.
|
|
6
|
+
# Custom fields on those types must still be authorized normally.
|
|
7
|
+
SCOPED_RELAY_FIELD_NAMES = [:node, :nodes, :edges].freeze
|
|
8
|
+
|
|
5
9
|
def initialize(parent_type:, runner:, key:, selections_step:)
|
|
6
10
|
@selections_step = selections_step
|
|
7
11
|
@key = key
|
|
@@ -283,7 +287,7 @@ module GraphQL
|
|
|
283
287
|
authorized_objects = objects
|
|
284
288
|
end
|
|
285
289
|
|
|
286
|
-
if @parent_type.default_relay? && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
|
|
290
|
+
if @parent_type.default_relay? && SCOPED_RELAY_FIELD_NAMES.include?(@field_definition.original_name) && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
|
|
287
291
|
@was_scoped = true
|
|
288
292
|
end
|
|
289
293
|
|
|
@@ -527,7 +531,7 @@ module GraphQL
|
|
|
527
531
|
end
|
|
528
532
|
|
|
529
533
|
inner_type = return_type.of_type
|
|
530
|
-
result_a = Array.new(field_result.size)
|
|
534
|
+
result_a = field_result.is_a?(Array) ? Array.new(field_result.size) : []
|
|
531
535
|
field_result.each_with_index do |item, idx|
|
|
532
536
|
result_a[idx] = build_leaf_result(result_a, idx, item, inner_type, ctx, true)
|
|
533
537
|
end
|
|
@@ -645,11 +649,18 @@ module GraphQL
|
|
|
645
649
|
list_result = graphql_result[key] = []
|
|
646
650
|
@directive_finalizers&.each { |f| @runner.add_finalizer(@selections_step.query, list_result, nil, f) }
|
|
647
651
|
i = 0
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
652
|
+
if field_result.is_a?(Array)
|
|
653
|
+
s = field_result.size
|
|
654
|
+
while i < s
|
|
655
|
+
inner_f_r = field_result[i]
|
|
656
|
+
build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
|
|
657
|
+
i += 1
|
|
658
|
+
end
|
|
659
|
+
else
|
|
660
|
+
field_result.each do |inner_f_r|
|
|
661
|
+
build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
|
|
662
|
+
i += 1
|
|
663
|
+
end
|
|
653
664
|
end
|
|
654
665
|
elsif @runner.resolves_lazies || (
|
|
655
666
|
@static_type.kind.object? ?
|
|
@@ -619,7 +619,9 @@ module GraphQL
|
|
|
619
619
|
when Array
|
|
620
620
|
# It's an array full of execution errors; add them all.
|
|
621
621
|
if !value.empty? && value.all?(GraphQL::ExecutionError)
|
|
622
|
-
|
|
622
|
+
list_type = field && field.type
|
|
623
|
+
list_type = list_type.of_type if list_type && list_type.non_null?
|
|
624
|
+
list_type_at_all = list_type && list_type.list?
|
|
623
625
|
if selection_result.nil? || !selection_result.graphql_dead
|
|
624
626
|
value.each_with_index do |error, index|
|
|
625
627
|
error.ast_node ||= ast_node
|
|
@@ -627,7 +629,7 @@ module GraphQL
|
|
|
627
629
|
context.errors << error
|
|
628
630
|
end
|
|
629
631
|
if selection_result
|
|
630
|
-
if list_type_at_all
|
|
632
|
+
if list_type_at_all && !list_type.of_type.non_null?
|
|
631
633
|
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
|
632
634
|
set_result(selection_result, result_name, result_without_errors, false, is_non_null)
|
|
633
635
|
else
|
|
@@ -798,6 +800,8 @@ module GraphQL
|
|
|
798
800
|
|
|
799
801
|
def 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) # rubocop:disable Metrics/ParameterLists
|
|
800
802
|
runtime_state ||= get_current_runtime_state
|
|
803
|
+
runtime_state.current_field = field
|
|
804
|
+
runtime_state.current_arguments = arguments
|
|
801
805
|
runtime_state.current_result_name = this_idx
|
|
802
806
|
runtime_state.current_result = response_list
|
|
803
807
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
@@ -87,10 +87,10 @@ module GraphQL
|
|
|
87
87
|
@schema.analysis_engine.analyze_multiplex(@multiplex, multiplex_analyzers)
|
|
88
88
|
trace.end_analyze_multiplex(@multiplex, multiplex_analyzers)
|
|
89
89
|
|
|
90
|
-
results =
|
|
90
|
+
results = {}.compare_by_identity
|
|
91
91
|
queries.each do |query|
|
|
92
92
|
if query.validate && !query.valid?
|
|
93
|
-
results
|
|
93
|
+
results[query] = {
|
|
94
94
|
"errors" => query.static_errors.map(&:to_h)
|
|
95
95
|
}
|
|
96
96
|
next
|
|
@@ -125,8 +125,8 @@ module GraphQL
|
|
|
125
125
|
end
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
queries.
|
|
129
|
-
result = results[
|
|
128
|
+
queries.map do |query|
|
|
129
|
+
result = results[query]
|
|
130
130
|
|
|
131
131
|
fin_result = if (!@finalizers&.key?(query) && query.context.errors.empty?) || !query.valid?
|
|
132
132
|
result
|
|
@@ -155,12 +155,16 @@ module GraphQL
|
|
|
155
155
|
end
|
|
156
156
|
query.result
|
|
157
157
|
end
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
rescue SystemStackError => err
|
|
159
|
+
queries.map do |query|
|
|
160
|
+
@schema.query_stack_error(query, err)
|
|
161
|
+
query.result_values ||= { "errors" => query.context.errors.map(&:to_h) }
|
|
162
|
+
query.result
|
|
163
|
+
end
|
|
164
|
+
rescue Exception
|
|
165
|
+
# Assign values here so that the query's `@executed` becomes true
|
|
166
|
+
queries.map { |q| q.result_values ||= {} }
|
|
167
|
+
raise
|
|
164
168
|
end
|
|
165
169
|
ensure
|
|
166
170
|
Fiber[:__graphql_current_multiplex] = previous_multiplex
|
|
@@ -262,12 +266,12 @@ module GraphQL
|
|
|
262
266
|
end
|
|
263
267
|
|
|
264
268
|
if !auth_check
|
|
265
|
-
results
|
|
269
|
+
results[query] = {}
|
|
266
270
|
return
|
|
267
271
|
end
|
|
268
272
|
end
|
|
269
273
|
|
|
270
|
-
results
|
|
274
|
+
results[query] = { "data" => data }
|
|
271
275
|
objects = [root_value]
|
|
272
276
|
query.current_trace.objects(root_type, objects, query.context)
|
|
273
277
|
|
|
@@ -361,7 +365,7 @@ module GraphQL
|
|
|
361
365
|
objects = [root_value]
|
|
362
366
|
query.current_trace.objects(resolved_type, objects, query.context)
|
|
363
367
|
runtime_type_at[data] = resolved_type
|
|
364
|
-
results
|
|
368
|
+
results[query] = { "data" => data }
|
|
365
369
|
isolated_steps[0] << SelectionsStep.new(
|
|
366
370
|
parent_type: resolved_type,
|
|
367
371
|
field_resolve_step: nil,
|
|
@@ -376,10 +380,10 @@ module GraphQL
|
|
|
376
380
|
inner_type = root_type.unwrap
|
|
377
381
|
case inner_type.kind.name
|
|
378
382
|
when "SCALAR", "ENUM"
|
|
379
|
-
results
|
|
383
|
+
results[query] = run_isolated_scalar(root_type, query)
|
|
380
384
|
else
|
|
381
385
|
list_result = Array.new(root_value.size) { Hash.new.compare_by_identity }
|
|
382
|
-
results
|
|
386
|
+
results[query] = { "data" => list_result }
|
|
383
387
|
isolated_steps[0] << SelectionsStep.new(
|
|
384
388
|
parent_type: inner_type,
|
|
385
389
|
field_resolve_step: nil,
|
|
@@ -392,7 +396,7 @@ module GraphQL
|
|
|
392
396
|
)
|
|
393
397
|
end
|
|
394
398
|
when "SCALAR", "ENUM"
|
|
395
|
-
results
|
|
399
|
+
results[query] = run_isolated_scalar(root_type, query)
|
|
396
400
|
else
|
|
397
401
|
raise "Unhandled root type kind: #{root_type.kind.name.inspect}"
|
|
398
402
|
end
|
|
@@ -425,7 +429,7 @@ module GraphQL
|
|
|
425
429
|
key = dummy_path.pop
|
|
426
430
|
is_from_array = key.is_a?(Integer)
|
|
427
431
|
|
|
428
|
-
if lazy?(value)
|
|
432
|
+
if resolves_lazies && lazy?(value)
|
|
429
433
|
value = @schema.sync_lazy(value)
|
|
430
434
|
end
|
|
431
435
|
selections = partial.ast_nodes
|
|
@@ -65,24 +65,25 @@ module GraphQL
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def column_at(pos)
|
|
68
|
-
next_line_idx = lines_at.bsearch_index { |l| l >= pos } ||
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
pos -
|
|
68
|
+
next_line_idx = lines_at.bsearch_index { |l| l >= pos } || @lines_at.size
|
|
69
|
+
line_start = next_line_idx == 0 ? 0 : @lines_at[next_line_idx - 1] + 1
|
|
70
|
+
if @graphql_str.ascii_only?
|
|
71
|
+
pos - line_start + 1
|
|
72
72
|
else
|
|
73
|
-
pos + 1
|
|
73
|
+
@graphql_str.byteslice(line_start, pos - line_start).force_encoding(Encoding::UTF_8).length + 1
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
private
|
|
78
78
|
|
|
79
|
-
# @return [Array<Integer>]
|
|
79
|
+
# @return [Array<Integer>] Byte offsets of each line break in the original string
|
|
80
80
|
def lines_at
|
|
81
81
|
@lines_at ||= begin
|
|
82
|
+
graphql_str = @graphql_str.b
|
|
82
83
|
la = []
|
|
83
84
|
idx = 0
|
|
84
85
|
while idx
|
|
85
|
-
idx =
|
|
86
|
+
idx = graphql_str.index("\n", idx)
|
|
86
87
|
if idx
|
|
87
88
|
la << idx
|
|
88
89
|
idx += 1
|
|
@@ -27,7 +27,11 @@ module GraphQL
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
29
|
def index_from_cursor(cursor)
|
|
30
|
-
decode(cursor)
|
|
30
|
+
index = Integer(decode(cursor), 10, exception: false)
|
|
31
|
+
if index.nil? || index <= 0
|
|
32
|
+
raise GraphQL::ExecutionError, "Invalid cursor: #{cursor.inspect}"
|
|
33
|
+
end
|
|
34
|
+
[index, items.length + 1].min
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
# Populate all the pagination info _once_,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "graphql/pagination/connection"
|
|
3
|
+
require "monitor"
|
|
3
4
|
|
|
4
5
|
module GraphQL
|
|
5
6
|
module Pagination
|
|
@@ -87,7 +88,11 @@ module GraphQL
|
|
|
87
88
|
|
|
88
89
|
# @return [Integer]
|
|
89
90
|
def offset_from_cursor(cursor)
|
|
90
|
-
decode(cursor)
|
|
91
|
+
offset = Integer(decode(cursor), 10, exception: false)
|
|
92
|
+
if offset.nil? || offset <= 0
|
|
93
|
+
raise GraphQL::ExecutionError, "Invalid cursor: #{cursor.inspect}"
|
|
94
|
+
end
|
|
95
|
+
offset
|
|
91
96
|
end
|
|
92
97
|
|
|
93
98
|
# Abstract this operation so we can always ignore inputs less than zero.
|
|
@@ -178,6 +183,17 @@ module GraphQL
|
|
|
178
183
|
# Apply `first` and `last` to `sliced_nodes`,
|
|
179
184
|
# returning a new relation
|
|
180
185
|
def limited_nodes
|
|
186
|
+
return @limited_nodes if @limited_nodes
|
|
187
|
+
if async_dataloader?
|
|
188
|
+
(@load_monitor ||= Monitor.new).synchronize do
|
|
189
|
+
build_limited_nodes
|
|
190
|
+
end
|
|
191
|
+
else
|
|
192
|
+
build_limited_nodes
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def build_limited_nodes
|
|
181
197
|
@limited_nodes ||= begin
|
|
182
198
|
calculate_sliced_nodes_parameters
|
|
183
199
|
if @sliced_nodes_null_relation
|
|
@@ -217,15 +233,19 @@ module GraphQL
|
|
|
217
233
|
end
|
|
218
234
|
end
|
|
219
235
|
|
|
236
|
+
def async_dataloader?
|
|
237
|
+
@context&.dataloader.is_a?(GraphQL::Dataloader::AsyncDataloader)
|
|
238
|
+
end
|
|
239
|
+
|
|
220
240
|
# Load nodes after applying first/last/before/after,
|
|
221
241
|
# returns an array of nodes
|
|
222
242
|
def load_nodes
|
|
223
243
|
# Return an array so we can consistently use `.index(node)` on it
|
|
224
244
|
return @nodes if @nodes
|
|
225
|
-
if
|
|
245
|
+
if async_dataloader?
|
|
226
246
|
# `AsyncDataloader` may resolve sibling fields (eg, `edges` and `pageInfo`)
|
|
227
247
|
# in separate Fibers, so several callers can get here before `@nodes` is set.
|
|
228
|
-
(@
|
|
248
|
+
(@load_monitor ||= Monitor.new).synchronize do
|
|
229
249
|
@nodes ||= limited_nodes.to_a
|
|
230
250
|
end
|
|
231
251
|
else
|
|
@@ -35,7 +35,11 @@ module GraphQL
|
|
|
35
35
|
value_was_provided = @provided_variables.key?(variable_name)
|
|
36
36
|
max_errors = schema.validate_max_errors - @errors.count if schema.validate_max_errors
|
|
37
37
|
begin
|
|
38
|
-
validation_result =
|
|
38
|
+
validation_result = if value_was_provided || default_value.nil?
|
|
39
|
+
variable_type.validate_input(provided_value, ctx, max_errors: max_errors)
|
|
40
|
+
else
|
|
41
|
+
GraphQL::Query::InputValidationResult::VALID
|
|
42
|
+
end
|
|
39
43
|
if validation_result.valid?
|
|
40
44
|
if value_was_provided
|
|
41
45
|
# Add the variable if a value was provided
|
data/lib/graphql/query.rb
CHANGED
|
@@ -287,7 +287,8 @@ module GraphQL
|
|
|
287
287
|
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
|
288
288
|
def result
|
|
289
289
|
if !@executed
|
|
290
|
-
|
|
290
|
+
execution_engine = @schema.default_execution_next ? Execution::Next : Execution::Interpreter
|
|
291
|
+
execution_engine.run_all(@schema, [self], context: @context)
|
|
291
292
|
end
|
|
292
293
|
@result ||= Query::Result.new(query: self, values: @result_values)
|
|
293
294
|
end
|
|
@@ -84,6 +84,11 @@ module GraphQL
|
|
|
84
84
|
yield
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def resolve_field(...); end
|
|
88
|
+
def resolve_fragment_spread(...); end
|
|
89
|
+
def resolve_inline_fragment(...); end
|
|
90
|
+
def resolve_operation(...); end
|
|
91
|
+
|
|
87
92
|
def validate!(arguments, context)
|
|
88
93
|
Schema::Validator.validate!(validators, self, context, arguments)
|
|
89
94
|
end
|
|
@@ -397,7 +397,7 @@ module GraphQL
|
|
|
397
397
|
# This union/interface is used in `loads:` but not otherwise visible to this query
|
|
398
398
|
context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
|
|
399
399
|
else
|
|
400
|
-
|
|
400
|
+
arg_loads_type == application_object_type
|
|
401
401
|
end
|
|
402
402
|
else
|
|
403
403
|
context.types.possible_types(arg_loads_type).include?(application_object_type)
|
|
@@ -33,30 +33,49 @@ module GraphQL
|
|
|
33
33
|
|
|
34
34
|
def freeze
|
|
35
35
|
@cached_visible.default_proc = nil
|
|
36
|
+
@cached_visible.freeze
|
|
36
37
|
@cached_visible_fields.default_proc = nil
|
|
37
38
|
@cached_visible_fields.each do |type, fields|
|
|
38
39
|
fields.default_proc = nil
|
|
40
|
+
fields.freeze
|
|
39
41
|
end
|
|
42
|
+
@cached_visible_fields.freeze
|
|
40
43
|
@cached_visible_arguments.default_proc = nil
|
|
41
44
|
@cached_visible_arguments.each do |type, fields|
|
|
42
45
|
fields.default_proc = nil
|
|
46
|
+
fields.freeze
|
|
43
47
|
end
|
|
48
|
+
@cached_visible_arguments.freeze
|
|
44
49
|
@cached_parent_fields.default_proc = nil
|
|
45
50
|
@cached_parent_fields.each do |type, fields|
|
|
46
51
|
fields.default_proc = nil
|
|
52
|
+
fields.freeze
|
|
47
53
|
end
|
|
54
|
+
@cached_parent_fields.freeze
|
|
48
55
|
@cached_parent_arguments.default_proc = nil
|
|
49
56
|
@cached_parent_arguments.each do |type, args|
|
|
50
57
|
args.default_proc = nil
|
|
58
|
+
args.freeze
|
|
51
59
|
end
|
|
60
|
+
@cached_parent_arguments.freeze
|
|
52
61
|
@cached_possible_types.default_proc = nil
|
|
62
|
+
@cached_possible_types.freeze
|
|
53
63
|
@cached_enum_values.default_proc = nil
|
|
64
|
+
@cached_enum_values.freeze
|
|
54
65
|
@cached_fields.default_proc = nil
|
|
66
|
+
@cached_fields.freeze
|
|
55
67
|
@cached_arguments.default_proc = nil
|
|
68
|
+
@cached_arguments.freeze
|
|
56
69
|
@loadable_possible_types.default_proc = nil
|
|
70
|
+
@loadable_possible_types.freeze
|
|
57
71
|
@cached_field_result.default_proc = nil
|
|
58
|
-
@cached_field_result.each
|
|
72
|
+
@cached_field_result.each do |_, h|
|
|
73
|
+
h.default_proc = nil
|
|
74
|
+
h.freeze
|
|
75
|
+
end
|
|
76
|
+
@cached_field_result.freeze
|
|
59
77
|
@cached_type_result.default_proc = nil
|
|
78
|
+
@cached_type_result.freeze
|
|
60
79
|
super
|
|
61
80
|
end
|
|
62
81
|
|
|
@@ -277,18 +296,31 @@ module GraphQL
|
|
|
277
296
|
arguments(f).each do |arg|
|
|
278
297
|
argument(f, arg.graphql_name)
|
|
279
298
|
end
|
|
299
|
+
|
|
300
|
+
resolver_class = f.resolver
|
|
301
|
+
if resolver_class
|
|
302
|
+
arguments(resolver_class).each do |arg|
|
|
303
|
+
argument(resolver_class, arg.graphql_name)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
280
306
|
end
|
|
281
307
|
@schema.introspection_system.dynamic_fields.each do |f|
|
|
282
308
|
field(type_defn, f.graphql_name)
|
|
283
309
|
end
|
|
310
|
+
interfaces(type_defn)
|
|
284
311
|
elsif type_defn.kind.input_object?
|
|
285
312
|
arguments(type_defn).each do |arg|
|
|
286
313
|
argument(type_defn, arg.graphql_name)
|
|
287
314
|
end
|
|
288
315
|
elsif type_defn.kind.enum?
|
|
289
316
|
enum_values(type_defn)
|
|
317
|
+
elsif type_defn.kind.union?
|
|
318
|
+
loadable_possible_types(type_defn, @context)
|
|
319
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
|
320
|
+
field(type_defn, f.graphql_name)
|
|
321
|
+
end
|
|
290
322
|
end
|
|
291
|
-
|
|
323
|
+
possible_types(type_defn)
|
|
292
324
|
end
|
|
293
325
|
if @schema.query
|
|
294
326
|
@schema.introspection_system.entry_points.each do |f|
|
|
@@ -304,6 +336,25 @@ module GraphQL
|
|
|
304
336
|
end
|
|
305
337
|
end
|
|
306
338
|
|
|
339
|
+
directives.each do |directive|
|
|
340
|
+
arguments(directive).each do |directive_argument|
|
|
341
|
+
argument(directive, directive_argument.graphql_name)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
@cached_arguments.each do |owner, args|
|
|
346
|
+
@cached_parent_arguments[owner]
|
|
347
|
+
|
|
348
|
+
args.each do |arg|
|
|
349
|
+
if (loads_type = arg.loads)
|
|
350
|
+
loadable?(loads_type, @context)
|
|
351
|
+
possible_types(loads_type)
|
|
352
|
+
if loads_type.kind.union?
|
|
353
|
+
loadable_possible_types(loads_type, @context)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|
|
307
358
|
end
|
|
308
359
|
|
|
309
360
|
private
|
|
@@ -16,6 +16,7 @@ module GraphQL
|
|
|
16
16
|
@schema = context.schema
|
|
17
17
|
@inline_fragment_paths = {}
|
|
18
18
|
@field_unwrapped_types = {}.compare_by_identity
|
|
19
|
+
@child_positions = {}.compare_by_identity
|
|
19
20
|
super(document)
|
|
20
21
|
end
|
|
21
22
|
|
|
@@ -159,6 +160,7 @@ module GraphQL
|
|
|
159
160
|
@current_argument_definition = @parent_argument_definition
|
|
160
161
|
@parent_argument_definition = prev_parent
|
|
161
162
|
@path_depth -= 1
|
|
163
|
+
@child_positions.delete(node)
|
|
162
164
|
end
|
|
163
165
|
|
|
164
166
|
def on_fragment_spread(node, parent)
|
|
@@ -171,7 +173,7 @@ module GraphQL
|
|
|
171
173
|
def on_input_object(node, parent)
|
|
172
174
|
arg_defn = @current_argument_definition
|
|
173
175
|
if arg_defn && arg_defn.type.list?
|
|
174
|
-
@path[@path_depth] = parent
|
|
176
|
+
@path[@path_depth] = child_positions(parent)[node]
|
|
175
177
|
@path_depth += 1
|
|
176
178
|
super
|
|
177
179
|
@path_depth -= 1
|
|
@@ -180,6 +182,14 @@ module GraphQL
|
|
|
180
182
|
end
|
|
181
183
|
end
|
|
182
184
|
|
|
185
|
+
def child_positions(parent)
|
|
186
|
+
@child_positions[parent] ||= begin
|
|
187
|
+
positions = {}.compare_by_identity
|
|
188
|
+
parent.children.each_with_index { |child, index| positions[child] ||= index }
|
|
189
|
+
positions
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
183
193
|
# @return [GraphQL::BaseType] The current object type
|
|
184
194
|
def type_definition
|
|
185
195
|
@current_object_type
|
|
@@ -9,18 +9,53 @@ module GraphQL
|
|
|
9
9
|
'Schema is not configured for subscriptions',
|
|
10
10
|
nodes: node
|
|
11
11
|
))
|
|
12
|
-
elsif node.selections.size != 1
|
|
13
|
-
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
|
14
|
-
'A subscription operation may only have one selection',
|
|
15
|
-
nodes: node,
|
|
16
|
-
))
|
|
17
12
|
else
|
|
13
|
+
root_fields = subscription_root_fields(node)
|
|
14
|
+
if root_fields.map { |field| field.alias || field.name }.uniq.size != 1
|
|
15
|
+
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
|
16
|
+
'A subscription operation may only have one selection',
|
|
17
|
+
nodes: node,
|
|
18
|
+
))
|
|
19
|
+
elsif root_fields.any? { |field| field.name.start_with?("__") }
|
|
20
|
+
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
|
21
|
+
'A subscription operation may not select an introspection field',
|
|
22
|
+
nodes: node,
|
|
23
|
+
))
|
|
24
|
+
end
|
|
18
25
|
super
|
|
19
26
|
end
|
|
20
27
|
else
|
|
21
28
|
super
|
|
22
29
|
end
|
|
23
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def subscription_root_fields(node)
|
|
35
|
+
root_type = context.types.subscription_root
|
|
36
|
+
fields = []
|
|
37
|
+
selections = node.selections.dup
|
|
38
|
+
visited_fragments = {}
|
|
39
|
+
|
|
40
|
+
while (selection = selections.pop)
|
|
41
|
+
case selection
|
|
42
|
+
when GraphQL::Language::Nodes::Field
|
|
43
|
+
fields << selection
|
|
44
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
|
45
|
+
next if visited_fragments[selection.name]
|
|
46
|
+
visited_fragments[selection.name] = true
|
|
47
|
+
fragment = context.fragments[selection.name]
|
|
48
|
+
selections << fragment if fragment
|
|
49
|
+
when GraphQL::Language::Nodes::InlineFragment, GraphQL::Language::Nodes::FragmentDefinition
|
|
50
|
+
fragment_type = selection.type ? @types.type(selection.type.name) : root_type
|
|
51
|
+
if fragment_type && fragment_type.kind.composite? && @types.possible_types(fragment_type).include?(root_type)
|
|
52
|
+
selections.concat(selection.selections)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
fields
|
|
58
|
+
end
|
|
24
59
|
end
|
|
25
60
|
end
|
|
26
61
|
end
|
|
@@ -13,6 +13,8 @@ module GraphQL
|
|
|
13
13
|
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00'
|
|
14
14
|
TIMESTAMP_CLASS_NAMES = ["Date", "DateTime", "Time"].freeze
|
|
15
15
|
OPEN_STRUCT_KEY = "__ostruct__"
|
|
16
|
+
HASH_KEY = "__graphql_hash__"
|
|
17
|
+
RESERVED_KEYS = [GLOBALID_KEY, SYMBOL_KEY, SYMBOL_KEYS_KEY, TIMESTAMP_KEY, OPEN_STRUCT_KEY, HASH_KEY].freeze
|
|
16
18
|
|
|
17
19
|
module_function
|
|
18
20
|
|
|
@@ -57,7 +59,7 @@ module GraphQL
|
|
|
57
59
|
# @return [Object] An object that load Global::Identification recursive
|
|
58
60
|
def load_value(value)
|
|
59
61
|
if value.is_a?(Array)
|
|
60
|
-
is_gids =
|
|
62
|
+
is_gids = !value.empty? && value.all? { |v| v.is_a?(Hash) && v.size == 1 && v[GLOBALID_KEY] }
|
|
61
63
|
if is_gids
|
|
62
64
|
# Assume it's an array of global IDs
|
|
63
65
|
ids = value.map { |v| v[GLOBALID_KEY] }
|
|
@@ -91,6 +93,10 @@ module GraphQL
|
|
|
91
93
|
when OPEN_STRUCT_KEY
|
|
92
94
|
ostruct_values = load_value(value[OPEN_STRUCT_KEY])
|
|
93
95
|
OpenStruct.new(ostruct_values)
|
|
96
|
+
when HASH_KEY
|
|
97
|
+
value[HASH_KEY].each_with_object({}) do |(k, v), loaded_h|
|
|
98
|
+
loaded_h[load_value(k)] = load_value(v)
|
|
99
|
+
end
|
|
94
100
|
else
|
|
95
101
|
key = value.keys.first
|
|
96
102
|
{ key => load_value(value[key]) }
|
|
@@ -98,15 +104,21 @@ module GraphQL
|
|
|
98
104
|
else
|
|
99
105
|
loaded_h = {}
|
|
100
106
|
sym_keys = value.fetch(SYMBOL_KEYS_KEY, [])
|
|
107
|
+
symbol_key_values = sym_keys.is_a?(Hash) ? sym_keys : nil
|
|
101
108
|
value.each do |k, v|
|
|
102
109
|
if k == SYMBOL_KEYS_KEY
|
|
103
110
|
next
|
|
104
111
|
end
|
|
105
|
-
if sym_keys.include?(k)
|
|
112
|
+
if !symbol_key_values && sym_keys.include?(k)
|
|
106
113
|
k = k.to_sym
|
|
107
114
|
end
|
|
108
115
|
loaded_h[k] = load_value(v)
|
|
109
116
|
end
|
|
117
|
+
if symbol_key_values
|
|
118
|
+
symbol_key_values.each do |k, v|
|
|
119
|
+
loaded_h[k.to_sym] = load_value(v)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
110
122
|
loaded_h
|
|
111
123
|
end
|
|
112
124
|
else
|
|
@@ -120,16 +132,31 @@ module GraphQL
|
|
|
120
132
|
if obj.is_a?(Array)
|
|
121
133
|
obj.map{|item| dump_value(item)}
|
|
122
134
|
elsif obj.is_a?(Hash)
|
|
135
|
+
has_colliding_symbol_key = obj.any? { |k, _v| k.is_a?(Symbol) && obj.key?(k.to_s) }
|
|
136
|
+
if obj.any? { |k, _v| RESERVED_KEYS.include?(k.to_s) }
|
|
137
|
+
return {
|
|
138
|
+
HASH_KEY => obj.map { |k, v| [dump_value(k.is_a?(Symbol) ? k : k.to_s), dump_value(v)] },
|
|
139
|
+
}
|
|
140
|
+
end
|
|
123
141
|
symbol_keys = nil
|
|
142
|
+
symbol_key_values = nil
|
|
124
143
|
dumped_h = {}
|
|
125
144
|
obj.each do |k, v|
|
|
126
|
-
|
|
127
|
-
if k.is_a?(Symbol)
|
|
145
|
+
dumped_v = dump_value(v)
|
|
146
|
+
if has_colliding_symbol_key && k.is_a?(Symbol)
|
|
147
|
+
symbol_key_values ||= {}
|
|
148
|
+
symbol_key_values[k.to_s] = dumped_v
|
|
149
|
+
else
|
|
150
|
+
dumped_h[k.to_s] = dumped_v
|
|
151
|
+
end
|
|
152
|
+
if !has_colliding_symbol_key && k.is_a?(Symbol)
|
|
128
153
|
symbol_keys ||= Set.new
|
|
129
154
|
symbol_keys << k.to_s
|
|
130
155
|
end
|
|
131
156
|
end
|
|
132
|
-
if
|
|
157
|
+
if symbol_key_values
|
|
158
|
+
dumped_h[SYMBOL_KEYS_KEY] = symbol_key_values
|
|
159
|
+
elsif symbol_keys
|
|
133
160
|
dumped_h[SYMBOL_KEYS_KEY] = symbol_keys.to_a
|
|
134
161
|
end
|
|
135
162
|
dumped_h
|
|
@@ -37,13 +37,18 @@ module GraphQL
|
|
|
37
37
|
scope.set_transaction_name(transaction_name(query))
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
if graphql_data_collection.nil? || graphql_data_collection.document
|
|
41
|
+
span.set_data('graphql.document', query.query_string)
|
|
42
|
+
end
|
|
41
43
|
if query.selected_operation_name
|
|
42
44
|
span.set_data('graphql.operation.name', query.selected_operation_name)
|
|
43
45
|
end
|
|
44
46
|
if query.selected_operation
|
|
45
47
|
span.set_data('graphql.operation.type', query.selected_operation.operation_type)
|
|
46
48
|
end
|
|
49
|
+
if graphql_data_collection&.variables && !query.provided_variables.empty?
|
|
50
|
+
span.set_data('graphql.variables', query.provided_variables)
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
|
|
@@ -55,6 +60,13 @@ module GraphQL
|
|
|
55
60
|
|
|
56
61
|
private
|
|
57
62
|
|
|
63
|
+
def graphql_data_collection
|
|
64
|
+
@graphql_data_collection ||= if Sentry.respond_to?(:configuration)
|
|
65
|
+
configuration = Sentry.configuration
|
|
66
|
+
configuration.data_collection.graphql if configuration.respond_to?(:data_collection)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
58
70
|
def operation_name(query)
|
|
59
71
|
selected_op = query.selected_operation
|
|
60
72
|
if selected_op
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.6.
|
|
4
|
+
version: 2.6.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Mosolgo
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|