graphql 2.6.2 → 2.6.3
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/query_complexity.rb +29 -13
- data/lib/graphql/backtrace/table.rb +10 -1
- data/lib/graphql/current.rb +7 -1
- data/lib/graphql/execution/directive_checks.rb +2 -0
- data/lib/graphql/execution/field_resolve_step.rb +13 -0
- data/lib/graphql/execution/finalize.rb +3 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
- data/lib/graphql/execution/load_argument_step.rb +6 -0
- data/lib/graphql/execution/prepare_object_step.rb +6 -0
- data/lib/graphql/execution/runner.rb +1 -1
- data/lib/graphql/language.rb +8 -2
- data/lib/graphql/schema/argument.rb +1 -1
- data/lib/graphql/schema/interface.rb +1 -1
- data/lib/graphql/schema/introspection_system.rb +6 -21
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
- data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
- data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/format_validator.rb +3 -3
- data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/length_validator.rb +6 -6
- data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
- data/lib/graphql/schema/validator/required_validator.rb +6 -4
- data/lib/graphql/schema/validator.rb +9 -0
- data/lib/graphql/schema/visibility/profile.rb +6 -4
- data/lib/graphql/schema/visibility.rb +30 -22
- data/lib/graphql/schema.rb +1 -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: 16abc9c2a5eda0da251dbe7e14433241bc7d038d2527926832ceb29336fb857a
|
|
4
|
+
data.tar.gz: c89bd2a4b340ca30bfa7b823f51e4671972355d54bd56fdaed598c13a415c3b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6452d2a517c502b4a8934582d060885a5f6462de244a1f331433f1076dc57a879cdd081e147f70925064a99607e0794fcf6eadad7a3f4a9111a5ab979c552554
|
|
7
|
+
data.tar.gz: b6a0219606d905fc885ab2192596d1acb1d92bce407defc751bfafb1a7199e9c32275c1d3202f3246251e72b80554698b1f54920f76178f1ddc81b41d628bacd
|
|
@@ -9,6 +9,8 @@ module GraphQL
|
|
|
9
9
|
super
|
|
10
10
|
@skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields
|
|
11
11
|
@complexities_on_type_by_query = {}
|
|
12
|
+
@intersect_cache = Hash.new { |h, k| h[k] = {}.compare_by_identity }.compare_by_identity
|
|
13
|
+
@possible_types_cache = {}.compare_by_identity
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
# Override this method to use the complexity result
|
|
@@ -159,8 +161,22 @@ module GraphQL
|
|
|
159
161
|
|
|
160
162
|
def types_intersect?(query, a, b)
|
|
161
163
|
return true if a == b
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
|
|
165
|
+
if a.object_id < b.object_id
|
|
166
|
+
first_cache = @intersect_cache[a]
|
|
167
|
+
second_key = b
|
|
168
|
+
else
|
|
169
|
+
first_cache = @intersect_cache[b]
|
|
170
|
+
second_key = a
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if first_cache.key?(second_key)
|
|
174
|
+
first_cache[second_key]
|
|
175
|
+
else
|
|
176
|
+
a_types = @possible_types_cache[a] ||= query.types.possible_types(a).to_set
|
|
177
|
+
b_types = @possible_types_cache[b] ||= query.types.possible_types(b).to_set
|
|
178
|
+
first_cache[second_key] = a_types.intersect?(b_types)
|
|
179
|
+
end
|
|
164
180
|
end
|
|
165
181
|
|
|
166
182
|
# A hook which is called whenever a field's max complexity is calculated.
|
|
@@ -175,18 +191,16 @@ module GraphQL
|
|
|
175
191
|
# @param inner_selections [Array<Hash<String, ScopedTypeComplexity>>] Field selections for a scope
|
|
176
192
|
# @return [Integer] Total complexity value for all these selections in the parent scope
|
|
177
193
|
def merged_max_complexity(query, inner_selections)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
194
|
+
child_scopes_by_key = {}
|
|
195
|
+
inner_selections.each do |inner_selection|
|
|
196
|
+
inner_selection.each do |k, v|
|
|
197
|
+
scopes = child_scopes_by_key[k] ||= []
|
|
198
|
+
scopes << v
|
|
199
|
+
end
|
|
182
200
|
end
|
|
183
|
-
|
|
184
201
|
# Add up the total cost for each unique field name's coalesced selections
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# all keys come with at least one scope.
|
|
188
|
-
child_scopes = inner_selections.filter_map { _1[field_key] }
|
|
189
|
-
|
|
202
|
+
total = 0
|
|
203
|
+
child_scopes_by_key.each do |field_key, child_scopes|
|
|
190
204
|
# Compute maximum possible cost of child selections;
|
|
191
205
|
# composites merge their maximums, while leaf scopes are always zero.
|
|
192
206
|
# FieldsWillMerge validation assures all scopes are uniformly composite or leaf.
|
|
@@ -214,8 +228,10 @@ module GraphQL
|
|
|
214
228
|
child_complexity: maximum_children_cost,
|
|
215
229
|
)
|
|
216
230
|
|
|
217
|
-
total
|
|
231
|
+
total += maximum_cost
|
|
218
232
|
end
|
|
233
|
+
|
|
234
|
+
total
|
|
219
235
|
end
|
|
220
236
|
|
|
221
237
|
def legacy_merged_max_complexity(query, inner_selections)
|
|
@@ -90,7 +90,16 @@ module GraphQL
|
|
|
90
90
|
|
|
91
91
|
if ast_node
|
|
92
92
|
field_defn = query.get_field(result.graphql_result_type, ast_node.name)
|
|
93
|
-
args =
|
|
93
|
+
args = begin
|
|
94
|
+
if (cached_args = query.arguments_cache.cached_arguments_for(ast_node, field_defn))
|
|
95
|
+
cached_args.to_h
|
|
96
|
+
else
|
|
97
|
+
EmptyObjects::EMPTY_HASH
|
|
98
|
+
end
|
|
99
|
+
rescue StandardError => err
|
|
100
|
+
"Failed to load arguments, #{err.class}: #{err.message}"
|
|
101
|
+
end
|
|
102
|
+
|
|
94
103
|
field_path = field_defn.path
|
|
95
104
|
if ast_node.alias
|
|
96
105
|
field_path += " as #{ast_node.alias}"
|
data/lib/graphql/current.rb
CHANGED
|
@@ -41,7 +41,13 @@ module GraphQL
|
|
|
41
41
|
# @see GraphQL::Field#path for a string identifying this field
|
|
42
42
|
# @return [GraphQL::Field, nil] The currently-running field, if there is one.
|
|
43
43
|
def self.field
|
|
44
|
-
Fiber[:__graphql_runtime_info]
|
|
44
|
+
if (interpreter_info = Fiber[:__graphql_runtime_info])
|
|
45
|
+
interpreter_info.values&.first&.current_field
|
|
46
|
+
elsif (field = Fiber[:__graphql_current_field])
|
|
47
|
+
field
|
|
48
|
+
else
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
# @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
|
|
@@ -18,11 +18,13 @@ module GraphQL
|
|
|
18
18
|
case name
|
|
19
19
|
when SKIP
|
|
20
20
|
args = query.arguments_for(directive_ast_node, directive_defn)
|
|
21
|
+
next if args.is_a?(GraphQL::ExecutionError)
|
|
21
22
|
if args[:if] == true
|
|
22
23
|
return false
|
|
23
24
|
end
|
|
24
25
|
when INCLUDE
|
|
25
26
|
args = query.arguments_for(directive_ast_node, directive_defn)
|
|
27
|
+
next if args.is_a?(GraphQL::ExecutionError)
|
|
26
28
|
if args[:if] == false
|
|
27
29
|
return false
|
|
28
30
|
end
|
|
@@ -50,11 +50,14 @@ module GraphQL
|
|
|
50
50
|
|
|
51
51
|
def value
|
|
52
52
|
query = @selections_step.query
|
|
53
|
+
set_current_field
|
|
53
54
|
query.current_trace.begin_execute_field(@field_definition, @arguments, @field_results, query)
|
|
54
55
|
sync(@field_results)
|
|
55
56
|
query.current_trace.end_execute_field(@field_definition, @arguments, @field_results, query, @field_results)
|
|
56
57
|
@runner.add_step(self)
|
|
57
58
|
true
|
|
59
|
+
ensure
|
|
60
|
+
set_current_field(nil)
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def sync(lazy)
|
|
@@ -76,6 +79,7 @@ module GraphQL
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
def call
|
|
82
|
+
set_current_field if @field_definition
|
|
79
83
|
if @enqueued_authorization
|
|
80
84
|
enqueue_next_steps
|
|
81
85
|
elsif @finish_extension_idx
|
|
@@ -94,6 +98,8 @@ module GraphQL
|
|
|
94
98
|
else
|
|
95
99
|
raise
|
|
96
100
|
end
|
|
101
|
+
ensure
|
|
102
|
+
set_current_field(nil)
|
|
97
103
|
end
|
|
98
104
|
|
|
99
105
|
def add_graphql_error(err)
|
|
@@ -121,6 +127,7 @@ module GraphQL
|
|
|
121
127
|
query = @selections_step.query
|
|
122
128
|
field_name = @ast_node.name
|
|
123
129
|
@field_definition = query.types.field(@parent_type, field_name) || raise(GraphQL::Error, "No field definition found for #{@parent_type.to_type_signature}.#{ast_node.name} (at #{@ast_node.position})")
|
|
130
|
+
set_current_field
|
|
124
131
|
@arguments, errors = @runner.input_values[query].argument_values(@field_definition, @ast_node.arguments, self) # rubocop:disable Development/ContextIsPassedCop
|
|
125
132
|
if errors
|
|
126
133
|
build_errors_result(errors, nil)
|
|
@@ -131,6 +138,8 @@ module GraphQL
|
|
|
131
138
|
@field_results.nil? # Make sure the arguments flow didn't already call through
|
|
132
139
|
execute_field
|
|
133
140
|
end
|
|
141
|
+
ensure
|
|
142
|
+
set_current_field(nil)
|
|
134
143
|
end
|
|
135
144
|
|
|
136
145
|
# Used for compatibility in Schema::Subscription
|
|
@@ -552,6 +561,10 @@ module GraphQL
|
|
|
552
561
|
@runner.schema.type_error(err, @selections_step.query.context)
|
|
553
562
|
end
|
|
554
563
|
|
|
564
|
+
def set_current_field(new_value = @field_definition)
|
|
565
|
+
Fiber[:__graphql_current_field] = new_value
|
|
566
|
+
end
|
|
567
|
+
|
|
555
568
|
private
|
|
556
569
|
|
|
557
570
|
def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
|
|
@@ -117,7 +117,7 @@ module GraphQL
|
|
|
117
117
|
@current_exec_path << key
|
|
118
118
|
@current_result_path << key
|
|
119
119
|
|
|
120
|
-
field_defn = @query.context.types.field(parent_type, ast_selection.name) || raise("Invariant: No field found for #{
|
|
120
|
+
field_defn = @query.context.types.field(parent_type, ast_selection.name) || raise("Invariant: No field found for #{parent_type.to_type_signature}.#{ast_selection.name}")
|
|
121
121
|
result_type = field_defn.type
|
|
122
122
|
if (result_type_non_null = result_type.non_null?)
|
|
123
123
|
result_type = result_type.of_type
|
|
@@ -154,12 +154,14 @@ module GraphQL
|
|
|
154
154
|
@runner.type_condition_applies?(@query.context, static_type_at_result, t.name)
|
|
155
155
|
)
|
|
156
156
|
result_h = check_object_result(result_h, parent_type, ast_selection.selections)
|
|
157
|
+
return nil if result_h.nil?
|
|
157
158
|
end
|
|
158
159
|
when Language::Nodes::FragmentSpread
|
|
159
160
|
fragment_defn = @query.document.definitions.find { |defn| defn.is_a?(Language::Nodes::FragmentDefinition) && defn.name == ast_selection.name }
|
|
160
161
|
static_type_at_result = @static_type_at[result_h]
|
|
161
162
|
if static_type_at_result && @runner.type_condition_applies?(@query.context, static_type_at_result, fragment_defn.type.name)
|
|
162
163
|
result_h = check_object_result(result_h, parent_type, fragment_defn.selections)
|
|
164
|
+
return nil if result_h.nil?
|
|
163
165
|
end
|
|
164
166
|
end
|
|
165
167
|
end
|
|
@@ -30,7 +30,10 @@ module GraphQL
|
|
|
30
30
|
@storage[argument_owner][parent_object][ast_node] = resolved_args
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
+
end
|
|
33
34
|
|
|
35
|
+
def cached_arguments_for(ast_node, argument_owner)
|
|
36
|
+
@storage[argument_owner][nil][ast_node]
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
# @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
|
|
@@ -14,6 +14,7 @@ module GraphQL
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def value
|
|
17
|
+
@field_resolve_step.set_current_field
|
|
17
18
|
schema = @field_resolve_step.runner.schema
|
|
18
19
|
@loaded_value = schema.sync_lazy(@loaded_value)
|
|
19
20
|
assign_value
|
|
@@ -34,9 +35,12 @@ module GraphQL
|
|
|
34
35
|
@loaded_value = ex_err
|
|
35
36
|
end
|
|
36
37
|
assign_value
|
|
38
|
+
ensure
|
|
39
|
+
@field_resolve_step.set_current_field(nil)
|
|
37
40
|
end
|
|
38
41
|
|
|
39
42
|
def call
|
|
43
|
+
@field_resolve_step.set_current_field
|
|
40
44
|
context = @field_resolve_step.selections_step.query.context
|
|
41
45
|
@loaded_value = begin
|
|
42
46
|
@load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
|
|
@@ -64,6 +68,8 @@ module GraphQL
|
|
|
64
68
|
ex_err
|
|
65
69
|
end
|
|
66
70
|
assign_value
|
|
71
|
+
ensure
|
|
72
|
+
@field_resolve_step.set_current_field(nil)
|
|
67
73
|
end
|
|
68
74
|
|
|
69
75
|
private
|
|
@@ -19,6 +19,7 @@ module GraphQL
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def value
|
|
22
|
+
@field_resolve_step.set_current_field
|
|
22
23
|
if @authorized_value
|
|
23
24
|
query = @field_resolve_step.selections_step.query
|
|
24
25
|
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
@@ -36,9 +37,12 @@ module GraphQL
|
|
|
36
37
|
ctx.query.current_trace.end_resolve_type(st, @object, ctx, @resolved_type)
|
|
37
38
|
end
|
|
38
39
|
@runner.add_step(self)
|
|
40
|
+
ensure
|
|
41
|
+
@field_resolve_step.set_current_field(nil)
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def call
|
|
45
|
+
@field_resolve_step.set_current_field
|
|
42
46
|
case @next_step
|
|
43
47
|
when :resolve_type
|
|
44
48
|
static_type = @field_resolve_step.static_type
|
|
@@ -65,6 +69,8 @@ module GraphQL
|
|
|
65
69
|
else
|
|
66
70
|
raise ArgumentError, "This is a bug, unknown step: #{@next_step.inspect}"
|
|
67
71
|
end
|
|
72
|
+
ensure
|
|
73
|
+
@field_resolve_step.set_current_field(nil)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
def authorize
|
|
@@ -116,7 +116,7 @@ module GraphQL
|
|
|
116
116
|
query.context.add_error(err)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
trace.execute_query_lazy(query: nil, multiplex: @multiplex) do
|
|
119
|
+
trace.execute_query_lazy(query: @multiplex.queries.size == 1 ? @multiplex.queries.first : nil, multiplex: @multiplex) do
|
|
120
120
|
while (next_isolated_steps = isolated_steps.shift)
|
|
121
121
|
next_isolated_steps.each do |step|
|
|
122
122
|
add_step(step)
|
data/lib/graphql/language.rb
CHANGED
|
@@ -47,13 +47,19 @@ module GraphQL
|
|
|
47
47
|
def self.escape_single_quoted_newlines(query_str)
|
|
48
48
|
scanner = StringScanner.new(query_str)
|
|
49
49
|
inside_single_quoted_string = false
|
|
50
|
+
inside_triple_quoted_string = false
|
|
50
51
|
new_query_str = nil
|
|
51
52
|
while !scanner.eos?
|
|
52
|
-
if scanner.skip(
|
|
53
|
+
if scanner.skip('"""')
|
|
54
|
+
inside_triple_quoted_string = !inside_triple_quoted_string
|
|
55
|
+
new_query_str && (new_query_str << scanner.matched)
|
|
56
|
+
elsif scanner.skip(/(?:\\"|[^"\n\r])+/m)
|
|
53
57
|
new_query_str && (new_query_str << scanner.matched)
|
|
54
58
|
elsif scanner.skip('"')
|
|
55
59
|
new_query_str && (new_query_str << '"')
|
|
56
|
-
|
|
60
|
+
if !inside_triple_quoted_string
|
|
61
|
+
inside_single_quoted_string = !inside_single_quoted_string
|
|
62
|
+
end
|
|
57
63
|
elsif scanner.skip("\n")
|
|
58
64
|
if inside_single_quoted_string
|
|
59
65
|
new_query_str ||= query_str[0, scanner.pos - 1]
|
|
@@ -101,7 +101,7 @@ module GraphQL
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
child_class.ancestors.reverse_each do |ancestor|
|
|
104
|
-
if ancestor.const_defined?(:ResolverMethods)
|
|
104
|
+
if ancestor != child_class && ancestor <= GraphQL::Schema::Interface && ancestor.const_defined?(:ResolverMethods, false)
|
|
105
105
|
child_class.extend(ancestor::ResolverMethods)
|
|
106
106
|
end
|
|
107
107
|
end
|
|
@@ -105,11 +105,13 @@ module GraphQL
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def load_constant(class_name)
|
|
108
|
-
const =
|
|
108
|
+
const = begin
|
|
109
|
+
@custom_namespace.const_get(class_name)
|
|
110
|
+
rescue NameError
|
|
111
|
+
# Dup the built-in so that the cached fields aren't shared
|
|
112
|
+
@built_in_namespace.const_get(class_name)
|
|
113
|
+
end
|
|
109
114
|
dup_type_class(const)
|
|
110
|
-
rescue NameError
|
|
111
|
-
# Dup the built-in so that the cached fields aren't shared
|
|
112
|
-
dup_type_class(@built_in_namespace.const_get(class_name))
|
|
113
115
|
end
|
|
114
116
|
|
|
115
117
|
def get_fields_from_class(class_sym:)
|
|
@@ -133,23 +135,6 @@ module GraphQL
|
|
|
133
135
|
end
|
|
134
136
|
end
|
|
135
137
|
end
|
|
136
|
-
|
|
137
|
-
class PerFieldProxyResolve
|
|
138
|
-
def initialize(object_class:, inner_resolve:)
|
|
139
|
-
@object_class = object_class
|
|
140
|
-
@inner_resolve = inner_resolve
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def call(obj, args, ctx)
|
|
144
|
-
query_ctx = ctx.query.context
|
|
145
|
-
# Remove the QueryType wrapper
|
|
146
|
-
if obj.is_a?(GraphQL::Schema::Object)
|
|
147
|
-
obj = obj.object
|
|
148
|
-
end
|
|
149
|
-
wrapped_object = @object_class.wrap(obj, query_ctx)
|
|
150
|
-
@inner_resolve.call(wrapped_object, args, ctx)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
138
|
end
|
|
154
139
|
end
|
|
155
140
|
end
|
|
@@ -8,7 +8,7 @@ module GraphQL
|
|
|
8
8
|
# @example Require a non-empty string for an argument
|
|
9
9
|
# argument :name, String, required: true, validate: { allow_blank: false }
|
|
10
10
|
class AllowBlankValidator < Validator
|
|
11
|
-
def initialize(allow_blank_positional, allow_blank: nil, message: "%{validated} can't be blank", **default_options)
|
|
11
|
+
def initialize(allow_blank_positional = nil, allow_blank: nil, message: "%{validated} can't be blank", **default_options)
|
|
12
12
|
@message = message
|
|
13
13
|
super(**default_options)
|
|
14
14
|
@allow_blank = allow_blank.nil? ? allow_blank_positional : allow_blank
|
|
@@ -16,10 +16,10 @@ module GraphQL
|
|
|
16
16
|
|
|
17
17
|
def validate(_object, _context, value)
|
|
18
18
|
if value.respond_to?(:blank?) && value.blank?
|
|
19
|
-
if (value.nil? && @allow_null) || @allow_blank
|
|
19
|
+
if (value.nil? && validation_parameter(@allow_null)) || validation_parameter(@allow_blank)
|
|
20
20
|
# pass
|
|
21
21
|
else
|
|
22
|
-
@message
|
|
22
|
+
validation_parameter(@message)
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -9,15 +9,15 @@ module GraphQL
|
|
|
9
9
|
# argument :name, String, required: false, validates: { allow_null: false }
|
|
10
10
|
class AllowNullValidator < Validator
|
|
11
11
|
MESSAGE = "%{validated} can't be null"
|
|
12
|
-
def initialize(allow_null_positional, allow_null: nil, message: MESSAGE, **default_options)
|
|
12
|
+
def initialize(allow_null_positional = nil, allow_null: nil, message: MESSAGE, **default_options)
|
|
13
13
|
@message = message
|
|
14
14
|
super(**default_options)
|
|
15
15
|
@allow_null = allow_null.nil? ? allow_null_positional : allow_null
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def validate(_object, _context, value)
|
|
19
|
-
if value.nil? &&
|
|
20
|
-
@message
|
|
19
|
+
if value.nil? && !validation_parameter(@allow_null)
|
|
20
|
+
validation_parameter(@message)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -23,8 +23,8 @@ module GraphQL
|
|
|
23
23
|
def validate(_object, _context, value)
|
|
24
24
|
if permitted_empty_value?(value)
|
|
25
25
|
# pass
|
|
26
|
-
elsif @in_list.include?(value)
|
|
27
|
-
@message
|
|
26
|
+
elsif validation_parameter(@in_list).include?(value)
|
|
27
|
+
validation_parameter(@message)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -37,9 +37,9 @@ module GraphQL
|
|
|
37
37
|
if permitted_empty_value?(value)
|
|
38
38
|
# Do nothing
|
|
39
39
|
elsif value.nil? ||
|
|
40
|
-
(@with_pattern && !value.match?(@with_pattern)) ||
|
|
41
|
-
(@without_pattern && value.match?(@without_pattern))
|
|
42
|
-
@message
|
|
40
|
+
(@with_pattern && !value.match?(validation_parameter(@with_pattern))) ||
|
|
41
|
+
(@without_pattern && value.match?(validation_parameter(@without_pattern)))
|
|
42
|
+
validation_parameter(@message)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -25,8 +25,8 @@ module GraphQL
|
|
|
25
25
|
def validate(_object, _context, value)
|
|
26
26
|
if permitted_empty_value?(value)
|
|
27
27
|
# pass
|
|
28
|
-
elsif
|
|
29
|
-
@message
|
|
28
|
+
elsif !validation_parameter(@in_list).include?(value)
|
|
29
|
+
validation_parameter(@message)
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -45,12 +45,12 @@ module GraphQL
|
|
|
45
45
|
def validate(_object, _context, value)
|
|
46
46
|
return if permitted_empty_value?(value) # pass in this case
|
|
47
47
|
length = value.nil? ? 0 : value.length
|
|
48
|
-
if @maximum && length >
|
|
49
|
-
partial_format(@too_long, { count:
|
|
50
|
-
elsif @minimum && length <
|
|
51
|
-
partial_format(@too_short, { count:
|
|
52
|
-
elsif @is && length !=
|
|
53
|
-
partial_format(@wrong_length, { count:
|
|
48
|
+
if (current_max = validation_parameter(@maximum)) && length > current_max
|
|
49
|
+
partial_format(validation_parameter(@too_long), { count: current_max })
|
|
50
|
+
elsif (current_min = validation_parameter(@minimum)) && length < current_min
|
|
51
|
+
partial_format(validation_parameter(@too_short), { count: current_min })
|
|
52
|
+
elsif (current_is = validation_parameter(@is)) && length != current_is
|
|
53
|
+
partial_format(validation_parameter(@wrong_length), { count: current_is })
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
end
|
|
@@ -55,25 +55,25 @@ module GraphQL
|
|
|
55
55
|
if permitted_empty_value?(value)
|
|
56
56
|
# pass in this case
|
|
57
57
|
elsif value.nil? # @allow_null is handled in the parent class
|
|
58
|
-
@null_message
|
|
59
|
-
elsif @greater_than && value <=
|
|
60
|
-
partial_format(@message, { comparison: "greater than", target:
|
|
61
|
-
elsif @greater_than_or_equal_to && value <
|
|
62
|
-
partial_format(@message, { comparison: "greater than or equal to", target:
|
|
63
|
-
elsif @less_than && value >=
|
|
64
|
-
partial_format(@message, { comparison: "less than", target:
|
|
65
|
-
elsif @less_than_or_equal_to && value >
|
|
66
|
-
partial_format(@message, { comparison: "less than or equal to", target:
|
|
67
|
-
elsif @equal_to && value !=
|
|
68
|
-
partial_format(@message, { comparison: "equal to", target:
|
|
69
|
-
elsif @other_than && value ==
|
|
70
|
-
partial_format(@message, { comparison: "something other than", target:
|
|
71
|
-
elsif @even && !value.even?
|
|
72
|
-
(partial_format(@message, { comparison: "even", target: "" })).strip
|
|
73
|
-
elsif @odd && !value.odd?
|
|
74
|
-
(partial_format(@message, { comparison: "odd", target: "" })).strip
|
|
75
|
-
elsif @within &&
|
|
76
|
-
partial_format(@message, { comparison: "within", target:
|
|
58
|
+
validation_parameter(@null_message)
|
|
59
|
+
elsif (current_greater_than = validation_parameter(@greater_than)) && value <= current_greater_than
|
|
60
|
+
partial_format(validation_parameter(@message), { comparison: "greater than", target: current_greater_than })
|
|
61
|
+
elsif (current_greater_than_or_equal_to = validation_parameter(@greater_than_or_equal_to)) && value < current_greater_than_or_equal_to
|
|
62
|
+
partial_format(validation_parameter(@message), { comparison: "greater than or equal to", target: current_greater_than_or_equal_to })
|
|
63
|
+
elsif (current_less_than = validation_parameter(@less_than)) && value >= current_less_than
|
|
64
|
+
partial_format(validation_parameter(@message), { comparison: "less than", target: current_less_than })
|
|
65
|
+
elsif (current_less_than_or_equal_to = validation_parameter(@less_than_or_equal_to)) && value > current_less_than_or_equal_to
|
|
66
|
+
partial_format(validation_parameter(@message), { comparison: "less than or equal to", target: current_less_than_or_equal_to })
|
|
67
|
+
elsif (current_equal_to = validation_parameter(@equal_to)) && value != current_equal_to
|
|
68
|
+
partial_format(validation_parameter(@message), { comparison: "equal to", target: current_equal_to })
|
|
69
|
+
elsif (current_other_than = validation_parameter(@other_than)) && value == current_other_than
|
|
70
|
+
partial_format(validation_parameter(@message), { comparison: "something other than", target: current_other_than })
|
|
71
|
+
elsif validation_parameter(@even) && !value.even?
|
|
72
|
+
(partial_format(validation_parameter(@message), { comparison: "even", target: "" })).strip
|
|
73
|
+
elsif validation_parameter(@odd) && !value.odd?
|
|
74
|
+
(partial_format(validation_parameter(@message), { comparison: "odd", target: "" })).strip
|
|
75
|
+
elsif (current_within = validation_parameter(@within)) && !current_within.include?(value)
|
|
76
|
+
partial_format(validation_parameter(@message), { comparison: "within", target: current_within })
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
end
|
|
@@ -67,7 +67,8 @@ module GraphQL
|
|
|
67
67
|
no_visible_conditions = true
|
|
68
68
|
|
|
69
69
|
if !value.nil?
|
|
70
|
-
@one_of.each do |one_of_condition|
|
|
70
|
+
validation_parameter(@one_of).each do |one_of_condition|
|
|
71
|
+
one_of_condition = validation_parameter(one_of_condition)
|
|
71
72
|
case one_of_condition
|
|
72
73
|
when Symbol
|
|
73
74
|
if no_visible_conditions && visible_keywords.include?(one_of_condition)
|
|
@@ -108,7 +109,7 @@ module GraphQL
|
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
if no_visible_conditions
|
|
111
|
-
if @allow_all_hidden
|
|
112
|
+
if validation_parameter(@allow_all_hidden)
|
|
112
113
|
return nil
|
|
113
114
|
else
|
|
114
115
|
raise GraphQL::Error, <<~ERR
|
|
@@ -122,7 +123,7 @@ module GraphQL
|
|
|
122
123
|
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
|
123
124
|
nil # OK
|
|
124
125
|
else
|
|
125
|
-
@message || build_message(context)
|
|
126
|
+
validation_parameter(@message) || build_message(context)
|
|
126
127
|
end
|
|
127
128
|
end
|
|
128
129
|
|
|
@@ -130,8 +131,9 @@ module GraphQL
|
|
|
130
131
|
argument_definitions = context.types.arguments(@validated)
|
|
131
132
|
|
|
132
133
|
required_names = @one_of.map do |arg_keyword|
|
|
134
|
+
arg_keyword = validation_parameter(arg_keyword)
|
|
133
135
|
if arg_keyword.is_a?(Array)
|
|
134
|
-
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
|
136
|
+
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, validation_parameter(arg)) }
|
|
135
137
|
names.compact! # hidden arguments are `nil`
|
|
136
138
|
"(" + names.join(" and ") + ")"
|
|
137
139
|
else
|
|
@@ -34,6 +34,15 @@ module GraphQL
|
|
|
34
34
|
string
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# @return [Object] The current value to use for validation, based on `config_value` from configuration time. If a Proc is given, this calls it and returns it.
|
|
38
|
+
def validation_parameter(config_value)
|
|
39
|
+
if config_value.is_a?(Proc)
|
|
40
|
+
config_value.call
|
|
41
|
+
else
|
|
42
|
+
config_value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
37
46
|
# @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true`
|
|
38
47
|
def permitted_empty_value?(value)
|
|
39
48
|
(value.nil? && @allow_null) ||
|
|
@@ -290,11 +290,13 @@ module GraphQL
|
|
|
290
290
|
end
|
|
291
291
|
# Lots more to do here
|
|
292
292
|
end
|
|
293
|
-
@schema.
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
if @schema.query
|
|
294
|
+
@schema.introspection_system.entry_points.each do |f|
|
|
295
|
+
arguments(f).each do |arg|
|
|
296
|
+
argument(f, arg.graphql_name)
|
|
297
|
+
end
|
|
298
|
+
field(@schema.query, f.graphql_name)
|
|
296
299
|
end
|
|
297
|
-
field(@schema.query, f.graphql_name)
|
|
298
300
|
end
|
|
299
301
|
@schema.introspection_system.dynamic_fields.each do |f|
|
|
300
302
|
arguments(f).each do |arg|
|
|
@@ -8,11 +8,17 @@ module GraphQL
|
|
|
8
8
|
# Use this plugin to make some parts of your schema hidden from some viewers.
|
|
9
9
|
#
|
|
10
10
|
class Visibility
|
|
11
|
+
class TypeConfigurationError < GraphQL::Error
|
|
12
|
+
def initialize(config_message, config_str)
|
|
13
|
+
message = "GraphQL::Schema::Visibility already preloaded, but #{config_message} added to the schema. Move this `#{config_str}` configuration above `use(GraphQL::Schema::Visibility)"
|
|
14
|
+
super(message)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
11
17
|
# @param schema [Class<GraphQL::Schema>]
|
|
12
18
|
# @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
|
|
13
19
|
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?` and `Rails.env.staging?`)
|
|
14
20
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
|
15
|
-
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging?) :
|
|
21
|
+
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging? || nil) : false), migration_errors: false)
|
|
16
22
|
profiles&.each { |name, ctx|
|
|
17
23
|
ctx[:visibility_profile] = name
|
|
18
24
|
ctx.freeze
|
|
@@ -20,7 +26,7 @@ module GraphQL
|
|
|
20
26
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
|
29
|
+
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:, configuration_inherited: false)
|
|
24
30
|
@schema = schema
|
|
25
31
|
schema.use_visibility_profile = true
|
|
26
32
|
schema.visibility_profile_class = if migration_errors
|
|
@@ -40,6 +46,7 @@ module GraphQL
|
|
|
40
46
|
@types = nil
|
|
41
47
|
@all_references = nil
|
|
42
48
|
@loaded_all = false
|
|
49
|
+
@configuration_inherited = configuration_inherited
|
|
43
50
|
if preload
|
|
44
51
|
self.preload
|
|
45
52
|
end
|
|
@@ -94,6 +101,7 @@ module GraphQL
|
|
|
94
101
|
# Root types may have been nil:
|
|
95
102
|
types_to_visit.compact!
|
|
96
103
|
ensure_all_loaded(types_to_visit)
|
|
104
|
+
@cached_profiles.clear
|
|
97
105
|
@profiles.each do |profile_name, example_ctx|
|
|
98
106
|
prof = profile_for(example_ctx)
|
|
99
107
|
prof.preload
|
|
@@ -102,41 +110,27 @@ module GraphQL
|
|
|
102
110
|
|
|
103
111
|
# @api private
|
|
104
112
|
def query_configured(query_type)
|
|
105
|
-
|
|
106
|
-
ensure_all_loaded([query_type])
|
|
107
|
-
end
|
|
113
|
+
require_if_preloaded("a query type was", "query(...)")
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
# @api private
|
|
111
117
|
def mutation_configured(mutation_type)
|
|
112
|
-
|
|
113
|
-
ensure_all_loaded([mutation_type])
|
|
114
|
-
end
|
|
118
|
+
require_if_preloaded("a mutation type was", "mutation(...)")
|
|
115
119
|
end
|
|
116
120
|
|
|
117
121
|
# @api private
|
|
118
122
|
def subscription_configured(subscription_type)
|
|
119
|
-
|
|
120
|
-
ensure_all_loaded([subscription_type])
|
|
121
|
-
end
|
|
123
|
+
require_if_preloaded("a mutation type was", "subscription(...)")
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
# @api private
|
|
125
127
|
def orphan_types_configured(orphan_types)
|
|
126
|
-
|
|
127
|
-
ensure_all_loaded(orphan_types)
|
|
128
|
-
end
|
|
128
|
+
require_if_preloaded("orphan types were", "orphan_types(...)")
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
# @api private
|
|
132
132
|
def introspection_system_configured(introspection_system)
|
|
133
|
-
|
|
134
|
-
introspection_types = [
|
|
135
|
-
*@schema.introspection_system.types.values,
|
|
136
|
-
*@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
|
|
137
|
-
]
|
|
138
|
-
ensure_all_loaded(introspection_types)
|
|
139
|
-
end
|
|
133
|
+
require_if_preloaded("custom introspection was", "introspection(...)")
|
|
140
134
|
end
|
|
141
135
|
|
|
142
136
|
# Make another Visibility for `schema` based on this one
|
|
@@ -148,7 +142,8 @@ module GraphQL
|
|
|
148
142
|
dynamic: @dynamic,
|
|
149
143
|
preload: @preload,
|
|
150
144
|
profiles: @profiles,
|
|
151
|
-
migration_errors: @migration_errors
|
|
145
|
+
migration_errors: @migration_errors,
|
|
146
|
+
configuration_inherited: true,
|
|
152
147
|
)
|
|
153
148
|
end
|
|
154
149
|
|
|
@@ -196,6 +191,19 @@ module GraphQL
|
|
|
196
191
|
|
|
197
192
|
private
|
|
198
193
|
|
|
194
|
+
def require_if_preloaded(config_message, config_code)
|
|
195
|
+
case @preload
|
|
196
|
+
when false
|
|
197
|
+
# Rails.env wasn't defined, so this won't try to preload unless manually set to true
|
|
198
|
+
when true, nil
|
|
199
|
+
if @configuration_inherited
|
|
200
|
+
preload
|
|
201
|
+
else
|
|
202
|
+
raise TypeConfigurationError.new(config_message, config_code)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
199
207
|
def ensure_all_loaded(types_to_visit)
|
|
200
208
|
while (type = types_to_visit.shift)
|
|
201
209
|
if type.kind.fields? && @preloaded_types.add?(type)
|
data/lib/graphql/schema.rb
CHANGED
|
@@ -757,6 +757,7 @@ module GraphQL
|
|
|
757
757
|
# reset this cached value:
|
|
758
758
|
@introspection_system = nil
|
|
759
759
|
introspection_system
|
|
760
|
+
self.visibility&.introspection_system_configured(introspection_system)
|
|
760
761
|
@introspection
|
|
761
762
|
else
|
|
762
763
|
@introspection || find_inherited_value(:introspection)
|
|
@@ -768,7 +769,6 @@ module GraphQL
|
|
|
768
769
|
if !@introspection_system
|
|
769
770
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
|
770
771
|
@introspection_system.resolve_late_bindings
|
|
771
|
-
self.visibility&.introspection_system_configured(@introspection_system)
|
|
772
772
|
end
|
|
773
773
|
@introspection_system
|
|
774
774
|
end
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Mosolgo
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-05-
|
|
10
|
+
date: 2026-05-26 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|