graphql 1.11.5 → 1.11.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/object_generator.rb +2 -0
  3. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  4. data/lib/graphql/execution/interpreter/arguments.rb +21 -6
  5. data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
  6. data/lib/graphql/execution/interpreter/runtime.rb +53 -39
  7. data/lib/graphql/integer_decoding_error.rb +17 -0
  8. data/lib/graphql/invalid_null_error.rb +1 -1
  9. data/lib/graphql/pagination/connections.rb +11 -5
  10. data/lib/graphql/query/context.rb +4 -1
  11. data/lib/graphql/query/validation_pipeline.rb +1 -1
  12. data/lib/graphql/query.rb +4 -1
  13. data/lib/graphql/relay/array_connection.rb +2 -2
  14. data/lib/graphql/relay/range_add.rb +14 -5
  15. data/lib/graphql/schema/build_from_definition.rb +11 -7
  16. data/lib/graphql/schema/default_type_error.rb +2 -0
  17. data/lib/graphql/schema/field/connection_extension.rb +8 -7
  18. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  19. data/lib/graphql/schema/field.rb +22 -12
  20. data/lib/graphql/schema/member/has_arguments.rb +51 -52
  21. data/lib/graphql/schema/member/has_fields.rb +2 -2
  22. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  23. data/lib/graphql/schema/unique_within_type.rb +1 -2
  24. data/lib/graphql/schema.rb +39 -11
  25. data/lib/graphql/static_validation/all_rules.rb +1 -0
  26. data/lib/graphql/static_validation/base_visitor.rb +3 -0
  27. data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
  28. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  29. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  30. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  31. data/lib/graphql/static_validation/validation_context.rb +6 -1
  32. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  33. data/lib/graphql/static_validation/validator.rb +33 -10
  34. data/lib/graphql/static_validation.rb +1 -0
  35. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  36. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  37. data/lib/graphql/types/int.rb +9 -2
  38. data/lib/graphql/types/relay/base_connection.rb +2 -1
  39. data/lib/graphql/types/relay/base_edge.rb +2 -1
  40. data/lib/graphql/types/string.rb +7 -1
  41. data/lib/graphql/unauthorized_error.rb +1 -1
  42. data/lib/graphql/version.rb +1 -1
  43. data/lib/graphql.rb +1 -0
  44. data/readme.md +1 -1
  45. 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
- maybe_lazies = []
94
- arg_lazies = arg_defns.map do |arg_name, arg_defn|
95
- arg_key = arg_defn.keyword
96
- has_value = false
97
- default_used = false
98
- if values.key?(arg_name)
99
- has_value = true
100
- value = values[arg_name]
101
- elsif values.key?(arg_key)
102
- has_value = true
103
- value = values[arg_key]
104
- elsif arg_defn.default_value?
105
- has_value = true
106
- value = arg_defn.default_value
107
- default_used = true
108
- end
109
-
110
- if has_value
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
- coerced_value = if loaded_value
123
- loaded_value
124
- else
125
- context.schema.error_handler.with_error_handling(context) do
126
- arg_defn.type.coerce_input(value, context)
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
- context.schema.after_lazy(coerced_value) do |coerced_value|
131
- prepared_value = context.schema.error_handler.with_error_handling(context) do
132
- arg_defn.prepare_value(parent_object, coerced_value, context: context)
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
- kwarg_arguments[arg_key] = prepared_value
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
- )
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
- maybe_lazies.concat(arg_lazies)
147
- context.schema.after_any_lazies(maybe_lazies) do
148
- GraphQL::Execution::Interpreter::Arguments.new(
149
- keyword_arguments: kwarg_arguments,
150
- argument_values: argument_values,
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
- # urlsafe_decode64 is for forward compatibility
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
@@ -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.select do |possible_type|
1122
- next true unless type.kind.interface?
1123
- next true unless possible_type.kind.object?
1124
-
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 if stored_possible_types
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)
@@ -34,6 +34,7 @@ module GraphQL
34
34
  GraphQL::StaticValidation::VariableUsagesAreAllowed,
35
35
  GraphQL::StaticValidation::MutationRootExists,
36
36
  GraphQL::StaticValidation::SubscriptionRootExists,
37
+ GraphQL::StaticValidation::InputObjectNamesAreUnique,
37
38
  ]
38
39
  end
39
40
  end
@@ -205,6 +205,9 @@ module GraphQL
205
205
  private
206
206
 
207
207
  def add_error(error, path: nil)
208
+ if @context.too_many_errors?
209
+ throw :too_many_validation_errors
210
+ end
208
211
  error.path ||= (path || @path.dup)
209
212
  context.errors << error
210
213
  end
@@ -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
- context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
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
- args = possible_arguments(node1, node2)
206
- if args.size > 1
207
- msg = "Field '#{response_key}' has an argument conflict: #{args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")}?"
208
- context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
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: args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
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 possible_arguments(field1, field2)
330
+ def same_arguments?(field1, field2)
330
331
  # Check for incompatible / non-identical arguments on this node:
331
- [field1, field2].map do |n|
332
- if n.arguments.any?
333
- serialized_args = {}
334
- n.arguments.each do |a|
335
- arg_value = a.value
336
- serialized_args[a.name] = serialize_arg(arg_value)
337
- end
338
- serialized_args
339
- else
340
- NO_ARGS
341
- end
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
- context.errors << GraphQL::StaticValidation::FragmentsAreFiniteError.new(
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
- # Attach legacy-style rules.
36
- # Only loop through rules if it has legacy-style rules
37
- unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
38
- legacy_rules.each do |rule_class_or_module|
39
- if rule_class_or_module.method_defined?(:validate)
40
- rule_class_or_module.new.validate(context)
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"
@@ -95,7 +95,7 @@ module GraphQL
95
95
  end
96
96
 
97
97
  def self.use(schema_defn, options = {})
98
- tracer = self.new(options)
98
+ tracer = self.new(**options)
99
99
  schema_defn.instrument(:field, tracer)
100
100
  schema_defn.tracer(tracer)
101
101
  end
@@ -16,7 +16,10 @@ module GraphQL
16
16
  end
17
17
 
18
18
  def collect(object)
19
- labels = { key: object['key'], platform_key: object['platform_key'] }
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
 
@@ -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, _ctx)
13
- value.is_a?(Integer) ? value : nil
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)
@@ -94,7 +94,8 @@ module GraphQL
94
94
  type = nullable ? [@node_type, null: true] : [@node_type]
95
95
  field :nodes, type,
96
96
  null: nullable,
97
- description: "A list of nodes."
97
+ description: "A list of nodes.",
98
+ connection: false
98
99
  end
99
100
  end
100
101
 
@@ -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
@@ -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 ? str : str.encode(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.name}'s authorization check"
25
+ message ||= "An instance of #{object.class} failed #{type.graphql_name}'s authorization check"
26
26
  super(message)
27
27
  end
28
28
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.11.5"
3
+ VERSION = "1.11.9"
4
4
  end