graphql 2.5.9 → 2.5.10
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/active_record_association_source.rb +14 -2
- data/lib/graphql/dataloader/null_dataloader.rb +7 -0
- data/lib/graphql/execution/interpreter/runtime.rb +4 -15
- data/lib/graphql/language/lexer.rb +9 -2
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +1 -0
- data/lib/graphql/query/context.rb +1 -1
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/argument.rb +5 -0
- data/lib/graphql/schema/build_from_definition.rb +5 -1
- data/lib/graphql/schema/field.rb +8 -0
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/validator/required_validator.rb +12 -3
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +73 -8
- data/lib/graphql/schema/visibility.rb +29 -16
- data/lib/graphql/schema.rb +52 -21
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13d11e14c49ac11e7f851c104641f4234ea464aa5aeb33a4ae50961f4da03e83
|
4
|
+
data.tar.gz: afc9ba506e4cf724b5ff508a73593330d37527461186cb92c264c411204fe13c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3196f1a70900ee0f2597ad54a16ecb960a7f8cefadda7aff81dce760aae2fad6e3a43c29fa3cd9a6792b82edfd34f6be70f9ae61ab1d6c98be01363acf618d9
|
7
|
+
data.tar.gz: 45bfc9181659e24ca4b1e5844942093293f60a13206670ae2c8bc657288a1c025a4dc348b1a0c702aa7bc5aaf80e2a83616db3807d735eee286dd6be4d2144fb
|
@@ -31,7 +31,12 @@ module GraphQL
|
|
31
31
|
def fetch(records)
|
32
32
|
record_classes = Set.new.compare_by_identity
|
33
33
|
associated_classes = Set.new.compare_by_identity
|
34
|
+
scoped_fetch = !@scope.nil?
|
34
35
|
records.each do |record|
|
36
|
+
if scoped_fetch
|
37
|
+
assoc = record.association(@association)
|
38
|
+
assoc.reset
|
39
|
+
end
|
35
40
|
if record_classes.add?(record.class)
|
36
41
|
reflection = record.class.reflect_on_association(@association)
|
37
42
|
if !reflection.polymorphic? && reflection.klass
|
@@ -48,9 +53,16 @@ module GraphQL
|
|
48
53
|
|
49
54
|
::ActiveRecord::Associations::Preloader.new(records: records, associations: @association, available_records: available_records, scope: @scope).call
|
50
55
|
|
51
|
-
loaded_associated_records = records.map { |r|
|
56
|
+
loaded_associated_records = records.map { |r|
|
57
|
+
assoc = r.association(@association)
|
58
|
+
lar = assoc.target
|
59
|
+
if scoped_fetch
|
60
|
+
assoc.reset
|
61
|
+
end
|
62
|
+
lar
|
63
|
+
}
|
52
64
|
|
53
|
-
if
|
65
|
+
if !scoped_fetch
|
54
66
|
# Don't cache records loaded via scope because they might have reduced `SELECT`s
|
55
67
|
# Could check .select_values here?
|
56
68
|
records_by_model = {}
|
@@ -9,8 +9,11 @@ module GraphQL
|
|
9
9
|
class NullDataloader < Dataloader
|
10
10
|
# These are all no-ops because code was
|
11
11
|
# executed synchronously.
|
12
|
+
|
13
|
+
def initialize(*); end
|
12
14
|
def run; end
|
13
15
|
def run_isolated; yield; end
|
16
|
+
def clear_cache; end
|
14
17
|
def yield(_source)
|
15
18
|
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
|
16
19
|
end
|
@@ -19,6 +22,10 @@ module GraphQL
|
|
19
22
|
yield
|
20
23
|
nil
|
21
24
|
end
|
25
|
+
|
26
|
+
def with(*)
|
27
|
+
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
|
28
|
+
end
|
22
29
|
end
|
23
30
|
end
|
24
31
|
end
|
@@ -298,8 +298,6 @@ module GraphQL
|
|
298
298
|
end
|
299
299
|
|
300
300
|
call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
|
301
|
-
finished_jobs = 0
|
302
|
-
enqueued_jobs = gathered_selections.size
|
303
301
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
304
302
|
# Field resolution may pause the fiber,
|
305
303
|
# so it wouldn't get to the `Resolve` call that happens below.
|
@@ -310,12 +308,6 @@ module GraphQL
|
|
310
308
|
evaluate_selection(
|
311
309
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
312
310
|
)
|
313
|
-
finished_jobs += 1
|
314
|
-
if finished_jobs == enqueued_jobs
|
315
|
-
if target_result
|
316
|
-
selections_result.merge_into(target_result)
|
317
|
-
end
|
318
|
-
end
|
319
311
|
@dataloader.clear_cache
|
320
312
|
}
|
321
313
|
else
|
@@ -323,15 +315,12 @@ module GraphQL
|
|
323
315
|
evaluate_selection(
|
324
316
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
325
317
|
)
|
326
|
-
finished_jobs += 1
|
327
|
-
if finished_jobs == enqueued_jobs
|
328
|
-
if target_result
|
329
|
-
selections_result.merge_into(target_result)
|
330
|
-
end
|
331
|
-
end
|
332
318
|
}
|
333
319
|
end
|
334
320
|
end
|
321
|
+
if target_result
|
322
|
+
selections_result.merge_into(target_result)
|
323
|
+
end
|
335
324
|
selections_result
|
336
325
|
end
|
337
326
|
end
|
@@ -557,7 +546,7 @@ module GraphQL
|
|
557
546
|
path
|
558
547
|
end
|
559
548
|
|
560
|
-
HALT = Object.new
|
549
|
+
HALT = Object.new.freeze
|
561
550
|
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
562
551
|
case value
|
563
552
|
when nil
|
@@ -20,6 +20,11 @@ module GraphQL
|
|
20
20
|
@finished
|
21
21
|
end
|
22
22
|
|
23
|
+
def freeze
|
24
|
+
@scanner = nil
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
23
28
|
attr_reader :pos, :tokens_count
|
24
29
|
|
25
30
|
def advance
|
@@ -242,7 +247,7 @@ module GraphQL
|
|
242
247
|
:SCALAR,
|
243
248
|
nil,
|
244
249
|
:FRAGMENT
|
245
|
-
]
|
250
|
+
].freeze
|
246
251
|
|
247
252
|
# This produces a unique integer for bytes 2 and 3 of each keyword string
|
248
253
|
# See https://tenderlovemaking.com/2023/09/02/fast-tokenizers-with-stringscanner.html
|
@@ -271,7 +276,8 @@ module GraphQL
|
|
271
276
|
PUNCTUATION_NAME_FOR_BYTE = Punctuation.constants.each_with_object([]) { |name, arr|
|
272
277
|
punct = Punctuation.const_get(name)
|
273
278
|
arr[punct.ord] = name
|
274
|
-
}
|
279
|
+
}.freeze
|
280
|
+
|
275
281
|
|
276
282
|
QUOTE = '"'
|
277
283
|
UNICODE_DIGIT = /[0-9A-Za-z]/
|
@@ -321,6 +327,7 @@ module GraphQL
|
|
321
327
|
punct = Punctuation.const_get(punct_name)
|
322
328
|
FIRST_BYTES[punct.ord] = ByteFor::PUNCTUATION
|
323
329
|
end
|
330
|
+
FIRST_BYTES.freeze
|
324
331
|
|
325
332
|
|
326
333
|
# Replace any escaped unicode or whitespace with the _actual_ characters
|
@@ -83,7 +83,11 @@ module GraphQL
|
|
83
83
|
|
84
84
|
def to_query_string(printer: GraphQL::Language::Printer.new)
|
85
85
|
if printer.is_a?(GraphQL::Language::Printer)
|
86
|
-
|
86
|
+
if frozen?
|
87
|
+
@query_string || printer.print(self)
|
88
|
+
else
|
89
|
+
@query_string ||= printer.print(self)
|
90
|
+
end
|
87
91
|
else
|
88
92
|
printer.print(self)
|
89
93
|
end
|
@@ -84,7 +84,7 @@ module GraphQL
|
|
84
84
|
|
85
85
|
attr_writer :types
|
86
86
|
|
87
|
-
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
|
87
|
+
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path]).freeze
|
88
88
|
# @!method []=(key, value)
|
89
89
|
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
90
90
|
|
@@ -258,7 +258,9 @@ module GraphQL
|
|
258
258
|
# We can get these now; we'll have to get late-bound types later
|
259
259
|
if interface_type.is_a?(Module) && type.is_a?(Class)
|
260
260
|
implementers = @possible_types[interface_type] ||= []
|
261
|
-
implementers
|
261
|
+
if !implementers.include?(type)
|
262
|
+
implementers << type
|
263
|
+
end
|
262
264
|
end
|
263
265
|
when String, Schema::LateBoundType
|
264
266
|
interface_type = interface_type_membership
|
@@ -212,6 +212,11 @@ module GraphQL
|
|
212
212
|
@statically_coercible = !requires_parent_object
|
213
213
|
end
|
214
214
|
|
215
|
+
def freeze
|
216
|
+
statically_coercible?
|
217
|
+
super
|
218
|
+
end
|
219
|
+
|
215
220
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
216
221
|
# Used by the runtime.
|
217
222
|
# @api private
|
@@ -187,6 +187,10 @@ module GraphQL
|
|
187
187
|
|
188
188
|
object_types.each do |t|
|
189
189
|
t.interfaces.each do |int_t|
|
190
|
+
if int_t.is_a?(LateBoundType)
|
191
|
+
int_t = types[int_t.graphql_name]
|
192
|
+
t.implements(int_t)
|
193
|
+
end
|
190
194
|
int_t.orphan_types(t)
|
191
195
|
end
|
192
196
|
end
|
@@ -519,7 +523,7 @@ module GraphQL
|
|
519
523
|
|
520
524
|
def define_field_resolve_method(owner, method_name, field_name)
|
521
525
|
owner.define_method(method_name) { |**args|
|
522
|
-
field_instance =
|
526
|
+
field_instance = context.types.field(owner, field_name)
|
523
527
|
context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
|
524
528
|
}
|
525
529
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -8,8 +8,8 @@ module GraphQL
|
|
8
8
|
new_memberships = []
|
9
9
|
new_interfaces.each do |int|
|
10
10
|
if int.is_a?(Module)
|
11
|
-
unless int.include?(GraphQL::Schema::Interface)
|
12
|
-
raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
11
|
+
unless int.include?(GraphQL::Schema::Interface) && !int.is_a?(Class)
|
12
|
+
raise "#{int.respond_to?(:graphql_name) ? "#{int.graphql_name} (#{int})" : int.inspect} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
13
13
|
end
|
14
14
|
|
15
15
|
new_memberships << int.type_membership_class.new(int, self, **options)
|
@@ -12,12 +12,26 @@ module GraphQL
|
|
12
12
|
|
13
13
|
# @return [Schema::NonNull] Make a non-null-type representation of this type
|
14
14
|
def to_non_null_type
|
15
|
-
@to_non_null_type
|
15
|
+
@to_non_null_type || begin
|
16
|
+
t = GraphQL::Schema::NonNull.new(self)
|
17
|
+
if frozen?
|
18
|
+
t
|
19
|
+
else
|
20
|
+
@to_non_null_type = t
|
21
|
+
end
|
22
|
+
end
|
16
23
|
end
|
17
24
|
|
18
25
|
# @return [Schema::List] Make a list-type representation of this type
|
19
26
|
def to_list_type
|
20
|
-
@to_list_type
|
27
|
+
@to_list_type || begin
|
28
|
+
t = GraphQL::Schema::List.new(self)
|
29
|
+
if frozen?
|
30
|
+
t
|
31
|
+
else
|
32
|
+
@to_list_type = t
|
33
|
+
end
|
34
|
+
end
|
21
35
|
end
|
22
36
|
|
23
37
|
# @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
module RactorShareable
|
5
|
+
def self.extended(schema_class)
|
6
|
+
schema_class.extend(SchemaExtension)
|
7
|
+
schema_class.freeze_schema
|
8
|
+
end
|
9
|
+
|
10
|
+
module SchemaExtension
|
11
|
+
|
12
|
+
def freeze_error_handlers(handlers)
|
13
|
+
handlers[:subclass_handlers].default_proc = nil
|
14
|
+
handlers[:subclass_handlers].each do |_class, subclass_handlers|
|
15
|
+
freeze_error_handlers(subclass_handlers)
|
16
|
+
end
|
17
|
+
Ractor.make_shareable(handlers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def freeze_schema
|
21
|
+
# warm some ivars:
|
22
|
+
default_analysis_engine
|
23
|
+
default_execution_strategy
|
24
|
+
GraphQL.default_parser
|
25
|
+
default_logger
|
26
|
+
freeze_error_handlers(error_handlers)
|
27
|
+
# TODO: this freezes errors of parent classes which could cause trouble
|
28
|
+
parent_class = superclass
|
29
|
+
while parent_class.respond_to?(:error_handlers)
|
30
|
+
freeze_error_handlers(parent_class.error_handlers)
|
31
|
+
parent_class = parent_class.superclass
|
32
|
+
end
|
33
|
+
|
34
|
+
own_tracers.freeze
|
35
|
+
@frozen_tracers = tracers.freeze
|
36
|
+
own_trace_modes.each do |m|
|
37
|
+
trace_options_for(m)
|
38
|
+
build_trace_mode(m)
|
39
|
+
end
|
40
|
+
build_trace_mode(:default)
|
41
|
+
Ractor.make_shareable(@trace_options_for_mode)
|
42
|
+
Ractor.make_shareable(own_trace_modes)
|
43
|
+
Ractor.make_shareable(own_multiplex_analyzers)
|
44
|
+
@frozen_multiplex_analyzers = Ractor.make_shareable(multiplex_analyzers)
|
45
|
+
Ractor.make_shareable(own_query_analyzers)
|
46
|
+
@frozen_query_analyzers = Ractor.make_shareable(query_analyzers)
|
47
|
+
Ractor.make_shareable(own_plugins)
|
48
|
+
own_plugins.each do |(plugin, options)|
|
49
|
+
Ractor.make_shareable(plugin)
|
50
|
+
Ractor.make_shareable(options)
|
51
|
+
end
|
52
|
+
@frozen_plugins = Ractor.make_shareable(plugins)
|
53
|
+
Ractor.make_shareable(own_references_to)
|
54
|
+
@frozen_directives = Ractor.make_shareable(directives)
|
55
|
+
|
56
|
+
Ractor.make_shareable(visibility)
|
57
|
+
Ractor.make_shareable(introspection_system)
|
58
|
+
extend(FrozenMethods)
|
59
|
+
|
60
|
+
Ractor.make_shareable(self)
|
61
|
+
superclass.respond_to?(:freeze_schema) && superclass.freeze_schema
|
62
|
+
end
|
63
|
+
|
64
|
+
module FrozenMethods
|
65
|
+
def tracers; @frozen_tracers; end
|
66
|
+
def multiplex_analyzers; @frozen_multiplex_analyzers; end
|
67
|
+
def query_analyzers; @frozen_query_analyzers; end
|
68
|
+
def plugins; @frozen_plugins; end
|
69
|
+
def directives; @frozen_directives; end
|
70
|
+
|
71
|
+
# This actually accumulates info during execution...
|
72
|
+
# How to support it?
|
73
|
+
def lazy?(_obj); false; end
|
74
|
+
def sync_lazy(obj); obj; end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -96,17 +96,26 @@ module GraphQL
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def build_message(context)
|
99
|
-
argument_definitions =
|
99
|
+
argument_definitions = context.types.arguments(@validated)
|
100
|
+
|
100
101
|
required_names = @one_of.map do |arg_keyword|
|
101
102
|
if arg_keyword.is_a?(Array)
|
102
103
|
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
104
|
+
names.compact! # hidden arguments are `nil`
|
103
105
|
"(" + names.join(" and ") + ")"
|
104
106
|
else
|
105
107
|
arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
106
108
|
end
|
107
109
|
end
|
110
|
+
required_names.compact! # remove entries for hidden arguments
|
111
|
+
|
108
112
|
|
109
|
-
|
113
|
+
case required_names.size
|
114
|
+
when 0
|
115
|
+
# The required definitions were hidden from the client.
|
116
|
+
# Another option here would be to raise an error in the application....
|
117
|
+
"%{validated} is missing a required argument."
|
118
|
+
when 1
|
110
119
|
"%{validated} must include the following argument: #{required_names.first}."
|
111
120
|
else
|
112
121
|
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
@@ -115,7 +124,7 @@ module GraphQL
|
|
115
124
|
|
116
125
|
def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
117
126
|
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
118
|
-
argument_definition
|
127
|
+
argument_definition&.graphql_name
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
@@ -76,10 +76,10 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
def initialize(context:, schema:, name: nil)
|
79
|
+
def initialize(context:, schema:, name: nil, visibility:)
|
80
80
|
@name = name
|
81
81
|
@skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
|
82
|
-
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema)
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema, visibility: visibility)
|
83
83
|
if !@skip_error
|
84
84
|
context[:visibility_migration_running] = true
|
85
85
|
warden_ctx_vals = context.to_h.dup
|
@@ -31,10 +31,37 @@ module GraphQL
|
|
31
31
|
# @return [Symbol, nil]
|
32
32
|
attr_reader :name
|
33
33
|
|
34
|
-
def
|
34
|
+
def freeze
|
35
|
+
@cached_visible.default_proc = nil
|
36
|
+
@cached_visible_fields.default_proc = nil
|
37
|
+
@cached_visible_fields.each do |type, fields|
|
38
|
+
fields.default_proc = nil
|
39
|
+
end
|
40
|
+
@cached_visible_arguments.default_proc = nil
|
41
|
+
@cached_visible_arguments.each do |type, fields|
|
42
|
+
fields.default_proc = nil
|
43
|
+
end
|
44
|
+
@cached_parent_fields.default_proc = nil
|
45
|
+
@cached_parent_fields.each do |type, fields|
|
46
|
+
fields.default_proc = nil
|
47
|
+
end
|
48
|
+
@cached_parent_arguments.default_proc = nil
|
49
|
+
@cached_parent_arguments.each do |type, args|
|
50
|
+
args.default_proc = nil
|
51
|
+
end
|
52
|
+
@cached_possible_types.default_proc = nil
|
53
|
+
@cached_enum_values.default_proc = nil
|
54
|
+
@cached_fields.default_proc = nil
|
55
|
+
@cached_arguments.default_proc = nil
|
56
|
+
@loadable_possible_types.default_proc = nil
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(name: nil, context:, schema:, visibility:)
|
35
61
|
@name = name
|
36
62
|
@context = context
|
37
63
|
@schema = schema
|
64
|
+
@visibility = visibility
|
38
65
|
@all_types = {}
|
39
66
|
@all_types_loaded = false
|
40
67
|
@unvisited_types = []
|
@@ -122,7 +149,7 @@ module GraphQL
|
|
122
149
|
end
|
123
150
|
|
124
151
|
def type(type_name)
|
125
|
-
t = @
|
152
|
+
t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
126
153
|
if t
|
127
154
|
if t.is_a?(Array)
|
128
155
|
vis_t = nil
|
@@ -250,8 +277,8 @@ module GraphQL
|
|
250
277
|
end
|
251
278
|
|
252
279
|
def directives
|
253
|
-
@all_directives ||= @
|
254
|
-
@cached_visible[dir] && @
|
280
|
+
@all_directives ||= @visibility.all_directives.select { |dir|
|
281
|
+
@cached_visible[dir] && @visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
|
255
282
|
}
|
256
283
|
end
|
257
284
|
|
@@ -276,6 +303,42 @@ module GraphQL
|
|
276
303
|
@cached_visible[enum_value]
|
277
304
|
end
|
278
305
|
|
306
|
+
def preload
|
307
|
+
load_all_types
|
308
|
+
@all_types.each do |type_name, type_defn|
|
309
|
+
if type_defn.kind.fields?
|
310
|
+
fields(type_defn).each do |f|
|
311
|
+
field(type_defn, f.graphql_name)
|
312
|
+
arguments(f).each do |arg|
|
313
|
+
argument(f, arg.graphql_name)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
317
|
+
field(type_defn, f.graphql_name)
|
318
|
+
end
|
319
|
+
elsif type_defn.kind.input_object?
|
320
|
+
arguments(type_defn).each do |arg|
|
321
|
+
argument(type_defn, arg.graphql_name)
|
322
|
+
end
|
323
|
+
elsif type_defn.kind.enum?
|
324
|
+
enum_values(type_defn)
|
325
|
+
end
|
326
|
+
# Lots more to do here
|
327
|
+
end
|
328
|
+
@schema.introspection_system.entry_points.each do |f|
|
329
|
+
arguments(f).each do |arg|
|
330
|
+
argument(f, arg.graphql_name)
|
331
|
+
end
|
332
|
+
field(@schema.query, f.graphql_name)
|
333
|
+
end
|
334
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
335
|
+
arguments(f).each do |arg|
|
336
|
+
argument(f, arg.graphql_name)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
279
342
|
private
|
280
343
|
|
281
344
|
def non_duplicate_items(definitions, visibility_cache)
|
@@ -322,7 +385,7 @@ module GraphQL
|
|
322
385
|
end
|
323
386
|
|
324
387
|
def referenced?(type_defn)
|
325
|
-
@
|
388
|
+
@visibility.all_references[type_defn].any? do |ref|
|
326
389
|
case ref
|
327
390
|
when GraphQL::Schema::Argument
|
328
391
|
@cached_visible_arguments[ref.owner][ref]
|
@@ -340,9 +403,11 @@ module GraphQL
|
|
340
403
|
case type.kind.name
|
341
404
|
when "INTERFACE"
|
342
405
|
pts = []
|
343
|
-
@
|
344
|
-
if
|
345
|
-
|
406
|
+
@visibility.all_interface_type_memberships[type].each do |impl_type, type_memberships|
|
407
|
+
if impl_type.kind.object? && referenced?(impl_type) && @cached_visible[impl_type]
|
408
|
+
if type_memberships.any? { |itm| @cached_visible[itm] }
|
409
|
+
pts << impl_type
|
410
|
+
end
|
346
411
|
end
|
347
412
|
end
|
348
413
|
pts
|
@@ -18,9 +18,6 @@ module GraphQL
|
|
18
18
|
ctx.freeze
|
19
19
|
}
|
20
20
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
21
|
-
if preload
|
22
|
-
schema.visibility.preload
|
23
|
-
end
|
24
21
|
end
|
25
22
|
|
26
23
|
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
@@ -43,6 +40,17 @@ module GraphQL
|
|
43
40
|
@types = nil
|
44
41
|
@all_references = nil
|
45
42
|
@loaded_all = false
|
43
|
+
if preload
|
44
|
+
self.preload
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def freeze
|
49
|
+
load_all
|
50
|
+
@visit = true
|
51
|
+
@interface_type_memberships.default_proc = nil
|
52
|
+
@all_references.default_proc = nil
|
53
|
+
super
|
46
54
|
end
|
47
55
|
|
48
56
|
def all_directives
|
@@ -65,6 +73,8 @@ module GraphQL
|
|
65
73
|
@types[type_name]
|
66
74
|
end
|
67
75
|
|
76
|
+
attr_accessor :types
|
77
|
+
|
68
78
|
def preload?
|
69
79
|
@preload
|
70
80
|
end
|
@@ -86,7 +96,7 @@ module GraphQL
|
|
86
96
|
ensure_all_loaded(types_to_visit)
|
87
97
|
@profiles.each do |profile_name, example_ctx|
|
88
98
|
prof = profile_for(example_ctx)
|
89
|
-
prof.
|
99
|
+
prof.preload
|
90
100
|
end
|
91
101
|
end
|
92
102
|
|
@@ -153,12 +163,12 @@ module GraphQL
|
|
153
163
|
visibility_profile = context[:visibility_profile]
|
154
164
|
if @profiles.include?(visibility_profile)
|
155
165
|
profile_ctx = @profiles[visibility_profile]
|
156
|
-
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
|
166
|
+
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema, visibility: self)
|
157
167
|
elsif @dynamic
|
158
168
|
if context.is_a?(Query::NullContext)
|
159
169
|
top_level_profile
|
160
170
|
else
|
161
|
-
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
171
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
162
172
|
end
|
163
173
|
elsif !context.key?(:visibility_profile)
|
164
174
|
raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
|
@@ -168,7 +178,7 @@ module GraphQL
|
|
168
178
|
elsif context.is_a?(Query::NullContext)
|
169
179
|
top_level_profile
|
170
180
|
else
|
171
|
-
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
181
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
172
182
|
end
|
173
183
|
end
|
174
184
|
|
@@ -181,7 +191,7 @@ module GraphQL
|
|
181
191
|
if refresh
|
182
192
|
@top_level_profile = nil
|
183
193
|
end
|
184
|
-
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
|
194
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema, visibility: self)
|
185
195
|
end
|
186
196
|
|
187
197
|
private
|
@@ -202,7 +212,11 @@ module GraphQL
|
|
202
212
|
def load_all(types: nil)
|
203
213
|
if @visit.nil?
|
204
214
|
# Set up the visit system
|
205
|
-
@interface_type_memberships = Hash.new { |h, interface_type|
|
215
|
+
@interface_type_memberships = Hash.new { |h, interface_type|
|
216
|
+
h[interface_type] = Hash.new { |h2, obj_type|
|
217
|
+
h2[obj_type] = []
|
218
|
+
}.compare_by_identity
|
219
|
+
}.compare_by_identity
|
206
220
|
@directives = []
|
207
221
|
@types = {} # String => Module
|
208
222
|
@all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity
|
@@ -227,7 +241,7 @@ module GraphQL
|
|
227
241
|
@all_references[itm.abstract_type] << member
|
228
242
|
# `itm.object_type` may not actually be `member` if this implementation
|
229
243
|
# is inherited from a superclass
|
230
|
-
@interface_type_memberships[itm.abstract_type] <<
|
244
|
+
@interface_type_memberships[itm.abstract_type][member] << itm
|
231
245
|
end
|
232
246
|
elsif member < GraphQL::Schema::Union
|
233
247
|
@unions_for_references << member
|
@@ -276,12 +290,11 @@ module GraphQL
|
|
276
290
|
|
277
291
|
# TODO: somehow don't iterate over all these,
|
278
292
|
# only the ones that may have been modified
|
279
|
-
@interface_type_memberships.each do |int_type,
|
280
|
-
|
281
|
-
if !
|
282
|
-
|
283
|
-
|
284
|
-
@all_references[impl_type] |= referers
|
293
|
+
@interface_type_memberships.each do |int_type, obj_type_memberships|
|
294
|
+
referrers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
|
295
|
+
if !referrers.empty?
|
296
|
+
obj_type_memberships.each_key do |impl_type|
|
297
|
+
@all_references[impl_type] |= referrers
|
285
298
|
end
|
286
299
|
end
|
287
300
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -7,6 +7,7 @@ require "graphql/schema/find_inherited_value"
|
|
7
7
|
require "graphql/schema/finder"
|
8
8
|
require "graphql/schema/introspection_system"
|
9
9
|
require "graphql/schema/late_bound_type"
|
10
|
+
require "graphql/schema/ractor_shareable"
|
10
11
|
require "graphql/schema/timeout"
|
11
12
|
require "graphql/schema/type_expression"
|
12
13
|
require "graphql/schema/unique_within_type"
|
@@ -148,10 +149,12 @@ module GraphQL
|
|
148
149
|
end
|
149
150
|
|
150
151
|
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
151
|
-
def default_trace_mode(new_mode =
|
152
|
-
if new_mode
|
152
|
+
def default_trace_mode(new_mode = NOT_CONFIGURED)
|
153
|
+
if !NOT_CONFIGURED.equal?(new_mode)
|
153
154
|
@default_trace_mode = new_mode
|
154
|
-
elsif defined?(@default_trace_mode)
|
155
|
+
elsif defined?(@default_trace_mode) &&
|
156
|
+
!@default_trace_mode.nil? # This `nil?` check seems necessary because of
|
157
|
+
# Ractors silently initializing @default_trace_mode somehow
|
155
158
|
@default_trace_mode
|
156
159
|
elsif superclass.respond_to?(:default_trace_mode)
|
157
160
|
superclass.default_trace_mode
|
@@ -365,7 +368,8 @@ module GraphQL
|
|
365
368
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
366
369
|
def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
367
370
|
if use_visibility_profile
|
368
|
-
|
371
|
+
profile = Visibility::Profile.from_context(context, self)
|
372
|
+
return profile.type(type_name)
|
369
373
|
end
|
370
374
|
local_entry = own_types[type_name]
|
371
375
|
type_defn = case local_entry
|
@@ -697,7 +701,21 @@ module GraphQL
|
|
697
701
|
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
698
702
|
end
|
699
703
|
|
700
|
-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
|
704
|
+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
705
|
+
if use_visibility_profile
|
706
|
+
profile = Visibility::Profile.from_context(context, self)
|
707
|
+
parent_type = case type_or_name
|
708
|
+
when String
|
709
|
+
profile.type(type_or_name)
|
710
|
+
when Module
|
711
|
+
type_or_name
|
712
|
+
when LateBoundType
|
713
|
+
profile.type(type_or_name.name)
|
714
|
+
else
|
715
|
+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
716
|
+
end
|
717
|
+
return profile.field(parent_type, field_name)
|
718
|
+
end
|
701
719
|
parent_type = case type_or_name
|
702
720
|
when LateBoundType
|
703
721
|
get_type(type_or_name.name, context)
|
@@ -1105,20 +1123,21 @@ module GraphQL
|
|
1105
1123
|
end
|
1106
1124
|
end
|
1107
1125
|
|
1108
|
-
NEW_HANDLER_HASH = ->(h, k) {
|
1109
|
-
h[k] = {
|
1110
|
-
class: k,
|
1111
|
-
handler: nil,
|
1112
|
-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
1113
|
-
}
|
1114
|
-
}
|
1115
|
-
|
1116
1126
|
def error_handlers
|
1117
|
-
@error_handlers ||=
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1127
|
+
@error_handlers ||= begin
|
1128
|
+
new_handler_hash = ->(h, k) {
|
1129
|
+
h[k] = {
|
1130
|
+
class: k,
|
1131
|
+
handler: nil,
|
1132
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
{
|
1136
|
+
class: nil,
|
1137
|
+
handler: nil,
|
1138
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
1139
|
+
}
|
1140
|
+
end
|
1122
1141
|
end
|
1123
1142
|
|
1124
1143
|
# @api private
|
@@ -1695,7 +1714,11 @@ module GraphQL
|
|
1695
1714
|
# @return [true, false, nil]
|
1696
1715
|
def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
|
1697
1716
|
if NOT_CONFIGURED.equal?(new_value)
|
1698
|
-
@allow_legacy_invalid_empty_selections_on_union
|
1717
|
+
if defined?(@allow_legacy_invalid_empty_selections_on_union)
|
1718
|
+
@allow_legacy_invalid_empty_selections_on_union
|
1719
|
+
else
|
1720
|
+
find_inherited_value(:allow_legacy_invalid_empty_selections_on_union)
|
1721
|
+
end
|
1699
1722
|
else
|
1700
1723
|
@allow_legacy_invalid_empty_selections_on_union = new_value
|
1701
1724
|
end
|
@@ -1726,7 +1749,11 @@ module GraphQL
|
|
1726
1749
|
# @return [true, false, nil]
|
1727
1750
|
def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED)
|
1728
1751
|
if NOT_CONFIGURED.equal?(new_value)
|
1729
|
-
@allow_legacy_invalid_return_type_conflicts
|
1752
|
+
if defined?(@allow_legacy_invalid_return_type_conflicts)
|
1753
|
+
@allow_legacy_invalid_return_type_conflicts
|
1754
|
+
else
|
1755
|
+
find_inherited_value(:allow_legacy_invalid_return_type_conflicts)
|
1756
|
+
end
|
1730
1757
|
else
|
1731
1758
|
@allow_legacy_invalid_return_type_conflicts = new_value
|
1732
1759
|
end
|
@@ -1774,7 +1801,11 @@ module GraphQL
|
|
1774
1801
|
# complexity_cost_calculation_mode(:compare)
|
1775
1802
|
def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED)
|
1776
1803
|
if NOT_CONFIGURED.equal?(new_mode)
|
1777
|
-
@complexity_cost_calculation_mode
|
1804
|
+
if defined?(@complexity_cost_calculation_mode)
|
1805
|
+
@complexity_cost_calculation_mode
|
1806
|
+
else
|
1807
|
+
find_inherited_value(:complexity_cost_calculation_mode)
|
1808
|
+
end
|
1778
1809
|
else
|
1779
1810
|
@complexity_cost_calculation_mode = new_mode
|
1780
1811
|
end
|
@@ -19,13 +19,17 @@ module GraphQL
|
|
19
19
|
:on_field,
|
20
20
|
]
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
VALIDATE_DIRECTIVE_LOCATION_ON_NODE = <<~RUBY
|
23
|
+
def %{method_name}(node, parent)
|
24
24
|
if !node.directives.empty?
|
25
25
|
validate_directive_location(node)
|
26
26
|
end
|
27
27
|
super(node, parent)
|
28
28
|
end
|
29
|
+
RUBY
|
30
|
+
DIRECTIVE_NODE_HOOKS.each do |method_name|
|
31
|
+
# Can't use `define_method {...}` here because the proc can't be isolated for use in non-main Ractors
|
32
|
+
module_eval(VALIDATE_DIRECTIVE_LOCATION_ON_NODE % { method_name: method_name }) # rubocop:disable Development/NoEvalCop
|
29
33
|
end
|
30
34
|
|
31
35
|
private
|
data/lib/graphql/type_kinds.rb
CHANGED
data/lib/graphql/version.rb
CHANGED
data/lib/graphql.rb
CHANGED
@@ -72,7 +72,7 @@ This is probably a bug in GraphQL-Ruby, please report this error on GitHub: http
|
|
72
72
|
GraphQL::Language::Lexer.tokenize(graphql_string)
|
73
73
|
end
|
74
74
|
|
75
|
-
NOT_CONFIGURED = Object.new
|
75
|
+
NOT_CONFIGURED = Object.new.freeze
|
76
76
|
private_constant :NOT_CONFIGURED
|
77
77
|
module EmptyObjects
|
78
78
|
EMPTY_HASH = {}.freeze
|
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.5.
|
4
|
+
version: 2.5.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-07-03 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: base64
|
@@ -622,6 +622,7 @@ files:
|
|
622
622
|
- lib/graphql/schema/non_null.rb
|
623
623
|
- lib/graphql/schema/object.rb
|
624
624
|
- lib/graphql/schema/printer.rb
|
625
|
+
- lib/graphql/schema/ractor_shareable.rb
|
625
626
|
- lib/graphql/schema/relay_classic_mutation.rb
|
626
627
|
- lib/graphql/schema/resolver.rb
|
627
628
|
- lib/graphql/schema/resolver/has_payload_type.rb
|