graphql 2.6.1 → 2.6.3

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +29 -13
  3. data/lib/graphql/backtrace/table.rb +10 -1
  4. data/lib/graphql/current.rb +7 -1
  5. data/lib/graphql/dataloader.rb +1 -1
  6. data/lib/graphql/execution/directive_checks.rb +2 -0
  7. data/lib/graphql/execution/field_resolve_step.rb +178 -65
  8. data/lib/graphql/execution/finalize.rb +21 -8
  9. data/lib/graphql/execution/input_values.rb +110 -38
  10. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
  11. data/lib/graphql/execution/interpreter/runtime.rb +36 -15
  12. data/lib/graphql/execution/load_argument_step.rb +41 -3
  13. data/lib/graphql/execution/next.rb +20 -12
  14. data/lib/graphql/execution/prepare_object_step.rb +24 -5
  15. data/lib/graphql/execution/resolve_type_step.rb +27 -0
  16. data/lib/graphql/execution/runner.rb +65 -30
  17. data/lib/graphql/execution/selections_step.rb +1 -1
  18. data/lib/graphql/execution.rb +8 -1
  19. data/lib/graphql/execution_error.rb +6 -12
  20. data/lib/graphql/introspection/entry_points.rb +2 -2
  21. data/lib/graphql/introspection/schema_type.rb +6 -2
  22. data/lib/graphql/language/lexer.rb +1 -1
  23. data/lib/graphql/language/parser.rb +1 -1
  24. data/lib/graphql/language.rb +8 -2
  25. data/lib/graphql/pagination/connections.rb +1 -3
  26. data/lib/graphql/query.rb +2 -2
  27. data/lib/graphql/schema/argument.rb +3 -3
  28. data/lib/graphql/schema/directive/feature.rb +4 -0
  29. data/lib/graphql/schema/directive/transform.rb +20 -0
  30. data/lib/graphql/schema/has_single_input_argument.rb +24 -13
  31. data/lib/graphql/schema/input_object.rb +4 -0
  32. data/lib/graphql/schema/interface.rb +1 -1
  33. data/lib/graphql/schema/introspection_system.rb +6 -21
  34. data/lib/graphql/schema/printer.rb +1 -1
  35. data/lib/graphql/schema/ractor_shareable.rb +1 -0
  36. data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
  37. data/lib/graphql/schema/resolver.rb +0 -7
  38. data/lib/graphql/schema/subscription.rb +53 -8
  39. data/lib/graphql/schema/timeout.rb +2 -2
  40. data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
  41. data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
  42. data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
  43. data/lib/graphql/schema/validator/format_validator.rb +3 -3
  44. data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
  45. data/lib/graphql/schema/validator/length_validator.rb +6 -6
  46. data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
  47. data/lib/graphql/schema/validator/required_validator.rb +6 -4
  48. data/lib/graphql/schema/validator.rb +9 -0
  49. data/lib/graphql/schema/visibility/profile.rb +6 -4
  50. data/lib/graphql/schema/visibility/visit.rb +1 -1
  51. data/lib/graphql/schema/visibility.rb +30 -22
  52. data/lib/graphql/schema.rb +31 -10
  53. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +6 -0
  54. data/lib/graphql/subscriptions/event.rb +0 -1
  55. data/lib/graphql/tracing/perfetto_trace.rb +5 -3
  56. data/lib/graphql/version.rb +1 -1
  57. metadata +3 -2
@@ -30,14 +30,47 @@ module GraphQL
30
30
  end
31
31
  end
32
32
 
33
+ # @api private
34
+ def call_resolve(args_hash)
35
+ if @field_resolve_step.nil?
36
+ super
37
+ else
38
+ context.namespace(:subscriptions)[:update_event] = event
39
+ result = nil
40
+ unsubscribed = true
41
+ unsubscribed_result = nil
42
+ begin
43
+ result = super
44
+ unsubscribed = false
45
+ rescue EarlyUnsubscribe => err
46
+ unsubscribed_result = err.unsubscribed_result
47
+ end
48
+
49
+
50
+ if unsubscribed
51
+ if unsubscribed_result
52
+ context.namespace(:subscriptions)[:final_update] = true
53
+ unsubscribed_result
54
+ else
55
+ context.skip
56
+ end
57
+ else
58
+ result
59
+ end
60
+ end
61
+ end
62
+
33
63
  # @api private
34
64
  def resolve_with_support(**args)
35
65
  @original_arguments = args # before `loads:` have been run
36
66
  result = nil
37
67
  unsubscribed = true
38
- unsubscribed_result = catch :graphql_subscription_unsubscribed do
68
+ unsubscribed_result = nil
69
+ begin
39
70
  result = super
40
71
  unsubscribed = false
72
+ rescue EarlyUnsubscribe => err
73
+ unsubscribed_result = err.unsubscribed_result
41
74
  end
42
75
 
43
76
 
@@ -114,7 +147,13 @@ module GraphQL
114
147
  # @return [void]
115
148
  def unsubscribe(update_value = nil)
116
149
  context.namespace(:subscriptions)[:unsubscribed] = true
117
- throw :graphql_subscription_unsubscribed, update_value
150
+ err = EarlyUnsubscribe.new
151
+ err.unsubscribed_result = update_value
152
+ raise err
153
+ end
154
+
155
+ class EarlyUnsubscribe < GraphQL::RuntimeError
156
+ attr_accessor :unsubscribed_result
118
157
  end
119
158
 
120
159
  # Call this method to provide a new subscription_scope; OR
@@ -187,12 +226,18 @@ module GraphQL
187
226
 
188
227
  # @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend
189
228
  def event
190
- @event ||= Subscriptions::Event.new(
191
- name: field.name,
192
- arguments: @original_arguments,
193
- context: context,
194
- field: field,
195
- )
229
+ @event ||= begin
230
+ if @original_arguments.nil? && @field_resolve_step
231
+ @original_arguments, _errors = @field_resolve_step.arguments_without_loads
232
+ end
233
+
234
+ Subscriptions::Event.new(
235
+ name: field.name,
236
+ arguments: @original_arguments,
237
+ context: context,
238
+ field: field,
239
+ )
240
+ end
196
241
  end
197
242
  end
198
243
  end
@@ -68,7 +68,7 @@ module GraphQL
68
68
  super
69
69
  end
70
70
 
71
- def execute_field(query:, field:, **_rest)
71
+ def begin_execute_field(field, _arguments, _objects, query)
72
72
  timeout_state = query.context.namespace(@timeout).fetch(:state)
73
73
  # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
74
74
  if timeout_state == false
@@ -84,7 +84,7 @@ module GraphQL
84
84
 
85
85
  # `handle_timeout` may have set this to be `false`
86
86
  if timeout_state != false
87
- error
87
+ raise error
88
88
  else
89
89
  super
90
90
  end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # @example Require a non-empty string for an argument
9
9
  # argument :name, String, required: true, validate: { allow_blank: false }
10
10
  class AllowBlankValidator < Validator
11
- def initialize(allow_blank_positional, allow_blank: nil, message: "%{validated} can't be blank", **default_options)
11
+ def initialize(allow_blank_positional = nil, allow_blank: nil, message: "%{validated} can't be blank", **default_options)
12
12
  @message = message
13
13
  super(**default_options)
14
14
  @allow_blank = allow_blank.nil? ? allow_blank_positional : allow_blank
@@ -16,10 +16,10 @@ module GraphQL
16
16
 
17
17
  def validate(_object, _context, value)
18
18
  if value.respond_to?(:blank?) && value.blank?
19
- if (value.nil? && @allow_null) || @allow_blank
19
+ if (value.nil? && validation_parameter(@allow_null)) || validation_parameter(@allow_blank)
20
20
  # pass
21
21
  else
22
- @message
22
+ validation_parameter(@message)
23
23
  end
24
24
  end
25
25
  end
@@ -9,15 +9,15 @@ module GraphQL
9
9
  # argument :name, String, required: false, validates: { allow_null: false }
10
10
  class AllowNullValidator < Validator
11
11
  MESSAGE = "%{validated} can't be null"
12
- def initialize(allow_null_positional, allow_null: nil, message: MESSAGE, **default_options)
12
+ def initialize(allow_null_positional = nil, allow_null: nil, message: MESSAGE, **default_options)
13
13
  @message = message
14
14
  super(**default_options)
15
15
  @allow_null = allow_null.nil? ? allow_null_positional : allow_null
16
16
  end
17
17
 
18
18
  def validate(_object, _context, value)
19
- if value.nil? && !@allow_null
20
- @message
19
+ if value.nil? && !validation_parameter(@allow_null)
20
+ validation_parameter(@message)
21
21
  end
22
22
  end
23
23
  end
@@ -23,8 +23,8 @@ module GraphQL
23
23
  def validate(_object, _context, value)
24
24
  if permitted_empty_value?(value)
25
25
  # pass
26
- elsif @in_list.include?(value)
27
- @message
26
+ elsif validation_parameter(@in_list).include?(value)
27
+ validation_parameter(@message)
28
28
  end
29
29
  end
30
30
  end
@@ -37,9 +37,9 @@ module GraphQL
37
37
  if permitted_empty_value?(value)
38
38
  # Do nothing
39
39
  elsif value.nil? ||
40
- (@with_pattern && !value.match?(@with_pattern)) ||
41
- (@without_pattern && value.match?(@without_pattern))
42
- @message
40
+ (@with_pattern && !value.match?(validation_parameter(@with_pattern))) ||
41
+ (@without_pattern && value.match?(validation_parameter(@without_pattern)))
42
+ validation_parameter(@message)
43
43
  end
44
44
  end
45
45
  end
@@ -25,8 +25,8 @@ module GraphQL
25
25
  def validate(_object, _context, value)
26
26
  if permitted_empty_value?(value)
27
27
  # pass
28
- elsif !@in_list.include?(value)
29
- @message
28
+ elsif !validation_parameter(@in_list).include?(value)
29
+ validation_parameter(@message)
30
30
  end
31
31
  end
32
32
  end
@@ -45,12 +45,12 @@ module GraphQL
45
45
  def validate(_object, _context, value)
46
46
  return if permitted_empty_value?(value) # pass in this case
47
47
  length = value.nil? ? 0 : value.length
48
- if @maximum && length > @maximum
49
- partial_format(@too_long, { count: @maximum })
50
- elsif @minimum && length < @minimum
51
- partial_format(@too_short, { count: @minimum })
52
- elsif @is && length != @is
53
- partial_format(@wrong_length, { count: @is })
48
+ if (current_max = validation_parameter(@maximum)) && length > current_max
49
+ partial_format(validation_parameter(@too_long), { count: current_max })
50
+ elsif (current_min = validation_parameter(@minimum)) && length < current_min
51
+ partial_format(validation_parameter(@too_short), { count: current_min })
52
+ elsif (current_is = validation_parameter(@is)) && length != current_is
53
+ partial_format(validation_parameter(@wrong_length), { count: current_is })
54
54
  end
55
55
  end
56
56
  end
@@ -55,25 +55,25 @@ module GraphQL
55
55
  if permitted_empty_value?(value)
56
56
  # pass in this case
57
57
  elsif value.nil? # @allow_null is handled in the parent class
58
- @null_message
59
- elsif @greater_than && value <= @greater_than
60
- partial_format(@message, { comparison: "greater than", target: @greater_than })
61
- elsif @greater_than_or_equal_to && value < @greater_than_or_equal_to
62
- partial_format(@message, { comparison: "greater than or equal to", target: @greater_than_or_equal_to })
63
- elsif @less_than && value >= @less_than
64
- partial_format(@message, { comparison: "less than", target: @less_than })
65
- elsif @less_than_or_equal_to && value > @less_than_or_equal_to
66
- partial_format(@message, { comparison: "less than or equal to", target: @less_than_or_equal_to })
67
- elsif @equal_to && value != @equal_to
68
- partial_format(@message, { comparison: "equal to", target: @equal_to })
69
- elsif @other_than && value == @other_than
70
- partial_format(@message, { comparison: "something other than", target: @other_than })
71
- elsif @even && !value.even?
72
- (partial_format(@message, { comparison: "even", target: "" })).strip
73
- elsif @odd && !value.odd?
74
- (partial_format(@message, { comparison: "odd", target: "" })).strip
75
- elsif @within && !@within.include?(value)
76
- partial_format(@message, { comparison: "within", target: @within })
58
+ validation_parameter(@null_message)
59
+ elsif (current_greater_than = validation_parameter(@greater_than)) && value <= current_greater_than
60
+ partial_format(validation_parameter(@message), { comparison: "greater than", target: current_greater_than })
61
+ elsif (current_greater_than_or_equal_to = validation_parameter(@greater_than_or_equal_to)) && value < current_greater_than_or_equal_to
62
+ partial_format(validation_parameter(@message), { comparison: "greater than or equal to", target: current_greater_than_or_equal_to })
63
+ elsif (current_less_than = validation_parameter(@less_than)) && value >= current_less_than
64
+ partial_format(validation_parameter(@message), { comparison: "less than", target: current_less_than })
65
+ elsif (current_less_than_or_equal_to = validation_parameter(@less_than_or_equal_to)) && value > current_less_than_or_equal_to
66
+ partial_format(validation_parameter(@message), { comparison: "less than or equal to", target: current_less_than_or_equal_to })
67
+ elsif (current_equal_to = validation_parameter(@equal_to)) && value != current_equal_to
68
+ partial_format(validation_parameter(@message), { comparison: "equal to", target: current_equal_to })
69
+ elsif (current_other_than = validation_parameter(@other_than)) && value == current_other_than
70
+ partial_format(validation_parameter(@message), { comparison: "something other than", target: current_other_than })
71
+ elsif validation_parameter(@even) && !value.even?
72
+ (partial_format(validation_parameter(@message), { comparison: "even", target: "" })).strip
73
+ elsif validation_parameter(@odd) && !value.odd?
74
+ (partial_format(validation_parameter(@message), { comparison: "odd", target: "" })).strip
75
+ elsif (current_within = validation_parameter(@within)) && !current_within.include?(value)
76
+ partial_format(validation_parameter(@message), { comparison: "within", target: current_within })
77
77
  end
78
78
  end
79
79
  end
@@ -67,7 +67,8 @@ module GraphQL
67
67
  no_visible_conditions = true
68
68
 
69
69
  if !value.nil?
70
- @one_of.each do |one_of_condition|
70
+ validation_parameter(@one_of).each do |one_of_condition|
71
+ one_of_condition = validation_parameter(one_of_condition)
71
72
  case one_of_condition
72
73
  when Symbol
73
74
  if no_visible_conditions && visible_keywords.include?(one_of_condition)
@@ -108,7 +109,7 @@ module GraphQL
108
109
  end
109
110
 
110
111
  if no_visible_conditions
111
- if @allow_all_hidden
112
+ if validation_parameter(@allow_all_hidden)
112
113
  return nil
113
114
  else
114
115
  raise GraphQL::Error, <<~ERR
@@ -122,7 +123,7 @@ module GraphQL
122
123
  if fully_matched_conditions == 1 && partially_matched_conditions == 0
123
124
  nil # OK
124
125
  else
125
- @message || build_message(context)
126
+ validation_parameter(@message) || build_message(context)
126
127
  end
127
128
  end
128
129
 
@@ -130,8 +131,9 @@ module GraphQL
130
131
  argument_definitions = context.types.arguments(@validated)
131
132
 
132
133
  required_names = @one_of.map do |arg_keyword|
134
+ arg_keyword = validation_parameter(arg_keyword)
133
135
  if arg_keyword.is_a?(Array)
134
- names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
136
+ names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, validation_parameter(arg)) }
135
137
  names.compact! # hidden arguments are `nil`
136
138
  "(" + names.join(" and ") + ")"
137
139
  else
@@ -34,6 +34,15 @@ module GraphQL
34
34
  string
35
35
  end
36
36
 
37
+ # @return [Object] The current value to use for validation, based on `config_value` from configuration time. If a Proc is given, this calls it and returns it.
38
+ def validation_parameter(config_value)
39
+ if config_value.is_a?(Proc)
40
+ config_value.call
41
+ else
42
+ config_value
43
+ end
44
+ end
45
+
37
46
  # @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true`
38
47
  def permitted_empty_value?(value)
39
48
  (value.nil? && @allow_null) ||
@@ -290,11 +290,13 @@ module GraphQL
290
290
  end
291
291
  # Lots more to do here
292
292
  end
293
- @schema.introspection_system.entry_points.each do |f|
294
- arguments(f).each do |arg|
295
- argument(f, arg.graphql_name)
293
+ if @schema.query
294
+ @schema.introspection_system.entry_points.each do |f|
295
+ arguments(f).each do |arg|
296
+ argument(f, arg.graphql_name)
297
+ end
298
+ field(@schema.query, f.graphql_name)
296
299
  end
297
- field(@schema.query, f.graphql_name)
298
300
  end
299
301
  @schema.introspection_system.dynamic_fields.each do |f|
300
302
  arguments(f).each do |arg|
@@ -102,7 +102,7 @@ module GraphQL
102
102
 
103
103
  missed_late_types_streak = 0
104
104
  while (owner, late_type = @late_bound_types.shift)
105
- if (late_type.is_a?(String) && (type = Member::BuildType.constantize(type))) ||
105
+ if (late_type.is_a?(String) && (type = Member::BuildType.constantize(late_type))) ||
106
106
  (late_type.is_a?(LateBoundType) && (type = @visited_types.find { |t| t.graphql_name == late_type.graphql_name }))
107
107
  missed_late_types_streak = 0 # might succeed next round
108
108
  update_type_owner(owner, type)
@@ -8,11 +8,17 @@ module GraphQL
8
8
  # Use this plugin to make some parts of your schema hidden from some viewers.
9
9
  #
10
10
  class Visibility
11
+ class TypeConfigurationError < GraphQL::Error
12
+ def initialize(config_message, config_str)
13
+ message = "GraphQL::Schema::Visibility already preloaded, but #{config_message} added to the schema. Move this `#{config_str}` configuration above `use(GraphQL::Schema::Visibility)"
14
+ super(message)
15
+ end
16
+ end
11
17
  # @param schema [Class<GraphQL::Schema>]
12
18
  # @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
13
19
  # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?` and `Rails.env.staging?`)
14
20
  # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
15
- def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging?) : nil), migration_errors: false)
21
+ def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging? || nil) : false), migration_errors: false)
16
22
  profiles&.each { |name, ctx|
17
23
  ctx[:visibility_profile] = name
18
24
  ctx.freeze
@@ -20,7 +26,7 @@ module GraphQL
20
26
  schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
21
27
  end
22
28
 
23
- def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
29
+ def initialize(schema, dynamic:, preload:, profiles:, migration_errors:, configuration_inherited: false)
24
30
  @schema = schema
25
31
  schema.use_visibility_profile = true
26
32
  schema.visibility_profile_class = if migration_errors
@@ -40,6 +46,7 @@ module GraphQL
40
46
  @types = nil
41
47
  @all_references = nil
42
48
  @loaded_all = false
49
+ @configuration_inherited = configuration_inherited
43
50
  if preload
44
51
  self.preload
45
52
  end
@@ -94,6 +101,7 @@ module GraphQL
94
101
  # Root types may have been nil:
95
102
  types_to_visit.compact!
96
103
  ensure_all_loaded(types_to_visit)
104
+ @cached_profiles.clear
97
105
  @profiles.each do |profile_name, example_ctx|
98
106
  prof = profile_for(example_ctx)
99
107
  prof.preload
@@ -102,41 +110,27 @@ module GraphQL
102
110
 
103
111
  # @api private
104
112
  def query_configured(query_type)
105
- if @preload
106
- ensure_all_loaded([query_type])
107
- end
113
+ require_if_preloaded("a query type was", "query(...)")
108
114
  end
109
115
 
110
116
  # @api private
111
117
  def mutation_configured(mutation_type)
112
- if @preload
113
- ensure_all_loaded([mutation_type])
114
- end
118
+ require_if_preloaded("a mutation type was", "mutation(...)")
115
119
  end
116
120
 
117
121
  # @api private
118
122
  def subscription_configured(subscription_type)
119
- if @preload
120
- ensure_all_loaded([subscription_type])
121
- end
123
+ require_if_preloaded("a mutation type was", "subscription(...)")
122
124
  end
123
125
 
124
126
  # @api private
125
127
  def orphan_types_configured(orphan_types)
126
- if @preload
127
- ensure_all_loaded(orphan_types)
128
- end
128
+ require_if_preloaded("orphan types were", "orphan_types(...)")
129
129
  end
130
130
 
131
131
  # @api private
132
132
  def introspection_system_configured(introspection_system)
133
- if @preload
134
- introspection_types = [
135
- *@schema.introspection_system.types.values,
136
- *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
137
- ]
138
- ensure_all_loaded(introspection_types)
139
- end
133
+ require_if_preloaded("custom introspection was", "introspection(...)")
140
134
  end
141
135
 
142
136
  # Make another Visibility for `schema` based on this one
@@ -148,7 +142,8 @@ module GraphQL
148
142
  dynamic: @dynamic,
149
143
  preload: @preload,
150
144
  profiles: @profiles,
151
- migration_errors: @migration_errors
145
+ migration_errors: @migration_errors,
146
+ configuration_inherited: true,
152
147
  )
153
148
  end
154
149
 
@@ -196,6 +191,19 @@ module GraphQL
196
191
 
197
192
  private
198
193
 
194
+ def require_if_preloaded(config_message, config_code)
195
+ case @preload
196
+ when false
197
+ # Rails.env wasn't defined, so this won't try to preload unless manually set to true
198
+ when true, nil
199
+ if @configuration_inherited
200
+ preload
201
+ else
202
+ raise TypeConfigurationError.new(config_message, config_code)
203
+ end
204
+ end
205
+ end
206
+
199
207
  def ensure_all_loaded(types_to_visit)
200
208
  while (type = types_to_visit.shift)
201
209
  if type.kind.fields? && @preloaded_types.add?(type)
@@ -757,6 +757,7 @@ module GraphQL
757
757
  # reset this cached value:
758
758
  @introspection_system = nil
759
759
  introspection_system
760
+ self.visibility&.introspection_system_configured(introspection_system)
760
761
  @introspection
761
762
  else
762
763
  @introspection || find_inherited_value(:introspection)
@@ -768,7 +769,6 @@ module GraphQL
768
769
  if !@introspection_system
769
770
  @introspection_system = Schema::IntrospectionSystem.new(self)
770
771
  @introspection_system.resolve_late_bindings
771
- self.visibility&.introspection_system_configured(@introspection_system)
772
772
  end
773
773
  @introspection_system
774
774
  end
@@ -1150,17 +1150,14 @@ module GraphQL
1150
1150
  attr_accessor :using_backtrace
1151
1151
 
1152
1152
  # @api private
1153
- def handle_or_reraise(context, err)
1153
+ def handle_or_reraise(context, err, object: context[:current_object], arguments: context[:current_arguments], field: context[:current_field])
1154
1154
  handler = Execution::Errors.find_handler_for(self, err.class)
1155
1155
  if handler
1156
- obj = context[:current_object]
1157
- args = context[:current_arguments]
1158
- args = args && args.respond_to?(:keyword_arguments) ? args.keyword_arguments : nil
1159
- field = context[:current_field]
1160
- if obj.is_a?(GraphQL::Schema::Object)
1161
- obj = obj.object
1156
+ arguments = arguments.respond_to?(:keyword_arguments) ? arguments.keyword_arguments : arguments
1157
+ if object.is_a?(GraphQL::Schema::Object)
1158
+ object = object.object
1162
1159
  end
1163
- handler[:handler].call(err, obj, args, context, field)
1160
+ handler[:handler].call(err, object, arguments, context, field)
1164
1161
  else
1165
1162
  if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
1166
1163
  err = GraphQL::Backtrace::TracedError.new(err, context)
@@ -1587,6 +1584,14 @@ module GraphQL
1587
1584
  # @see {Query#initialize} for arguments.
1588
1585
  # @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
1589
1586
  def execute(query_str = nil, **kwargs)
1587
+ if default_execution_next
1588
+ execute_next(query_str, **kwargs)
1589
+ else
1590
+ execute_legacy(query_str, **kwargs)
1591
+ end
1592
+ end
1593
+
1594
+ def execute_legacy(query_str = nil, **kwargs)
1590
1595
  if query_str
1591
1596
  kwargs[:query] = query_str
1592
1597
  end
@@ -1628,7 +1633,23 @@ module GraphQL
1628
1633
  # @option kwargs [nil, Integer] :max_complexity (nil)
1629
1634
  # @return [Array<GraphQL::Query::Result>] One result for each query in the input
1630
1635
  def multiplex(queries, **kwargs)
1631
- GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
1636
+ if @default_execution_next
1637
+ multiplex_next(queries, **kwargs)
1638
+ else
1639
+ GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
1640
+ end
1641
+ end
1642
+
1643
+ def default_execution_next(new_value = NOT_CONFIGURED)
1644
+ if !NOT_CONFIGURED.equal?(new_value)
1645
+ @default_execution_next = new_value
1646
+ elsif instance_variable_defined?(:@default_execution_next)
1647
+ @default_execution_next
1648
+ elsif superclass.respond_to?(:default_execution_next)
1649
+ superclass.default_execution_next
1650
+ else
1651
+ false
1652
+ end
1632
1653
  end
1633
1654
 
1634
1655
  def instrumenters
@@ -54,6 +54,12 @@ module GraphQL
54
54
  events << subscription_instance.event
55
55
  end
56
56
  value
57
+ elsif (exec_next_update_event = context.namespace(:subscriptions)[:update_event])
58
+ if context.query.subscription_topic == exec_next_update_event.topic
59
+ value
60
+ else
61
+ context.skip
62
+ end
57
63
  elsif (events = context.namespace(:subscriptions)[:events])
58
64
  # This is the first execution, so gather an Event
59
65
  # for the backend to register:
@@ -104,7 +104,6 @@ module GraphQL
104
104
 
105
105
  def stringify_args(arg_owner, args, context)
106
106
  arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
107
-
108
107
  case args
109
108
  when Hash
110
109
  next_args = {}
@@ -67,6 +67,8 @@ module GraphQL
67
67
  DA_FETCH_KEYS_IID => "fetch keys",
68
68
  }
69
69
 
70
+ ANON_CLASS_NAME = "(anonymous)"
71
+
70
72
  DEBUG_INSPECT_CATEGORY_IIDS = [15]
71
73
  DA_DEBUG_INSPECT_CLASS_IID = 16
72
74
  DEBUG_INSPECT_EVENT_NAME_IID = 17
@@ -118,7 +120,7 @@ module GraphQL
118
120
  }
119
121
 
120
122
  @source_name_iids = Hash.new do |h, source_class|
121
- h[source_class] = @interned_event_name_iids[source_class.name]
123
+ h[source_class] = @interned_event_name_iids[source_class.name || ANON_CLASS_NAME]
122
124
  end.compare_by_identity
123
125
 
124
126
  @auth_name_iids = Hash.new do |h, graphql_type|
@@ -144,7 +146,7 @@ module GraphQL
144
146
  end
145
147
 
146
148
  @class_name_iids = Hash.new do |h, k|
147
- h[k] = @interned_da_string_values[k.name]
149
+ h[k] = @interned_da_string_values[k.name || ANON_CLASS_NAME]
148
150
  end.compare_by_identity
149
151
 
150
152
  @starting_objects = GC.stat(:total_allocated_objects)
@@ -682,7 +684,7 @@ module GraphQL
682
684
  when GraphQL::Schema::InputObject
683
685
  payload_to_debug(k, v.to_h, iid: iid, intern_value: intern_value)
684
686
  else
685
- class_name_iid = @interned_da_string_values[(v.class.name || "(anonymous)")]
687
+ class_name_iid = @interned_da_string_values[(v.class.name || ANON_CLASS_NAME)]
686
688
  da = [
687
689
  debug_annotation(DA_DEBUG_INSPECT_CLASS_IID, :string_value_iid, class_name_iid),
688
690
  ]
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.6.1"
3
+ VERSION = "2.6.3"
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.6.1
4
+ version: 2.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-04-27 00:00:00.000000000 Z
10
+ date: 2026-05-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -512,6 +512,7 @@ files:
512
512
  - lib/graphql/execution/multiplex.rb
513
513
  - lib/graphql/execution/next.rb
514
514
  - lib/graphql/execution/prepare_object_step.rb
515
+ - lib/graphql/execution/resolve_type_step.rb
515
516
  - lib/graphql/execution/runner.rb
516
517
  - lib/graphql/execution/selections_step.rb
517
518
  - lib/graphql/execution_error.rb