graphql 2.5.20 → 2.5.21

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +3 -3
  3. data/lib/graphql/execution/multiplex.rb +1 -1
  4. data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
  5. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  6. data/lib/graphql/execution/{batching → next}/prepare_object_step.rb +26 -9
  7. data/lib/graphql/execution/{batching → next}/runner.rb +65 -28
  8. data/lib/graphql/execution/{batching → next}/selections_step.rb +1 -1
  9. data/lib/graphql/execution/{batching.rb → next.rb} +19 -12
  10. data/lib/graphql/execution.rb +1 -0
  11. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  12. data/lib/graphql/introspection/entry_points.rb +5 -1
  13. data/lib/graphql/pagination/connection.rb +2 -0
  14. data/lib/graphql/pagination/connections.rb +32 -0
  15. data/lib/graphql/schema/argument.rb +1 -0
  16. data/lib/graphql/schema/build_from_definition.rb +12 -25
  17. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  18. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  19. data/lib/graphql/schema/field.rb +52 -58
  20. data/lib/graphql/schema/field_extension.rb +33 -0
  21. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  22. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  23. data/lib/graphql/schema/member/has_dataloader.rb +8 -0
  24. data/lib/graphql/schema/member/has_fields.rb +5 -4
  25. data/lib/graphql/schema/member.rb +5 -0
  26. data/lib/graphql/schema/object.rb +1 -0
  27. data/lib/graphql/schema/resolver.rb +42 -0
  28. data/lib/graphql/types/relay/has_node_field.rb +10 -2
  29. data/lib/graphql/types/relay/has_nodes_field.rb +10 -2
  30. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  31. data/lib/graphql/version.rb +1 -1
  32. metadata +9 -8
  33. data/lib/graphql/execution/batching/field_compatibility.rb +0 -150
  34. data/lib/graphql/execution/batching/field_resolve_step.rb +0 -408
@@ -8,6 +8,7 @@ module GraphQL
8
8
  include GraphQL::Schema::Member::HasArguments
9
9
  include GraphQL::Schema::Member::HasArguments::FieldConfigured
10
10
  include GraphQL::Schema::Member::HasAstNode
11
+ include GraphQL::Schema::Member::HasAuthorization
11
12
  include GraphQL::Schema::Member::HasPath
12
13
  include GraphQL::Schema::Member::HasValidators
13
14
  extend GraphQL::Schema::FindInheritedValue
@@ -192,9 +193,10 @@ module GraphQL
192
193
  # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
193
194
  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
194
195
  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
195
- # @param resolve_static [Symbol, nil] Used by {Schema.execute_batching} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
196
- # @param resolve_batch [Symbol, nil] Used by {Schema.execute_batching} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
197
- # @param resolve_each [Symbol, nil] Used by {Schema.execute_batching} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
196
+ # @param resolve_static [Symbol, true, nil] Used by {Schema.execute_next} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
197
+ # @param resolve_batch [Symbol, true, nil] Used by {Schema.execute_next} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
198
+ # @param resolve_each [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
199
+ # @param resolve_legacy_instance_method [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Calls an instance method on the object type class.
198
200
  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
199
201
  # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
200
202
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
@@ -217,7 +219,7 @@ module GraphQL
217
219
  # @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
218
220
  # @param extras [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] Extra arguments to be injected into the resolver for this field
219
221
  # @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
220
- def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
222
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
221
223
  if name.nil?
222
224
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
223
225
  end
@@ -267,23 +269,29 @@ module GraphQL
267
269
  @resolver_method = (resolver_method || name_s).to_sym
268
270
 
269
271
  if resolve_static
270
- @batch_mode = :resolve_static
271
- @batch_mode_key = resolve_static == true ? @method_sym : resolve_static
272
+ @execution_next_mode = :resolve_static
273
+ @execution_next_mode_key = resolve_static == true ? @method_sym : resolve_static
272
274
  elsif resolve_batch
273
- @batch_mode = :resolve_batch
274
- @batch_mode_key = resolve_batch == true ? @method_sym : resolve_batch
275
+ @execution_next_mode = :resolve_batch
276
+ @execution_next_mode_key = resolve_batch == true ? @method_sym : resolve_batch
275
277
  elsif resolve_each
276
- @batch_mode = :resolve_each
277
- @batch_mode_key = resolve_each == true ? @method_sym : resolve_each
278
+ @execution_next_mode = :resolve_each
279
+ @execution_next_mode_key = resolve_each == true ? @method_sym : resolve_each
278
280
  elsif hash_key
279
- @batch_mode = :hash_key
280
- @batch_mode_key = hash_key
281
+ @execution_next_mode = :hash_key
282
+ @execution_next_mode_key = hash_key
281
283
  elsif dig
282
- @batch_mode = :dig
283
- @batch_mode_key = dig
284
+ @execution_next_mode = :dig
285
+ @execution_next_mode_key = dig
286
+ elsif resolver_class
287
+ @execution_next_mode = :resolver_class
288
+ @execution_next_mode_key = resolver_class
289
+ elsif resolve_legacy_instance_method
290
+ @execution_next_mode = :resolve_legacy_instance_method
291
+ @execution_next_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
284
292
  else
285
- @batch_mode = :direct_send
286
- @batch_mode_key = @method_sym
293
+ @execution_next_mode = :direct_send
294
+ @execution_next_mode_key = @method_sym
287
295
  end
288
296
 
289
297
  @complexity = complexity
@@ -356,49 +364,8 @@ module GraphQL
356
364
  end
357
365
  end
358
366
 
359
- # Called by {Execution::Batching} to resolve this field for each of `objects`
360
- # @param field_resolve_step [Execution::Batching::FieldResolveStep] an internal metadata object from execution code
361
- # @param objects [Array<Object>] Objects returned from previously-executed fields
362
- # @param context [GraphQL::Query::Context]
363
- # @param args_hash [Hash<Symbol => Object>] Ruby-style arguments for this field
364
- # @return [Array<Object>] One field result for each of `objects`; must have the same length as `objects`
365
- # @see #initialize Use `resolve_static:`, `resolve_batch:`, `resolve_each:`, `hash_key:`, or `method:`
366
367
  # @api private
367
- def resolve_batch(field_resolve_step, objects, context, args_hash)
368
- case @batch_mode
369
- when :resolve_batch
370
- if args_hash.empty?
371
- @owner.public_send(@batch_mode_key, objects, context)
372
- else
373
- @owner.public_send(@batch_mode_key, objects, context, **args_hash)
374
- end
375
- when :resolve_static
376
- result = if args_hash.empty?
377
- @owner.public_send(@batch_mode_key, context)
378
- else
379
- @owner.public_send(@batch_mode_key, context, **args_hash)
380
- end
381
- Array.new(objects.size, result)
382
- when :resolve_each
383
- if args_hash.empty?
384
- objects.map { |o| @owner.public_send(@batch_mode_key, o, context) }
385
- else
386
- objects.map { |o| @owner.public_send(@batch_mode_key, o, context, **args_hash) }
387
- end
388
- when :hash_key
389
- objects.map { |o| o[@batch_mode_key] }
390
- when :direct_send
391
- if args_hash.empty?
392
- objects.map { |o| o.public_send(@batch_mode_key) }
393
- else
394
- objects.map { |o| o.public_send(@batch_mode_key, **args_hash) }
395
- end
396
- when :dig
397
- objects.map { |o| o.dig(*@batch_mode_key) }
398
- else
399
- raise "Batching execution for #{path} not implemented; provide `resolve_static:`, `resolve_batch:`, `hash_key:`, `method:`, or use a compatibility plug-in"
400
- end
401
- end
368
+ attr_reader :execution_next_mode_key, :execution_next_mode
402
369
 
403
370
  # Calls the definition block, if one was given.
404
371
  # This is deferred so that references to the return type
@@ -954,6 +921,33 @@ ERR
954
921
  end
955
922
  end
956
923
 
924
+ public
925
+
926
+ def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
927
+ extension = @extensions[idx]
928
+ if extension
929
+ extension.resolve_next(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
930
+ if memo
931
+ memos = extended.memos ||= {}
932
+ memos[idx] = memo
933
+ end
934
+
935
+ if (extras = extension.added_extras)
936
+ ae = extended.added_extras ||= []
937
+ ae.concat(extras)
938
+ end
939
+
940
+ extended.object = extended_objs
941
+ extended.arguments = extended_args
942
+ run_next_extensions_before_resolve(extended_objs, extended_args, ctx, extended, idx: idx + 1, &block)
943
+ end
944
+ else
945
+ yield(objs, args)
946
+ end
947
+ end
948
+
949
+ private
950
+
957
951
  def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
958
952
  extension = @extensions[idx]
959
953
  if extension
@@ -134,6 +134,24 @@ module GraphQL
134
134
  yield(object, arguments, nil)
135
135
  end
136
136
 
137
+ # Called before batch-resolving {#field}. It should either:
138
+ #
139
+ # - `yield` values to continue execution; OR
140
+ # - return something else to shortcut field execution.
141
+ #
142
+ # Whatever this method returns will be used for execution.
143
+ #
144
+ # @param objects [Array<Object>] The objects the field is being resolved on
145
+ # @param arguments [Hash] Ruby keyword arguments for resolving this field
146
+ # @param context [Query::Context] the context for this query
147
+ # @yieldparam objects [Array<Object>] The objects to continue resolving the field on. Length must be the same as passed-in `objects:`
148
+ # @yieldparam arguments [Hash] The keyword arguments to continue resolving with
149
+ # @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
150
+ # @return [Array<Object>] The return value for this field, length matching passed-in `objects:`.
151
+ def resolve_next(objects:, arguments:, context:)
152
+ yield(objects, arguments, nil)
153
+ end
154
+
137
155
  # Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
138
156
  # but before the value was added to the GraphQL response.
139
157
  #
@@ -148,6 +166,21 @@ module GraphQL
148
166
  def after_resolve(object:, arguments:, context:, value:, memo:)
149
167
  value
150
168
  end
169
+
170
+ # Called after {#field} was batch-resolved, and after any lazy values (like `Promise`s) were synced,
171
+ # but before the value was added to the GraphQL response.
172
+ #
173
+ # Whatever this hook returns will be used as the return value.
174
+ #
175
+ # @param objects [Array<Object>] The objects the field is being resolved on
176
+ # @param arguments [Hash] Ruby keyword arguments for resolving this field
177
+ # @param context [Query::Context] the context for this query
178
+ # @param values [Array<Object>] Whatever the field returned, one for each of `objects`
179
+ # @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
180
+ # @return [Array<Object>] The return values for this field, length matching `objects:`.
181
+ def after_resolve_next(objects:, arguments:, context:, values:, memo:)
182
+ values
183
+ end
151
184
  end
152
185
  end
153
186
  end
@@ -130,7 +130,7 @@ module GraphQL
130
130
  true
131
131
  end
132
132
 
133
- def default_relay
133
+ def default_relay?
134
134
  false
135
135
  end
136
136
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Member
5
+ module HasAuthorization
6
+ def self.included(child_class)
7
+ child_class.include(InstanceConfigured)
8
+ end
9
+
10
+ def self.extended(child_class)
11
+ child_class.extend(ClassConfigured)
12
+ child_class.class_exec do
13
+ @authorizes = false
14
+ end
15
+ end
16
+
17
+ def authorized?(object, context)
18
+ true
19
+ end
20
+
21
+ module InstanceConfigured
22
+ def authorizes?(context)
23
+ raise RequiredImplementationMissingError, "#{self.class} must implement #authorizes?(context)"
24
+ end
25
+ end
26
+
27
+ module ClassConfigured
28
+ def authorizes?(context)
29
+ method(:authorized?).owner != GraphQL::Schema::Member::HasAuthorization
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -22,6 +22,14 @@ module GraphQL
22
22
 
23
23
  # A shortcut method for loading many keys from a source.
24
24
  # Identical to `dataloader.with(source_class, *source_args).load_all(load_keys)`
25
+ #
26
+ # @example
27
+ # field :score, Integer, resolve_batch: true
28
+ #
29
+ # def self.score(posts)
30
+ # dataload_all(PostScoreSource, posts.map(&:id))
31
+ # end
32
+ #
25
33
  # @param source_class [Class<GraphQL::Dataloader::Source>]
26
34
  # @param source_args [Array<Object>] Any extra parameters defined in `source_class`'s `initialize` method
27
35
  # @param load_keys [Array<Object>] The keys to look up using `def fetch`
@@ -19,10 +19,11 @@ module GraphQL
19
19
  # @option kwargs [Symbol] :method The method to call on the underlying object to resolve this field (defaults to `name`)
20
20
  # @option kwargs [String, Symbol] :hash_key The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
21
21
  # @option kwargs [Array<String, Symbol>] :dig The nested hash keys to lookup on the underlying hash to resolve this field using dig
22
- # @option kwargs [Symbol] :resolver_method The method on the type to call to resolve this field (defaults to `name`)
23
- # @option kwargs [Symbol] :resolve_static Used by {Schema.execute_batching} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
24
- # @option kwargs [Symbol] :resolve_batch Used by {Schema.execute_batching} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
25
- # @option kwargs [Symbol] :resolve_each Used by {Schema.execute_batching} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
22
+ # @option kwargs [Symbol, true] :resolver_method The method on the type to call to resolve this field (defaults to `name`)
23
+ # @option kwargs [Symbol, true] :resolve_static Used by {Schema.execute_next} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
24
+ # @option kwargs [Symbol, true] :resolve_batch Used by {Schema.execute_next} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
25
+ # @option kwargs [Symbol, true] :resolve_each Used by {Schema.execute_next} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
26
+ # @option kwargs [Symbol, true] :resolve_legacy_instance_method Used by {Schema.execute_next} to get a value value for each item. Calls an instance method on the object type class.
26
27
  # @option kwargs [Boolean] :connection `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
27
28
  # @option kwargs [Class] :connection_extension The extension to add, to implement connections. If `nil`, no extension is added.
28
29
  # @option kwargs [Integer, nil] :max_page_size For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
@@ -2,6 +2,7 @@
2
2
  require 'graphql/schema/member/base_dsl_methods'
3
3
  require 'graphql/schema/member/graphql_type_names'
4
4
  require 'graphql/schema/member/has_ast_node'
5
+ require 'graphql/schema/member/has_authorization'
5
6
  require 'graphql/schema/member/has_dataloader'
6
7
  require 'graphql/schema/member/has_directives'
7
8
  require 'graphql/schema/member/has_deprecation_reason'
@@ -31,6 +32,10 @@ module GraphQL
31
32
  extend HasPath
32
33
  extend HasAstNode
33
34
  extend HasDirectives
35
+
36
+ def self.authorizes?(_ctx)
37
+ false
38
+ end
34
39
  end
35
40
  end
36
41
  end
@@ -5,6 +5,7 @@ require "graphql/query/null_context"
5
5
  module GraphQL
6
6
  class Schema
7
7
  class Object < GraphQL::Schema::Member
8
+ extend GraphQL::Schema::Member::HasAuthorization
8
9
  extend GraphQL::Schema::Member::HasFields
9
10
  extend GraphQL::Schema::Member::HasInterfaces
10
11
  include Member::HasDataloader
@@ -23,6 +23,7 @@ module GraphQL
23
23
  # Really we only need description & comment from here, but:
24
24
  extend Schema::Member::BaseDSLMethods
25
25
  extend GraphQL::Schema::Member::HasArguments
26
+ extend GraphQL::Schema::Member::HasAuthorization
26
27
  extend GraphQL::Schema::Member::HasValidators
27
28
  include Schema::Member::HasPath
28
29
  extend Schema::Member::HasPath
@@ -45,6 +46,8 @@ module GraphQL
45
46
  @prepared_arguments = nil
46
47
  end
47
48
 
49
+ attr_accessor :exec_result, :exec_index, :field_resolve_step
50
+
48
51
  # @return [Object] The application object this field is being resolved on
49
52
  attr_accessor :object
50
53
 
@@ -56,6 +59,45 @@ module GraphQL
56
59
 
57
60
  attr_writer :prepared_arguments
58
61
 
62
+ def call
63
+ if self.class < Schema::HasSingleInputArgument
64
+ @prepared_arguments = @prepared_arguments[:input]
65
+ end
66
+ q = context.query
67
+ trace_objs = [object]
68
+ q.current_trace.begin_execute_field(field, @prepared_arguments, trace_objs, q)
69
+ is_authed, new_return_value = authorized?(**@prepared_arguments)
70
+
71
+ if (runner = @field_resolve_step.runner).resolves_lazies && runner.schema.lazy?(is_authed)
72
+ is_authed, new_return_value = runner.schema.sync_lazy(is_authed)
73
+ end
74
+
75
+ result = if is_authed
76
+ Schema::Validator.validate!(self.class.validators, object, context, @prepared_arguments, as: @field)
77
+ call_resolve(@prepared_arguments)
78
+ else
79
+ new_return_value
80
+ end
81
+ q = context.query
82
+ q.current_trace.end_execute_field(field, @prepared_arguments, trace_objs, q, [result])
83
+
84
+ exec_result[exec_index] = result
85
+ rescue RuntimeError => err
86
+ exec_result[exec_index] = err
87
+ rescue StandardError => stderr
88
+ exec_result[exec_index] = begin
89
+ context.query.handle_or_reraise(stderr)
90
+ rescue GraphQL::ExecutionError => ex_err
91
+ ex_err
92
+ end
93
+ ensure
94
+ field_pending_steps = field_resolve_step.pending_steps
95
+ field_pending_steps.delete(self)
96
+ if field_pending_steps.size == 0 && field_resolve_step.field_results
97
+ field_resolve_step.runner.add_step(field_resolve_step)
98
+ end
99
+ end
100
+
59
101
  def arguments
60
102
  @prepared_arguments || raise("Arguments have not been prepared yet, still waiting for #load_arguments to resolve. (Call `.arguments` later in the code.)")
61
103
  end
@@ -7,10 +7,17 @@ module GraphQL
7
7
  module HasNodeField
8
8
  def self.included(child_class)
9
9
  child_class.field(**field_options, &field_block)
10
+ child_class.extend(ExecutionMethods)
11
+ end
12
+
13
+ module ExecutionMethods
14
+ def get_relay_node(context, id:)
15
+ context.schema.object_from_id(id, context)
16
+ end
10
17
  end
11
18
 
12
19
  def get_relay_node(id:)
13
- context.schema.object_from_id(id, context)
20
+ self.class.get_relay_node(context, id: id)
14
21
  end
15
22
 
16
23
  class << self
@@ -21,7 +28,8 @@ module GraphQL
21
28
  null: true,
22
29
  description: "Fetches an object given its ID.",
23
30
  relay_node_field: true,
24
- resolver_method: :get_relay_node
31
+ resolver_method: :get_relay_node,
32
+ resolve_static: :get_relay_node,
25
33
  }
26
34
  end
27
35
 
@@ -7,10 +7,17 @@ module GraphQL
7
7
  module HasNodesField
8
8
  def self.included(child_class)
9
9
  child_class.field(**field_options, &field_block)
10
+ child_class.extend(ExecutionMethods)
11
+ end
12
+
13
+ module ExecutionMethods
14
+ def get_relay_nodes(context, ids:)
15
+ ids.map { |id| context.schema.object_from_id(id, context) }
16
+ end
10
17
  end
11
18
 
12
19
  def get_relay_nodes(ids:)
13
- ids.map { |id| context.schema.object_from_id(id, context) }
20
+ self.class.get_relay_nodes(context, ids: ids)
14
21
  end
15
22
 
16
23
  class << self
@@ -21,7 +28,8 @@ module GraphQL
21
28
  null: false,
22
29
  description: "Fetches a list of objects given a list of IDs.",
23
30
  relay_nodes_field: true,
24
- resolver_method: :get_relay_nodes
31
+ resolver_method: :get_relay_nodes,
32
+ resolve_static: :get_relay_nodes
25
33
  }
26
34
  end
27
35
 
@@ -6,12 +6,13 @@ module GraphQL
6
6
  module NodeBehaviors
7
7
  def self.included(child_module)
8
8
  child_module.extend(ClassMethods)
9
+ child_module.extend(ExecutionMethods)
9
10
  child_module.description("An object with an ID.")
10
- child_module.field(:id, ID, null: false, description: "ID of the object.", resolver_method: :default_global_id)
11
+ child_module.field(:id, ID, null: false, description: "ID of the object.", resolver_method: :default_global_id, resolve_each: :default_global_id)
11
12
  end
12
13
 
13
14
  def default_global_id
14
- context.schema.id_from_object(object, self.class, context)
15
+ self.class.default_global_id(object, context)
15
16
  end
16
17
 
17
18
  module ClassMethods
@@ -19,6 +20,16 @@ module GraphQL
19
20
  true
20
21
  end
21
22
  end
23
+
24
+ module ExecutionMethods
25
+ def default_global_id(object, context)
26
+ context.schema.id_from_object(object, self, context)
27
+ end
28
+
29
+ def included(child_class)
30
+ child_class.extend(ExecutionMethods)
31
+ end
32
+ end
22
33
  end
23
34
  end
24
35
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.5.20"
3
+ VERSION = "2.5.21"
4
4
  end
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.5.20
4
+ version: 2.5.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-02-23 00:00:00.000000000 Z
10
+ date: 2026-03-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -491,12 +491,6 @@ files:
491
491
  - lib/graphql/dig.rb
492
492
  - lib/graphql/duration_encoding_error.rb
493
493
  - lib/graphql/execution.rb
494
- - lib/graphql/execution/batching.rb
495
- - lib/graphql/execution/batching/field_compatibility.rb
496
- - lib/graphql/execution/batching/field_resolve_step.rb
497
- - lib/graphql/execution/batching/prepare_object_step.rb
498
- - lib/graphql/execution/batching/runner.rb
499
- - lib/graphql/execution/batching/selections_step.rb
500
494
  - lib/graphql/execution/directive_checks.rb
501
495
  - lib/graphql/execution/errors.rb
502
496
  - lib/graphql/execution/interpreter.rb
@@ -512,6 +506,12 @@ files:
512
506
  - lib/graphql/execution/lazy/lazy_method_map.rb
513
507
  - lib/graphql/execution/lookahead.rb
514
508
  - lib/graphql/execution/multiplex.rb
509
+ - lib/graphql/execution/next.rb
510
+ - lib/graphql/execution/next/field_resolve_step.rb
511
+ - lib/graphql/execution/next/load_argument_step.rb
512
+ - lib/graphql/execution/next/prepare_object_step.rb
513
+ - lib/graphql/execution/next/runner.rb
514
+ - lib/graphql/execution/next/selections_step.rb
515
515
  - lib/graphql/execution_error.rb
516
516
  - lib/graphql/integer_decoding_error.rb
517
517
  - lib/graphql/integer_encoding_error.rb
@@ -617,6 +617,7 @@ files:
617
617
  - lib/graphql/schema/member/graphql_type_names.rb
618
618
  - lib/graphql/schema/member/has_arguments.rb
619
619
  - lib/graphql/schema/member/has_ast_node.rb
620
+ - lib/graphql/schema/member/has_authorization.rb
620
621
  - lib/graphql/schema/member/has_dataloader.rb
621
622
  - lib/graphql/schema/member/has_deprecation_reason.rb
622
623
  - lib/graphql/schema/member/has_directives.rb