graphql 1.11.5 → 1.11.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/object_generator.rb +2 -0
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/execution/interpreter/arguments.rb +21 -6
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/runtime.rb +53 -39
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/pagination/connections.rb +11 -5
- data/lib/graphql/query/context.rb +4 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query.rb +4 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/schema/build_from_definition.rb +11 -7
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/field/connection_extension.rb +8 -7
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +22 -12
- data/lib/graphql/schema/member/has_arguments.rb +51 -52
- data/lib/graphql/schema/member/has_fields.rb +2 -2
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema.rb +39 -11
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +3 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/validation_context.rb +6 -1
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +33 -10
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/tracing/platform_tracing.rb +1 -1
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/relay/base_connection.rb +2 -1
- data/lib/graphql/types/relay/base_edge.rb +2 -1
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -0
- data/readme.md +1 -1
- metadata +10 -6
@@ -85,70 +85,69 @@ module GraphQL
|
|
85
85
|
# @param context [GraphQL::Query::Context]
|
86
86
|
# @return [Hash<Symbol, Object>, Execution::Lazy<Hash>]
|
87
87
|
def coerce_arguments(parent_object, values, context)
|
88
|
-
argument_values = {}
|
89
|
-
kwarg_arguments = {}
|
90
88
|
# Cache this hash to avoid re-merging it
|
91
89
|
arg_defns = self.arguments
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
has_value =
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
loads = arg_defn.loads
|
112
|
-
loaded_value = nil
|
113
|
-
if loads && !arg_defn.from_resolver?
|
114
|
-
loaded_value = if arg_defn.type.list?
|
115
|
-
loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
116
|
-
context.schema.after_any_lazies(loaded_values) { |result| result }
|
117
|
-
else
|
118
|
-
load_application_object(arg_defn, loads, value, context)
|
119
|
-
end
|
91
|
+
if arg_defns.empty?
|
92
|
+
GraphQL::Execution::Interpreter::Arguments.new(argument_values: nil)
|
93
|
+
else
|
94
|
+
argument_values = {}
|
95
|
+
arg_lazies = arg_defns.map do |arg_name, arg_defn|
|
96
|
+
arg_key = arg_defn.keyword
|
97
|
+
has_value = false
|
98
|
+
default_used = false
|
99
|
+
if values.key?(arg_name)
|
100
|
+
has_value = true
|
101
|
+
value = values[arg_name]
|
102
|
+
elsif values.key?(arg_key)
|
103
|
+
has_value = true
|
104
|
+
value = values[arg_key]
|
105
|
+
elsif arg_defn.default_value?
|
106
|
+
has_value = true
|
107
|
+
value = arg_defn.default_value
|
108
|
+
default_used = true
|
120
109
|
end
|
121
110
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
arg_defn.type.
|
111
|
+
if has_value
|
112
|
+
loads = arg_defn.loads
|
113
|
+
loaded_value = nil
|
114
|
+
if loads && !arg_defn.from_resolver?
|
115
|
+
loaded_value = if arg_defn.type.list?
|
116
|
+
loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
117
|
+
context.schema.after_any_lazies(loaded_values) { |result| result }
|
118
|
+
else
|
119
|
+
load_application_object(arg_defn, loads, value, context)
|
120
|
+
end
|
127
121
|
end
|
128
|
-
end
|
129
122
|
|
130
|
-
|
131
|
-
|
132
|
-
|
123
|
+
coerced_value = if loaded_value
|
124
|
+
loaded_value
|
125
|
+
else
|
126
|
+
context.schema.error_handler.with_error_handling(context) do
|
127
|
+
arg_defn.type.coerce_input(value, context)
|
128
|
+
end
|
133
129
|
end
|
134
130
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
131
|
+
context.schema.after_lazy(coerced_value) do |coerced_value|
|
132
|
+
prepared_value = context.schema.error_handler.with_error_handling(context) do
|
133
|
+
arg_defn.prepare_value(parent_object, coerced_value, context: context)
|
134
|
+
end
|
135
|
+
|
136
|
+
# TODO code smell to access such a deeply-nested constant in a distant module
|
137
|
+
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
|
138
|
+
value: prepared_value,
|
139
|
+
definition: arg_defn,
|
140
|
+
default_used: default_used,
|
141
|
+
)
|
142
|
+
end
|
142
143
|
end
|
143
144
|
end
|
144
|
-
end
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
)
|
146
|
+
context.schema.after_any_lazies(arg_lazies) do
|
147
|
+
GraphQL::Execution::Interpreter::Arguments.new(
|
148
|
+
argument_values: argument_values,
|
149
|
+
)
|
150
|
+
end
|
152
151
|
end
|
153
152
|
end
|
154
153
|
|
@@ -82,9 +82,9 @@ module GraphQL
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def global_id_field(field_name)
|
85
|
+
def global_id_field(field_name, **kwargs)
|
86
86
|
id_resolver = GraphQL::Relay::GlobalIdResolve.new(type: self)
|
87
|
-
field field_name, "ID", null: false
|
87
|
+
field field_name, "ID", **kwargs, null: false
|
88
88
|
define_method(field_name) do
|
89
89
|
id_resolver.call(object, {}, context)
|
90
90
|
end
|
@@ -105,7 +105,7 @@ module GraphQL
|
|
105
105
|
sig = super
|
106
106
|
# Arguments were added at the root, but they should be nested
|
107
107
|
sig[:arguments].clear
|
108
|
-
sig[:arguments][:input] = { type: input_type, required: true }
|
108
|
+
sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
|
109
109
|
sig
|
110
110
|
end
|
111
111
|
|
@@ -27,8 +27,7 @@ module GraphQL
|
|
27
27
|
# @param node_id [String] A unique ID generated by {.encode}
|
28
28
|
# @return [Array<(String, String)>] The type name & value passed to {.encode}
|
29
29
|
def decode(node_id, separator: self.default_id_separator)
|
30
|
-
|
31
|
-
Base64Bp.urlsafe_decode64(node_id).split(separator, 2)
|
30
|
+
GraphQL::Schema::Base64Encoder.decode(node_id).split(separator, 2)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -157,7 +157,7 @@ module GraphQL
|
|
157
157
|
|
158
158
|
accepts_definitions \
|
159
159
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
160
|
-
:max_depth, :max_complexity, :default_max_page_size,
|
160
|
+
:validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
|
161
161
|
:orphan_types, :resolve_type, :type_error, :parse_error,
|
162
162
|
:error_bubbling,
|
163
163
|
:raise_definition_error,
|
@@ -196,7 +196,7 @@ module GraphQL
|
|
196
196
|
attr_accessor \
|
197
197
|
:query, :mutation, :subscription,
|
198
198
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
199
|
-
:max_depth, :max_complexity, :default_max_page_size,
|
199
|
+
:validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
|
200
200
|
:orphan_types, :directives,
|
201
201
|
:query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
|
202
202
|
:cursor_encoder,
|
@@ -366,7 +366,7 @@ module GraphQL
|
|
366
366
|
validator_opts = { schema: self }
|
367
367
|
rules && (validator_opts[:rules] = rules)
|
368
368
|
validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
|
369
|
-
res = validator.validate(query)
|
369
|
+
res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
|
370
370
|
res[:errors]
|
371
371
|
end
|
372
372
|
|
@@ -950,6 +950,8 @@ module GraphQL
|
|
950
950
|
schema_defn.query = query && query.graphql_definition
|
951
951
|
schema_defn.mutation = mutation && mutation.graphql_definition
|
952
952
|
schema_defn.subscription = subscription && subscription.graphql_definition
|
953
|
+
schema_defn.validate_timeout = validate_timeout
|
954
|
+
schema_defn.validate_max_errors = validate_max_errors
|
953
955
|
schema_defn.max_complexity = max_complexity
|
954
956
|
schema_defn.error_bubbling = error_bubbling
|
955
957
|
schema_defn.max_depth = max_depth
|
@@ -1118,14 +1120,15 @@ module GraphQL
|
|
1118
1120
|
type.possible_types(context: context)
|
1119
1121
|
else
|
1120
1122
|
stored_possible_types = own_possible_types[type.graphql_name]
|
1121
|
-
visible_possible_types = stored_possible_types
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1123
|
+
visible_possible_types = if stored_possible_types && type.kind.interface?
|
1124
|
+
stored_possible_types.select do |possible_type|
|
1125
|
+
# Use `.graphql_name` comparison to match legacy vs class-based types.
|
1126
|
+
# When we don't need to support legacy `.define` types, use `.include?(type)` instead.
|
1127
|
+
possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
|
1128
|
+
end
|
1129
|
+
else
|
1130
|
+
stored_possible_types
|
1131
|
+
end
|
1129
1132
|
visible_possible_types ||
|
1130
1133
|
introspection_system.possible_types[type.graphql_name] ||
|
1131
1134
|
(
|
@@ -1272,6 +1275,31 @@ module GraphQL
|
|
1272
1275
|
end
|
1273
1276
|
end
|
1274
1277
|
|
1278
|
+
attr_writer :validate_timeout
|
1279
|
+
|
1280
|
+
def validate_timeout(new_validate_timeout = nil)
|
1281
|
+
if new_validate_timeout
|
1282
|
+
@validate_timeout = new_validate_timeout
|
1283
|
+
elsif defined?(@validate_timeout)
|
1284
|
+
@validate_timeout
|
1285
|
+
else
|
1286
|
+
find_inherited_value(:validate_timeout)
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
attr_writer :validate_max_errors
|
1291
|
+
|
1292
|
+
def validate_max_errors(new_validate_max_errors = nil)
|
1293
|
+
if new_validate_max_errors
|
1294
|
+
@validate_max_errors = new_validate_max_errors
|
1295
|
+
elsif defined?(@validate_max_errors)
|
1296
|
+
@validate_max_errors
|
1297
|
+
else
|
1298
|
+
find_inherited_value(:validate_max_errors)
|
1299
|
+
end
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
|
1275
1303
|
attr_writer :max_complexity
|
1276
1304
|
|
1277
1305
|
def max_complexity(max_complexity = nil)
|
@@ -193,25 +193,26 @@ module GraphQL
|
|
193
193
|
if node1.name != node2.name
|
194
194
|
errored_nodes = [node1.name, node2.name].sort.join(" or ")
|
195
195
|
msg = "Field '#{response_key}' has a field conflict: #{errored_nodes}?"
|
196
|
-
|
196
|
+
add_error(GraphQL::StaticValidation::FieldsWillMergeError.new(
|
197
197
|
msg,
|
198
198
|
nodes: [node1, node2],
|
199
199
|
path: [],
|
200
200
|
field_name: response_key,
|
201
201
|
conflicts: errored_nodes
|
202
|
-
)
|
202
|
+
))
|
203
203
|
end
|
204
204
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
205
|
+
if !same_arguments?(node1, node2)
|
206
|
+
args = [serialize_field_args(node1), serialize_field_args(node2)]
|
207
|
+
conflicts = args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
|
208
|
+
msg = "Field '#{response_key}' has an argument conflict: #{conflicts}?"
|
209
|
+
add_error(GraphQL::StaticValidation::FieldsWillMergeError.new(
|
209
210
|
msg,
|
210
211
|
nodes: [node1, node2],
|
211
212
|
path: [],
|
212
213
|
field_name: response_key,
|
213
|
-
conflicts:
|
214
|
-
)
|
214
|
+
conflicts: conflicts
|
215
|
+
))
|
215
216
|
end
|
216
217
|
end
|
217
218
|
|
@@ -326,20 +327,19 @@ module GraphQL
|
|
326
327
|
[fields, fragment_spreads]
|
327
328
|
end
|
328
329
|
|
329
|
-
def
|
330
|
+
def same_arguments?(field1, field2)
|
330
331
|
# Check for incompatible / non-identical arguments on this node:
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
end.uniq
|
332
|
+
arguments1 = field1.arguments
|
333
|
+
arguments2 = field2.arguments
|
334
|
+
|
335
|
+
return false if arguments1.length != arguments2.length
|
336
|
+
|
337
|
+
arguments1.all? do |argument1|
|
338
|
+
argument2 = arguments2.find { |argument| argument.name == argument1.name }
|
339
|
+
return false if argument2.nil?
|
340
|
+
|
341
|
+
serialize_arg(argument1.value) == serialize_arg(argument2.value)
|
342
|
+
end
|
343
343
|
end
|
344
344
|
|
345
345
|
def serialize_arg(arg_value)
|
@@ -353,6 +353,14 @@ module GraphQL
|
|
353
353
|
end
|
354
354
|
end
|
355
355
|
|
356
|
+
def serialize_field_args(field)
|
357
|
+
serialized_args = {}
|
358
|
+
field.arguments.each do |argument|
|
359
|
+
serialized_args[argument.name] = serialize_arg(argument.value)
|
360
|
+
end
|
361
|
+
serialized_args
|
362
|
+
end
|
363
|
+
|
356
364
|
def compared_fragments_key(frag1, frag2, exclusive)
|
357
365
|
# Cache key to not compare two fragments more than once.
|
358
366
|
# The key includes both fragment names sorted (this way we
|
@@ -7,12 +7,12 @@ module GraphQL
|
|
7
7
|
dependency_map = context.dependencies
|
8
8
|
dependency_map.cyclical_definitions.each do |defn|
|
9
9
|
if defn.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
10
|
-
|
10
|
+
add_error(GraphQL::StaticValidation::FragmentsAreFiniteError.new(
|
11
11
|
"Fragment #{defn.name} contains an infinite loop",
|
12
12
|
nodes: defn.node,
|
13
13
|
path: defn.path,
|
14
14
|
name: defn.name
|
15
|
-
)
|
15
|
+
))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module InputObjectNamesAreUnique
|
5
|
+
def on_input_object(node, parent)
|
6
|
+
validate_input_fields(node)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def validate_input_fields(node)
|
13
|
+
input_field_defns = node.arguments
|
14
|
+
input_fields_by_name = Hash.new { |h, k| h[k] = [] }
|
15
|
+
input_field_defns.each { |a| input_fields_by_name[a.name] << a }
|
16
|
+
|
17
|
+
input_fields_by_name.each do |name, defns|
|
18
|
+
if defns.size > 1
|
19
|
+
error = GraphQL::StaticValidation::InputObjectNamesAreUniqueError.new(
|
20
|
+
"There can be only one input field named \"#{name}\"",
|
21
|
+
nodes: defns,
|
22
|
+
name: name
|
23
|
+
)
|
24
|
+
add_error(error)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class InputObjectNamesAreUniqueError < StaticValidation::Error
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(message, path: nil, nodes: [], name:)
|
8
|
+
super(message, path: path, nodes: nodes)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash representation of this Message
|
13
|
+
def to_h
|
14
|
+
extensions = {
|
15
|
+
"code" => code,
|
16
|
+
"name" => name
|
17
|
+
}
|
18
|
+
|
19
|
+
super.merge({
|
20
|
+
"extensions" => extensions
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def code
|
25
|
+
"inputFieldNotUnique"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -19,10 +19,11 @@ module GraphQL
|
|
19
19
|
|
20
20
|
def_delegators :@query, :schema, :document, :fragments, :operations, :warden
|
21
21
|
|
22
|
-
def initialize(query, visitor_class)
|
22
|
+
def initialize(query, visitor_class, max_errors)
|
23
23
|
@query = query
|
24
24
|
@literal_validator = LiteralValidator.new(context: query.context)
|
25
25
|
@errors = []
|
26
|
+
@max_errors = max_errors || Float::INFINITY
|
26
27
|
@on_dependency_resolve_handlers = []
|
27
28
|
@visitor = visitor_class.new(document, self)
|
28
29
|
end
|
@@ -38,6 +39,10 @@ module GraphQL
|
|
38
39
|
def validate_literal(ast_value, type)
|
39
40
|
@literal_validator.validate(ast_value, type)
|
40
41
|
end
|
42
|
+
|
43
|
+
def too_many_errors?
|
44
|
+
@errors.length >= @max_errors
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ValidationTimeoutError < StaticValidation::Error
|
5
|
+
def initialize(message, path: nil, nodes: [])
|
6
|
+
super(message, path: path, nodes: nodes)
|
7
|
+
end
|
8
|
+
|
9
|
+
# A hash representation of this Message
|
10
|
+
def to_h
|
11
|
+
extensions = {
|
12
|
+
"code" => code
|
13
|
+
}
|
14
|
+
|
15
|
+
super.merge({
|
16
|
+
"extensions" => extensions
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def code
|
21
|
+
"validationTimeout"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -20,8 +20,11 @@ module GraphQL
|
|
20
20
|
|
21
21
|
# Validate `query` against the schema. Returns an array of message hashes.
|
22
22
|
# @param query [GraphQL::Query]
|
23
|
+
# @param validate [Boolean]
|
24
|
+
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
25
|
+
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
23
26
|
# @return [Array<Hash>]
|
24
|
-
def validate(query, validate: true)
|
27
|
+
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
25
28
|
query.trace("validate", { validate: validate, query: query }) do
|
26
29
|
can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
|
27
30
|
errors = if validate == false && can_skip_rewrite
|
@@ -30,23 +33,34 @@ module GraphQL
|
|
30
33
|
rules_to_use = validate ? @rules : []
|
31
34
|
visitor_class = BaseVisitor.including_rules(rules_to_use, rewrite: !can_skip_rewrite)
|
32
35
|
|
33
|
-
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
36
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
begin
|
39
|
+
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
40
|
+
# A timeout value of 0 or nil will execute the block without any timeout.
|
41
|
+
Timeout::timeout(timeout) do
|
42
|
+
|
43
|
+
catch(:too_many_validation_errors) do
|
44
|
+
# Attach legacy-style rules.
|
45
|
+
# Only loop through rules if it has legacy-style rules
|
46
|
+
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
47
|
+
legacy_rules.each do |rule_class_or_module|
|
48
|
+
if rule_class_or_module.method_defined?(:validate)
|
49
|
+
rule_class_or_module.new.validate(context)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context.visitor.visit
|
41
55
|
end
|
42
56
|
end
|
57
|
+
rescue Timeout::Error
|
58
|
+
handle_timeout(query, context)
|
43
59
|
end
|
44
60
|
|
45
|
-
context.visitor.visit
|
46
61
|
context.errors
|
47
62
|
end
|
48
63
|
|
49
|
-
|
50
64
|
irep = if errors.empty? && context
|
51
65
|
# Only return this if there are no errors and validation was actually run
|
52
66
|
context.visitor.rewrite_document
|
@@ -60,6 +74,15 @@ module GraphQL
|
|
60
74
|
}
|
61
75
|
end
|
62
76
|
end
|
77
|
+
|
78
|
+
# Invoked when static validation times out.
|
79
|
+
# @param query [GraphQL::Query]
|
80
|
+
# @param context [GraphQL::StaticValidation::ValidationContext]
|
81
|
+
def handle_timeout(query, context)
|
82
|
+
context.errors << GraphQL::StaticValidation::ValidationTimeoutError.new(
|
83
|
+
"Timeout on validation of query"
|
84
|
+
)
|
85
|
+
end
|
63
86
|
end
|
64
87
|
end
|
65
88
|
end
|
@@ -4,6 +4,7 @@ require "graphql/static_validation/definition_dependencies"
|
|
4
4
|
require "graphql/static_validation/type_stack"
|
5
5
|
require "graphql/static_validation/validator"
|
6
6
|
require "graphql/static_validation/validation_context"
|
7
|
+
require "graphql/static_validation/validation_timeout_error"
|
7
8
|
require "graphql/static_validation/literal_validator"
|
8
9
|
require "graphql/static_validation/base_visitor"
|
9
10
|
require "graphql/static_validation/no_validate_visitor"
|
@@ -16,7 +16,10 @@ module GraphQL
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def collect(object)
|
19
|
-
|
19
|
+
default_labels = { key: object['key'], platform_key: object['platform_key'] }
|
20
|
+
custom = object['custom_labels']
|
21
|
+
labels = custom.nil? ? default_labels : default_labels.merge(custom)
|
22
|
+
|
20
23
|
@graphql_gauge.observe object['duration'], labels
|
21
24
|
end
|
22
25
|
|
data/lib/graphql/types/int.rb
CHANGED
@@ -9,8 +9,15 @@ module GraphQL
|
|
9
9
|
MIN = -(2**31)
|
10
10
|
MAX = (2**31) - 1
|
11
11
|
|
12
|
-
def self.coerce_input(value,
|
13
|
-
value.is_a?(Integer)
|
12
|
+
def self.coerce_input(value, ctx)
|
13
|
+
return if !value.is_a?(Integer)
|
14
|
+
|
15
|
+
if value >= MIN && value <= MAX
|
16
|
+
value
|
17
|
+
else
|
18
|
+
err = GraphQL::IntegerDecodingError.new(value)
|
19
|
+
ctx.schema.type_error(err, ctx)
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
def self.coerce_result(value, ctx)
|
@@ -33,7 +33,8 @@ module GraphQL
|
|
33
33
|
if node_type
|
34
34
|
@node_type = node_type
|
35
35
|
# Add a default `node` field
|
36
|
-
field :node, node_type, null: null, description: "The item at the end of the edge."
|
36
|
+
field :node, node_type, null: null, description: "The item at the end of the edge.",
|
37
|
+
connection: false
|
37
38
|
end
|
38
39
|
@node_type
|
39
40
|
end
|
data/lib/graphql/types/string.rb
CHANGED
@@ -7,7 +7,13 @@ module GraphQL
|
|
7
7
|
|
8
8
|
def self.coerce_result(value, ctx)
|
9
9
|
str = value.to_s
|
10
|
-
str.encoding == Encoding::UTF_8
|
10
|
+
if str.encoding == Encoding::UTF_8
|
11
|
+
str
|
12
|
+
elsif str.frozen?
|
13
|
+
str.encode(Encoding::UTF_8)
|
14
|
+
else
|
15
|
+
str.encode!(Encoding::UTF_8)
|
16
|
+
end
|
11
17
|
rescue EncodingError
|
12
18
|
err = GraphQL::StringEncodingError.new(str)
|
13
19
|
ctx.schema.type_error(err, ctx)
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
22
22
|
@object = object
|
23
23
|
@type = type
|
24
24
|
@context = context
|
25
|
-
message ||= "An instance of #{object.class} failed #{type.
|
25
|
+
message ||= "An instance of #{object.class} failed #{type.graphql_name}'s authorization check"
|
26
26
|
super(message)
|
27
27
|
end
|
28
28
|
end
|
data/lib/graphql/version.rb
CHANGED