graphql 1.12.3 → 1.12.8

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +4 -1
  3. data/lib/generators/graphql/loader_generator.rb +1 -0
  4. data/lib/generators/graphql/mutation_generator.rb +1 -0
  5. data/lib/generators/graphql/relay.rb +55 -0
  6. data/lib/generators/graphql/relay_generator.rb +4 -46
  7. data/lib/generators/graphql/type_generator.rb +1 -0
  8. data/lib/graphql.rb +4 -2
  9. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  10. data/lib/graphql/backtrace/table.rb +0 -1
  11. data/lib/graphql/backtrace/traced_error.rb +0 -1
  12. data/lib/graphql/backtrace/tracer.rb +4 -8
  13. data/lib/graphql/dataloader.rb +102 -92
  14. data/lib/graphql/dataloader/null_dataloader.rb +5 -5
  15. data/lib/graphql/dataloader/request.rb +1 -6
  16. data/lib/graphql/dataloader/request_all.rb +1 -4
  17. data/lib/graphql/dataloader/source.rb +20 -6
  18. data/lib/graphql/execution/errors.rb +109 -11
  19. data/lib/graphql/execution/interpreter.rb +2 -2
  20. data/lib/graphql/execution/interpreter/arguments_cache.rb +37 -14
  21. data/lib/graphql/execution/interpreter/resolve.rb +33 -25
  22. data/lib/graphql/execution/interpreter/runtime.rb +41 -78
  23. data/lib/graphql/execution/multiplex.rb +21 -22
  24. data/lib/graphql/introspection.rb +1 -1
  25. data/lib/graphql/introspection/directive_type.rb +7 -3
  26. data/lib/graphql/language.rb +1 -0
  27. data/lib/graphql/language/cache.rb +37 -0
  28. data/lib/graphql/language/parser.rb +15 -5
  29. data/lib/graphql/language/parser.y +15 -5
  30. data/lib/graphql/object_type.rb +0 -2
  31. data/lib/graphql/pagination/active_record_relation_connection.rb +7 -0
  32. data/lib/graphql/pagination/connection.rb +15 -1
  33. data/lib/graphql/pagination/connections.rb +1 -0
  34. data/lib/graphql/pagination/relation_connection.rb +12 -1
  35. data/lib/graphql/parse_error.rb +0 -1
  36. data/lib/graphql/query.rb +9 -5
  37. data/lib/graphql/query/arguments_cache.rb +0 -1
  38. data/lib/graphql/query/context.rb +1 -3
  39. data/lib/graphql/query/executor.rb +0 -1
  40. data/lib/graphql/query/null_context.rb +3 -2
  41. data/lib/graphql/query/validation_pipeline.rb +1 -1
  42. data/lib/graphql/query/variable_validation_error.rb +1 -1
  43. data/lib/graphql/railtie.rb +9 -1
  44. data/lib/graphql/relay/range_add.rb +10 -5
  45. data/lib/graphql/schema.rb +14 -27
  46. data/lib/graphql/schema/argument.rb +61 -0
  47. data/lib/graphql/schema/field.rb +10 -5
  48. data/lib/graphql/schema/field/connection_extension.rb +1 -0
  49. data/lib/graphql/schema/find_inherited_value.rb +3 -1
  50. data/lib/graphql/schema/input_object.rb +6 -2
  51. data/lib/graphql/schema/member/has_arguments.rb +43 -56
  52. data/lib/graphql/schema/member/has_fields.rb +1 -4
  53. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  54. data/lib/graphql/schema/resolver.rb +28 -1
  55. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +3 -1
  56. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
  57. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
  58. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  59. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  60. data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
  61. data/lib/graphql/subscriptions/event.rb +0 -1
  62. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  63. data/lib/graphql/subscriptions/serialize.rb +3 -1
  64. data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
  65. data/lib/graphql/types/relay/base_connection.rb +4 -0
  66. data/lib/graphql/types/relay/connection_behaviors.rb +38 -5
  67. data/lib/graphql/types/relay/edge_behaviors.rb +12 -1
  68. data/lib/graphql/version.rb +1 -1
  69. data/readme.md +1 -1
  70. metadata +8 -90
@@ -84,6 +84,7 @@ module GraphQL
84
84
  after: arguments[:after],
85
85
  last: arguments[:last],
86
86
  before: arguments[:before],
87
+ arguments: arguments,
87
88
  edge_class: edge_class_for_field(field),
88
89
  )
89
90
  end
@@ -32,7 +32,11 @@ module GraphQL
32
32
  @has_next_page = if before_offset && before_offset > 0
33
33
  true
34
34
  elsif first
35
- relation_count(set_limit(sliced_nodes, first + 1)) == first + 1
35
+ if @nodes && @nodes.count < first
36
+ false
37
+ else
38
+ relation_larger_than(sliced_nodes, first)
39
+ end
36
40
  else
37
41
  false
38
42
  end
@@ -49,6 +53,13 @@ module GraphQL
49
53
 
50
54
  private
51
55
 
56
+ # @param relation [Object] A database query object
57
+ # @param size [Integer] The value against which we check the relation size
58
+ # @return [Boolean] True if the number of items in this relation is larger than `size`
59
+ def relation_larger_than(relation, size)
60
+ relation_count(set_limit(relation, size + 1)) == size + 1
61
+ end
62
+
52
63
  # @param relation [Object] A database query object
53
64
  # @return [Integer, nil] The offset value, or nil if there isn't one
54
65
  def relation_offset(relation)
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: language/parser.rb
3
2
  module GraphQL
4
3
  class ParseError < GraphQL::Error
5
4
  attr_reader :line, :col, :query
data/lib/graphql/query.rb CHANGED
@@ -195,9 +195,7 @@ module GraphQL
195
195
  # @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
196
196
  def result
197
197
  if !@executed
198
- with_prepared_ast {
199
- Execution::Multiplex.run_queries(@schema, [self], context: @context)
200
- }
198
+ Execution::Multiplex.run_queries(@schema, [self], context: @context)
201
199
  end
202
200
  @result ||= Query::Result.new(query: self, values: @result_values)
203
201
  end
@@ -251,12 +249,18 @@ module GraphQL
251
249
  # @param parent_object [GraphQL::Schema::Object]
252
250
  # @return Hash{Symbol => Object}
253
251
  def arguments_for(ast_node, definition, parent_object: nil)
252
+ if interpreter?
253
+ arguments_cache.fetch(ast_node, definition, parent_object)
254
+ else
255
+ arguments_cache[ast_node][definition]
256
+ end
257
+ end
258
+
259
+ def arguments_cache
254
260
  if interpreter?
255
261
  @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
256
- @arguments_cache.fetch(ast_node, definition, parent_object)
257
262
  else
258
263
  @arguments_cache ||= ArgumentsCache.build(self)
259
- @arguments_cache[ast_node][definition]
260
264
  end
261
265
  end
262
266
 
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../query.rb
3
2
  module GraphQL
4
3
  class Query
5
4
  module ArgumentsCache
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../execution/execute.rb
3
- # test_via: ../execution/lazy.rb
4
2
  module GraphQL
5
3
  class Query
6
4
  # Expose some query-specific info to field resolve functions.
@@ -159,7 +157,7 @@ module GraphQL
159
157
  end
160
158
 
161
159
  def dataloader
162
- @dataloader ||= query.multiplex.dataloader
160
+ @dataloader ||= query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new
163
161
  end
164
162
 
165
163
  # @api private
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../query.rb
3
2
  module GraphQL
4
3
  class Query
5
4
  class Executor
@@ -9,10 +9,11 @@ module GraphQL
9
9
  def visible_type?(t); true; end
10
10
  end
11
11
 
12
- attr_reader :schema, :query, :warden
12
+ attr_reader :schema, :query, :warden, :dataloader
13
13
 
14
14
  def initialize
15
15
  @query = nil
16
+ @dataloader = GraphQL::Dataloader::NullDataloader.new
16
17
  @schema = GraphQL::Schema.new
17
18
  @warden = NullWarden.new(
18
19
  GraphQL::Filter.new,
@@ -36,7 +37,7 @@ module GraphQL
36
37
  @instance = self.new
37
38
  end
38
39
 
39
- def_delegators :instance, :query, :schema, :warden, :interpreter?
40
+ def_delegators :instance, :query, :schema, :warden, :interpreter?, :dataloader
40
41
  end
41
42
  end
42
43
  end
@@ -36,7 +36,7 @@ module GraphQL
36
36
  @valid
37
37
  end
38
38
 
39
- # @return [Array<GraphQL::StaticValidation::Error >] Static validation errors for the query string
39
+ # @return [Array<GraphQL::StaticValidation::Error, GraphQL::Query::VariableValidationError>] Static validation errors for the query string
40
40
  def validation_errors
41
41
  ensure_has_validated
42
42
  @validation_errors
@@ -23,7 +23,7 @@ module GraphQL
23
23
  # a one level deep merge explicitly. However beyond that only show the
24
24
  # latest value and problems.
25
25
  super.merge({ "extensions" => { "value" => value, "problems" => validation_result.problems }}) do |key, oldValue, newValue|
26
- if oldValue.respond_to? merge
26
+ if oldValue.respond_to?(:merge)
27
27
  oldValue.merge(newValue)
28
28
  else
29
29
  newValue
@@ -1,8 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module GraphQL
5
4
  class Railtie < Rails::Railtie
5
+ config.before_configuration do
6
+ # Bootsnap compile cache has similar expiration properties,
7
+ # so we assume that if the user has bootsnap setup it's ok
8
+ # to piggy back on it.
9
+ if ::Object.const_defined?("Bootsnap::CompileCache::ISeq") && Bootsnap::CompileCache::ISeq.cache_dir
10
+ Language::Parser.cache ||= Language::Cache.new(Pathname.new(Bootsnap::CompileCache::ISeq.cache_dir).join('graphql'))
11
+ end
12
+ end
13
+
6
14
  rake_tasks do
7
15
  # Defer this so that you only need the `parser` gem when you _run_ the upgrader
8
16
  def load_upgraders
@@ -9,7 +9,7 @@ module GraphQL
9
9
  # should be ordered and paginated before providing it here.
10
10
  #
11
11
  # @example Adding a comment to list of comments
12
- # post = Post.find(args[:postId])
12
+ # post = Post.find(args[:post_id])
13
13
  # comments = post.comments
14
14
  # new_comment = comments.build(body: args[:body])
15
15
  # new_comment.save!
@@ -18,13 +18,13 @@ module GraphQL
18
18
  # parent: post,
19
19
  # collection: comments,
20
20
  # item: new_comment,
21
- # context: ctx,
21
+ # context: context,
22
22
  # )
23
23
  #
24
24
  # response = {
25
25
  # post: post,
26
- # commentsConnection: range_add.connection,
27
- # newCommentEdge: range_add.edge,
26
+ # comments_connection: range_add.connection,
27
+ # new_comment_edge: range_add.edge,
28
28
  # }
29
29
  class RangeAdd
30
30
  attr_reader :edge, :connection, :parent
@@ -39,7 +39,12 @@ module GraphQL
39
39
  conn_class = context.schema.connections.wrapper_for(collection)
40
40
  # The rest will be added by ConnectionExtension
41
41
  @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
42
- @edge = @connection.edge_class.new(item, @connection)
42
+ # Check if this connection supports it, to support old versions of GraphQL-Pro
43
+ @edge = if @connection.respond_to?(:range_add_edge)
44
+ @connection.range_add_edge(item)
45
+ else
46
+ @connection.edge_class.new(item, @connection)
47
+ end
43
48
  else
44
49
  connection_class = BaseConnection.connection_for_nodes(collection)
45
50
  @connection = connection_class.new(collection, {}, parent: parent, context: context)
@@ -711,7 +711,7 @@ module GraphQL
711
711
  alias :_schema_class :class
712
712
  def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
713
713
  def_delegators :_schema_class, :directive
714
- def_delegators :_schema_class, :error_handler, :rescues
714
+ def_delegators :_schema_class, :error_handler
715
715
 
716
716
 
717
717
  # Given this schema member, find the class-based definition object
@@ -989,7 +989,7 @@ module GraphQL
989
989
  schema_defn.lazy_methods.set(lazy_class, value_method)
990
990
  end
991
991
 
992
- rescues.each do |err_class, handler|
992
+ error_handler.each_rescue do |err_class, handler|
993
993
  schema_defn.rescue_from(err_class, &handler)
994
994
  end
995
995
 
@@ -1118,14 +1118,15 @@ module GraphQL
1118
1118
  type.possible_types(context: context)
1119
1119
  else
1120
1120
  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
1121
+ visible_possible_types = if stored_possible_types && type.kind.interface?
1122
+ stored_possible_types.select do |possible_type|
1123
+ # Use `.graphql_name` comparison to match legacy vs class-based types.
1124
+ # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1125
+ possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
1126
+ end
1127
+ else
1128
+ stored_possible_types
1129
+ end
1129
1130
  visible_possible_types ||
1130
1131
  introspection_system.possible_types[type.graphql_name] ||
1131
1132
  (
@@ -1423,7 +1424,7 @@ module GraphQL
1423
1424
 
1424
1425
  def rescue_from(*err_classes, &handler_block)
1425
1426
  err_classes.each do |err_class|
1426
- own_rescues[err_class] = handler_block
1427
+ error_handler.rescue_from(err_class, handler_block)
1427
1428
  end
1428
1429
  end
1429
1430
 
@@ -1467,10 +1468,6 @@ module GraphQL
1467
1468
  super
1468
1469
  end
1469
1470
 
1470
- def rescues
1471
- find_inherited_value(:rescues, EMPTY_HASH).merge(own_rescues)
1472
- end
1473
-
1474
1471
  def object_from_id(node_id, ctx)
1475
1472
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
1476
1473
  end
@@ -1547,15 +1544,10 @@ module GraphQL
1547
1544
  def parse_error(parse_err, ctx)
1548
1545
  ctx.errors.push(parse_err)
1549
1546
  end
1550
- attr_writer :error_handler
1551
1547
 
1552
- # @return [GraphQL::Execution::Errors, Class<GraphQL::Execution::Errors::NullErrorHandler>]
1548
+ # @return [GraphQL::Execution::Errors]
1553
1549
  def error_handler
1554
- if defined?(@error_handler)
1555
- @error_handler
1556
- else
1557
- find_inherited_value(:error_handler, GraphQL::Execution::Errors::NullErrorHandler)
1558
- end
1550
+ @error_handler ||= GraphQL::Execution::Errors.new(self)
1559
1551
  end
1560
1552
 
1561
1553
  def lazy_resolve(lazy_class, value_method)
@@ -1743,10 +1735,6 @@ module GraphQL
1743
1735
  @own_plugins ||= []
1744
1736
  end
1745
1737
 
1746
- def own_rescues
1747
- @own_rescues ||= {}
1748
- end
1749
-
1750
1738
  def own_orphan_types
1751
1739
  @own_orphan_types ||= []
1752
1740
  end
@@ -1989,7 +1977,6 @@ module GraphQL
1989
1977
  end
1990
1978
 
1991
1979
  # Install these here so that subclasses will also install it.
1992
- use(GraphQL::Execution::Errors)
1993
1980
  use(GraphQL::Pagination::Connections)
1994
1981
 
1995
1982
  protected
@@ -236,6 +236,67 @@ module GraphQL
236
236
  end
237
237
  end
238
238
 
239
+ # @api private
240
+ def coerce_into_values(parent_object, values, context, argument_values)
241
+ arg_name = graphql_name
242
+ arg_key = keyword
243
+ has_value = false
244
+ default_used = false
245
+ if values.key?(arg_name)
246
+ has_value = true
247
+ value = values[arg_name]
248
+ elsif values.key?(arg_key)
249
+ has_value = true
250
+ value = values[arg_key]
251
+ elsif default_value?
252
+ has_value = true
253
+ value = default_value
254
+ default_used = true
255
+ end
256
+
257
+ if has_value
258
+ loaded_value = nil
259
+ coerced_value = context.schema.error_handler.with_error_handling(context) do
260
+ type.coerce_input(value, context)
261
+ end
262
+
263
+ # TODO this should probably be inside after_lazy
264
+ if loads && !from_resolver?
265
+ loaded_value = if type.list?
266
+ loaded_values = coerced_value.map { |val| owner.load_application_object(self, loads, val, context) }
267
+ context.schema.after_any_lazies(loaded_values) { |result| result }
268
+ else
269
+ owner.load_application_object(self, loads, coerced_value, context)
270
+ end
271
+ end
272
+
273
+ coerced_value = if loaded_value
274
+ loaded_value
275
+ else
276
+ coerced_value
277
+ end
278
+
279
+ # If this isn't lazy, then the block returns eagerly and assigns the result here
280
+ # If it _is_ lazy, then we write the lazy to the hash, then update it later
281
+ argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |coerced_value|
282
+ owner.validate_directive_argument(self, coerced_value)
283
+ prepared_value = context.schema.error_handler.with_error_handling(context) do
284
+ prepare_value(parent_object, coerced_value, context: context)
285
+ end
286
+
287
+ # TODO code smell to access such a deeply-nested constant in a distant module
288
+ argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
289
+ value: prepared_value,
290
+ definition: self,
291
+ default_used: default_used,
292
+ )
293
+ end
294
+ else
295
+ # has_value is false
296
+ owner.validate_directive_argument(self, nil)
297
+ end
298
+ end
299
+
239
300
  private
240
301
 
241
302
  def validate_input_type(input_type)
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../object.rb
3
2
  require "graphql/schema/field/connection_extension"
4
3
  require "graphql/schema/field/scope_extension"
5
4
 
@@ -61,6 +60,10 @@ module GraphQL
61
60
  @introspection
62
61
  end
63
62
 
63
+ def inspect
64
+ "#<#{self.class} #{path}#{arguments.any? ? "(...)" : ""}: #{type.to_type_signature}>"
65
+ end
66
+
64
67
  alias :mutation :resolver
65
68
 
66
69
  # @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
@@ -285,22 +288,24 @@ module GraphQL
285
288
  @owner = owner
286
289
  @subscription_scope = subscription_scope
287
290
 
288
- # Do this last so we have as much context as possible when initializing them:
289
291
  @extensions = EMPTY_ARRAY
290
- if extensions.any?
291
- self.extensions(extensions)
292
- end
293
292
  # This should run before connection extension,
294
293
  # but should it run after the definition block?
295
294
  if scoped?
296
295
  self.extension(ScopeExtension)
297
296
  end
297
+
298
298
  # The problem with putting this after the definition_block
299
299
  # is that it would override arguments
300
300
  if connection? && connection_extension
301
301
  self.extension(connection_extension)
302
302
  end
303
303
 
304
+ # Do this last so we have as much context as possible when initializing them:
305
+ if extensions.any?
306
+ self.extensions(extensions)
307
+ end
308
+
304
309
  if directives.any?
305
310
  directives.each do |(dir_class, options)|
306
311
  self.directive(dir_class, **options)
@@ -42,6 +42,7 @@ module GraphQL
42
42
  value.after_value ||= original_arguments[:after]
43
43
  value.last_value ||= original_arguments[:last]
44
44
  value.before_value ||= original_arguments[:before]
45
+ value.arguments ||= original_arguments
45
46
  value.field ||= field
46
47
  if field.has_max_page_size? && !value.has_max_page_size_override?
47
48
  value.max_page_size = field.max_page_size
@@ -20,7 +20,9 @@ module GraphQL
20
20
  if self.is_a?(Class)
21
21
  superclass.respond_to?(method_name, true) ? superclass.send(method_name) : default_value
22
22
  else
23
- ancestors[1..-1].each do |ancestor|
23
+ ancestors_except_self = ancestors
24
+ ancestors_except_self.delete(self)
25
+ ancestors_except_self.each do |ancestor|
24
26
  if ancestor.respond_to?(method_name, true)
25
27
  return ancestor.send(method_name)
26
28
  end
@@ -214,8 +214,12 @@ module GraphQL
214
214
  arguments = coerce_arguments(nil, value, ctx)
215
215
 
216
216
  ctx.schema.after_lazy(arguments) do |resolved_arguments|
217
- input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
218
- input_obj_instance.prepare
217
+ if resolved_arguments.is_a?(GraphQL::Error)
218
+ raise resolved_arguments
219
+ else
220
+ input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
221
+ input_obj_instance.prepare
222
+ end
219
223
  end
220
224
  end
221
225