graphql 1.12.16 → 1.12.17

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03b30407d3081dad5d25f3a3f9a9e2781cd34db838688816b01c231aaa5a6dc5
4
- data.tar.gz: a06092995f8e3ea0ed2c75485124b0123d41a23d77eedd773e15a1d00fad80ab
3
+ metadata.gz: 6c9089e4578454f473996553a4771e4086e51b2b2db17fc31a579ab28019bd39
4
+ data.tar.gz: f45a70c81394f35c86d1610491f106e39899bc2c927a6a6d13e9e6533bc3bc85
5
5
  SHA512:
6
- metadata.gz: 3af391b8f7394985a42595af2d7b3735a6666310a4583ffd2a9ddce46cf868a6a8d7f2a9cb28ba6a1b85fb70cce8d7425074df847f8b113420e59599bd0379b5
7
- data.tar.gz: ca7370709ff588ba437a2f5b277482f2a00543b057ee96e164198ea44f81ad1e470fafe4c7eb89a9e1e64c0aeeddcc128983a67458040c855115516c09adcfd0
6
+ metadata.gz: b7231b5e00a336439d15d469a383cd7e211e305961d4a4b431e8a97f5e483819bbd87de3d7cfffc9b6677b8175f700e012cd9ee4a0c92fa47005455a14893d3c
7
+ data.tar.gz: 98495c2b24d65ef90d7f5e1036956349ea79d03ea192de6ed06e517657f49b4e0e9c748c8a28d54aac24e96b42f52eea5d4cbfbca801a4389b1515804296831a
@@ -89,6 +89,24 @@ module GraphQL
89
89
  nil
90
90
  end
91
91
 
92
+ # These arguments are given to `dataloader.with(source_class, ...)`. The object
93
+ # returned from this method is used to de-duplicate batch loads under the hood
94
+ # by using it as a Hash key.
95
+ #
96
+ # By default, the arguments are all put in an Array. To customize how this source's
97
+ # batches are merged, override this method to return something else.
98
+ #
99
+ # For example, if you pass `ActiveRecord::Relation`s to `.with(...)`, you could override
100
+ # this method to call `.to_sql` on them, thus merging `.load(...)` calls when they apply
101
+ # to equivalent relations.
102
+ #
103
+ # @param batch_args [Array<Object>]
104
+ # @param batch_kwargs [Hash]
105
+ # @return [Object]
106
+ def self.batch_key_for(*batch_args, **batch_kwargs)
107
+ [*batch_args, **batch_kwargs]
108
+ end
109
+
92
110
  private
93
111
 
94
112
  # Reads and returns the result for the key from the internal cache, or raises an error if the result was an error
@@ -27,18 +27,20 @@ module GraphQL
27
27
  schema.dataloader_class = self
28
28
  end
29
29
 
30
- def initialize
31
- @source_cache = Hash.new { |h, source_class| h[source_class] = Hash.new { |h2, batch_parameters|
32
- source = if RUBY_VERSION < "3"
33
- source_class.new(*batch_parameters)
34
- else
35
- batch_args, batch_kwargs = batch_parameters
36
- source_class.new(*batch_args, **batch_kwargs)
37
- end
38
- source.setup(self)
39
- h2[batch_parameters] = source
40
- }
30
+ # Call the block with a Dataloader instance,
31
+ # then run all enqueued jobs and return the result of the block.
32
+ def self.with_dataloading(&block)
33
+ dataloader = self.new
34
+ result = nil
35
+ dataloader.append_job {
36
+ result = block.call(dataloader)
41
37
  }
38
+ dataloader.run
39
+ result
40
+ end
41
+
42
+ def initialize
43
+ @source_cache = Hash.new { |h, k| h[k] = {} }
42
44
  @pending_jobs = []
43
45
  end
44
46
 
@@ -49,16 +51,24 @@ module GraphQL
49
51
  # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
50
52
  # and cached for the lifetime of this {Multiplex}.
51
53
  if RUBY_VERSION < "3"
52
- def with(source_class, *batch_parameters)
53
- @source_cache[source_class][batch_parameters]
54
+ def with(source_class, *batch_args)
55
+ batch_key = source_class.batch_key_for(*batch_args)
56
+ @source_cache[source_class][batch_key] ||= begin
57
+ source = source_class.new(*batch_args)
58
+ source.setup(self)
59
+ source
60
+ end
54
61
  end
55
62
  else
56
63
  def with(source_class, *batch_args, **batch_kwargs)
57
- batch_parameters = [batch_args, batch_kwargs]
58
- @source_cache[source_class][batch_parameters]
64
+ batch_key = source_class.batch_key_for(*batch_args, **batch_kwargs)
65
+ @source_cache[source_class][batch_key] ||= begin
66
+ source = source_class.new(*batch_args, **batch_kwargs)
67
+ source.setup(self)
68
+ source
69
+ end
59
70
  end
60
71
  end
61
-
62
72
  # Tell the dataloader that this fiber is waiting for data.
63
73
  #
64
74
  # Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
@@ -76,7 +76,7 @@ ERR
76
76
  # Apply definition from `define(...)` kwargs
77
77
  defn.define_keywords.each do |keyword, value|
78
78
  # Don't splat string hashes, which blows up on Rubies before 2.7
79
- if value.is_a?(Hash) && value.each_key.all? { |k| k.is_a?(Symbol) }
79
+ if value.is_a?(Hash) && !value.empty? && value.each_key.all? { |k| k.is_a?(Symbol) }
80
80
  defn_proxy.public_send(keyword, **value)
81
81
  else
82
82
  defn_proxy.public_send(keyword, value)
@@ -236,6 +236,7 @@ module GraphQL
236
236
  selections,
237
237
  selection_response,
238
238
  final_response,
239
+ nil,
239
240
  )
240
241
  end
241
242
  }
@@ -347,7 +348,7 @@ module GraphQL
347
348
  NO_ARGS = {}.freeze
348
349
 
349
350
  # @return [void]
350
- def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result) # rubocop:disable Metrics/ParameterLists
351
+ def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
351
352
  set_all_interpreter_context(owner_object, nil, nil, path)
352
353
 
353
354
  finished_jobs = 0
@@ -355,7 +356,7 @@ module GraphQL
355
356
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
356
357
  @dataloader.append_job {
357
358
  evaluate_selection(
358
- path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result
359
+ path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result, parent_object
359
360
  )
360
361
  finished_jobs += 1
361
362
  if target_result && finished_jobs == enqueued_jobs
@@ -370,7 +371,7 @@ module GraphQL
370
371
  attr_reader :progress_path
371
372
 
372
373
  # @return [void]
373
- def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result) # rubocop:disable Metrics/ParameterLists
374
+ def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
374
375
  return if dead_result?(selections_result)
375
376
  # As a performance optimization, the hash key will be a `Node` if
376
377
  # there's only one selection of the field. But if there are multiple
@@ -421,16 +422,16 @@ module GraphQL
421
422
  total_args_count = field_defn.arguments.size
422
423
  if total_args_count == 0
423
424
  kwarg_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
424
- evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result)
425
+ evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
425
426
  else
426
427
  # TODO remove all arguments(...) usages?
427
428
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
428
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result)
429
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
430
  end
430
431
  end
431
432
  end
432
433
 
433
- def evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
434
+ def evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
434
435
  context.scoped_context = scoped_context
435
436
  return_type = field_defn.type
436
437
  after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
@@ -472,6 +473,8 @@ module GraphQL
472
473
  # This is used by `__typename` in order to support the legacy runtime,
473
474
  # but it has no use here (and it's always `nil`).
474
475
  # Stop adding it here to avoid the overhead of `.merge_extras` below.
476
+ when :parent
477
+ extra_args[:parent] = parent_object
475
478
  else
476
479
  extra_args[extra] = field_defn.fetch_extra(extra, context)
477
480
  end
@@ -733,6 +736,7 @@ module GraphQL
733
736
  selections,
734
737
  this_result,
735
738
  final_result,
739
+ owner_object.object,
736
740
  )
737
741
  this_result
738
742
  end
@@ -23,6 +23,12 @@ module GraphQL
23
23
  if value.nil?
24
24
  'null'
25
25
  else
26
+ if (@object.type.kind.list? || (@object.type.kind.non_null? && @object.type.of_type.kind.list?)) && !value.respond_to?(:map)
27
+ # This is a bit odd -- we expect the default value to be an application-style value, so we use coerce result below.
28
+ # But coerce_result doesn't wrap single-item lists, which are valid inputs to list types.
29
+ # So, apply that wrapper here if needed.
30
+ value = [value]
31
+ end
26
32
  coerced_default_value = @object.type.coerce_result(value, @context)
27
33
  serialize_default_value(coerced_default_value, @object.type)
28
34
  end
data/lib/graphql/query.rb CHANGED
@@ -117,6 +117,10 @@ module GraphQL
117
117
  raise ArgumentError, "Query should only be provided a query string or a document, not both."
118
118
  end
119
119
 
120
+ if @query_string && !@query_string.is_a?(String)
121
+ raise ArgumentError, "Query string argument should be a String, got #{@query_string.class.name} instead."
122
+ end
123
+
120
124
  # A two-layer cache of type resolution:
121
125
  # { abstract_type => { value => resolved_type } }
122
126
  @resolved_types_cache = Hash.new do |h1, k1|
@@ -151,7 +151,7 @@ module GraphQL
151
151
  input_obj_arg = input_obj_arg.type_class
152
152
  # TODO: this skips input objects whose values were alread replaced with application objects.
153
153
  # See: https://github.com/rmosolgo/graphql-ruby/issues/2633
154
- if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
154
+ if value.is_a?(InputObject) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
155
155
  return false
156
156
  end
157
157
  end
@@ -298,7 +298,21 @@ module GraphQL
298
298
  # @api private
299
299
  def validate_default_value
300
300
  coerced_default_value = begin
301
- type.coerce_isolated_result(default_value) unless default_value.nil?
301
+ # This is weird, but we should accept single-item default values for list-type arguments.
302
+ # If we used `coerce_isolated_input` below, it would do this for us, but it's not really
303
+ # the right thing here because we expect default values in application format (Ruby values)
304
+ # not GraphQL format (scalar values).
305
+ #
306
+ # But I don't think Schema::List#coerce_result should apply wrapping to single-item lists.
307
+ prepped_default_value = if default_value.nil?
308
+ nil
309
+ elsif (type.kind.list? || (type.kind.non_null? && type.of_type.list?)) && !default_value.respond_to?(:map)
310
+ [default_value]
311
+ else
312
+ default_value
313
+ end
314
+
315
+ type.coerce_isolated_result(prepped_default_value) unless prepped_default_value.nil?
302
316
  rescue GraphQL::Schema::Enum::UnresolvedValueError
303
317
  # It raises this, which is helpful at runtime, but not here...
304
318
  default_value
@@ -71,11 +71,11 @@ module GraphQL
71
71
  end
72
72
 
73
73
  def prepare
74
- if context
75
- context.schema.after_any_lazies(@maybe_lazies) do
76
- object = context[:current_object]
74
+ if @context
75
+ @context.schema.after_any_lazies(@maybe_lazies) do
76
+ object = @context[:current_object]
77
77
  # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
78
- Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
78
+ Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
79
79
  self
80
80
  end
81
81
  else
@@ -218,8 +218,10 @@ module GraphQL
218
218
  own_extras + (superclass.respond_to?(:extras) ? superclass.extras : [])
219
219
  end
220
220
 
221
- # Specifies whether or not the field is nullable. Defaults to `true`
222
- # TODO unify with {#type}
221
+ # If `true` (default), then the return type for this resolver will be nullable.
222
+ # If `false`, then the return type is non-null.
223
+ #
224
+ # @see #type which sets the return type of this field and accepts a `null:` option
223
225
  # @param allow_null [Boolean] Whether or not the response can be null
224
226
  def null(allow_null = nil)
225
227
  if !allow_null.nil?
@@ -58,11 +58,9 @@ module GraphQL
58
58
  end
59
59
  end
60
60
 
61
- # Default implementation returns the root object.
61
+ # The default implementation returns nothing on subscribe.
62
62
  # Override it to return an object or
63
- # `:no_response` to return nothing.
64
- #
65
- # The default is `:no_response`.
63
+ # `:no_response` to (explicitly) return nothing.
66
64
  def subscribe(args = {})
67
65
  :no_response
68
66
  end
@@ -24,12 +24,13 @@ module GraphQL
24
24
  # @param other_than [Integer]
25
25
  # @param odd [Boolean]
26
26
  # @param even [Boolean]
27
+ # @param within [Range]
27
28
  # @param message [String] used for all validation failures
28
29
  def initialize(
29
30
  greater_than: nil, greater_than_or_equal_to: nil,
30
31
  less_than: nil, less_than_or_equal_to: nil,
31
32
  equal_to: nil, other_than: nil,
32
- odd: nil, even: nil,
33
+ odd: nil, even: nil, within: nil,
33
34
  message: "%{validated} must be %{comparison} %{target}",
34
35
  **default_options
35
36
  )
@@ -42,6 +43,7 @@ module GraphQL
42
43
  @other_than = other_than
43
44
  @odd = odd
44
45
  @even = even
46
+ @within = within
45
47
  @message = message
46
48
  super(**default_options)
47
49
  end
@@ -63,6 +65,8 @@ module GraphQL
63
65
  (partial_format(@message, { comparison: "even", target: "" })).strip
64
66
  elsif @odd && !value.odd?
65
67
  (partial_format(@message, { comparison: "odd", target: "" })).strip
68
+ elsif @within && !@within.include?(value)
69
+ partial_format(@message, { comparison: "within", target: @within })
66
70
  end
67
71
  end
68
72
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.12.16"
3
+ VERSION = "1.12.17"
4
4
  end
data/readme.md CHANGED
@@ -44,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
44
44
 
45
45
  ## Getting Involved
46
46
 
47
- - __Say hi & ask questions__ in the [#ruby channel on Slack](https://graphql-slack.herokuapp.com/) or [on Twitter](https://twitter.com/rmosolgo)!
47
+ - __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9) or [on Twitter](https://twitter.com/rmosolgo)!
48
48
  - __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
49
49
  - __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.16
4
+ version: 1.12.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-31 00:00:00.000000000 Z
11
+ date: 2021-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips