graphql 2.5.20 → 2.5.22

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 +711 -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} +20 -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 +56 -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 +28 -0
  24. data/lib/graphql/schema/member/has_fields.rb +6 -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 +10 -9
  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,11 @@ 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.
200
+ # @param dataload [Class, Hash] Shorthand for making dataloader calls
198
201
  # @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
202
  # @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
203
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
@@ -217,7 +220,7 @@ module GraphQL
217
220
  # @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
218
221
  # @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
222
  # @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)
223
+ 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, dataload: 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
224
  if name.nil?
222
225
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
223
226
  end
@@ -267,23 +270,32 @@ module GraphQL
267
270
  @resolver_method = (resolver_method || name_s).to_sym
268
271
 
269
272
  if resolve_static
270
- @batch_mode = :resolve_static
271
- @batch_mode_key = resolve_static == true ? @method_sym : resolve_static
273
+ @execution_next_mode = :resolve_static
274
+ @execution_next_mode_key = resolve_static == true ? @method_sym : resolve_static
272
275
  elsif resolve_batch
273
- @batch_mode = :resolve_batch
274
- @batch_mode_key = resolve_batch == true ? @method_sym : resolve_batch
276
+ @execution_next_mode = :resolve_batch
277
+ @execution_next_mode_key = resolve_batch == true ? @method_sym : resolve_batch
275
278
  elsif resolve_each
276
- @batch_mode = :resolve_each
277
- @batch_mode_key = resolve_each == true ? @method_sym : resolve_each
279
+ @execution_next_mode = :resolve_each
280
+ @execution_next_mode_key = resolve_each == true ? @method_sym : resolve_each
278
281
  elsif hash_key
279
- @batch_mode = :hash_key
280
- @batch_mode_key = hash_key
282
+ @execution_next_mode = :hash_key
283
+ @execution_next_mode_key = hash_key
281
284
  elsif dig
282
- @batch_mode = :dig
283
- @batch_mode_key = dig
285
+ @execution_next_mode = :dig
286
+ @execution_next_mode_key = dig
287
+ elsif resolver_class
288
+ @execution_next_mode = :resolver_class
289
+ @execution_next_mode_key = resolver_class
290
+ elsif resolve_legacy_instance_method
291
+ @execution_next_mode = :resolve_legacy_instance_method
292
+ @execution_next_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
293
+ elsif dataload
294
+ @execution_next_mode = :dataload
295
+ @execution_next_mode_key = dataload
284
296
  else
285
- @batch_mode = :direct_send
286
- @batch_mode_key = @method_sym
297
+ @execution_next_mode = :direct_send
298
+ @execution_next_mode_key = @method_sym
287
299
  end
288
300
 
289
301
  @complexity = complexity
@@ -356,49 +368,8 @@ module GraphQL
356
368
  end
357
369
  end
358
370
 
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
371
  # @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
372
+ attr_reader :execution_next_mode_key, :execution_next_mode
402
373
 
403
374
  # Calls the definition block, if one was given.
404
375
  # This is deferred so that references to the return type
@@ -954,6 +925,33 @@ ERR
954
925
  end
955
926
  end
956
927
 
928
+ public
929
+
930
+ def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
931
+ extension = @extensions[idx]
932
+ if extension
933
+ extension.resolve_next(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
934
+ if memo
935
+ memos = extended.memos ||= {}
936
+ memos[idx] = memo
937
+ end
938
+
939
+ if (extras = extension.added_extras)
940
+ ae = extended.added_extras ||= []
941
+ ae.concat(extras)
942
+ end
943
+
944
+ extended.object = extended_objs
945
+ extended.arguments = extended_args
946
+ run_next_extensions_before_resolve(extended_objs, extended_args, ctx, extended, idx: idx + 1, &block)
947
+ end
948
+ else
949
+ yield(objs, args)
950
+ end
951
+ end
952
+
953
+ private
954
+
957
955
  def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
958
956
  extension = @extensions[idx]
959
957
  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`
@@ -48,6 +56,16 @@ module GraphQL
48
56
  source.load(find_by_value)
49
57
  end
50
58
 
59
+ # @see dataload_record Like `dataload_record`, but accepts an Array of `find_by_values`
60
+ def dataload_all_records(model, find_by_values, find_by: nil)
61
+ source = if find_by
62
+ dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
63
+ else
64
+ dataloader.with(Dataloader::ActiveRecordSource, model)
65
+ end
66
+ source.load_all(find_by_values)
67
+ end
68
+
51
69
  # Look up an associated record using a Rails association (via {Dataloader::ActiveRecordAssociationSource})
52
70
  # @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
53
71
  # @param record [ActiveRecord::Base] The object that the association belongs to.
@@ -65,6 +83,16 @@ module GraphQL
65
83
  end
66
84
  source.load(record)
67
85
  end
86
+
87
+ # @see dataload_association Like `dataload_assocation` but accepts an Array of records (required param)
88
+ def dataload_all_associations(records, association_name, scope: nil)
89
+ source = if scope
90
+ dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
91
+ else
92
+ dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
93
+ end
94
+ source.load_all(records)
95
+ end
68
96
  end
69
97
  end
70
98
  end
@@ -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.
@@ -47,6 +48,7 @@ module GraphQL
47
48
  # @option kwargs [Boolean] :dynamic_introspection (Private, used by GraphQL-Ruby)
48
49
  # @option kwargs [Boolean] :relay_node_field (Private, used by GraphQL-Ruby)
49
50
  # @option kwargs [Boolean] :relay_nodes_field (Private, used by GraphQL-Ruby)
51
+ # @option kwargs [Class, Hash] :dataload Shorthand for dataloader lookups
50
52
  # @option kwargs [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] :extras Extra arguments to be injected into the resolver for this field
51
53
  # @param kwargs [Hash] Keywords for defining the field. Any not documented here will be passed to your base field class where they must be handled.
52
54
  # @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}.
@@ -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.22"
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.22
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-23 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
@@ -830,7 +831,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
830
831
  - !ruby/object:Gem::Version
831
832
  version: '0'
832
833
  requirements: []
833
- rubygems_version: 3.6.2
834
+ rubygems_version: 4.0.8
834
835
  specification_version: 4
835
836
  summary: A GraphQL language and runtime for Ruby
836
837
  test_files: []