graphql 2.5.22 → 2.5.24

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis.rb +20 -13
  3. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  4. data/lib/graphql/execution/finalize.rb +217 -0
  5. data/lib/graphql/execution/input_values.rb +261 -0
  6. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  7. data/lib/graphql/execution/interpreter/runtime.rb +3 -2
  8. data/lib/graphql/execution/interpreter.rb +6 -9
  9. data/lib/graphql/execution/lazy.rb +1 -1
  10. data/lib/graphql/execution/load_argument_step.rb +64 -0
  11. data/lib/graphql/execution/next.rb +26 -6
  12. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  13. data/lib/graphql/execution/runner.rb +410 -0
  14. data/lib/graphql/execution/selections_step.rb +91 -0
  15. data/lib/graphql/execution.rb +8 -5
  16. data/lib/graphql/execution_error.rb +5 -1
  17. data/lib/graphql/query/context.rb +7 -1
  18. data/lib/graphql/query/partial.rb +18 -3
  19. data/lib/graphql/query.rb +10 -1
  20. data/lib/graphql/runtime_error.rb +6 -0
  21. data/lib/graphql/schema/directive.rb +23 -9
  22. data/lib/graphql/schema/field/connection_extension.rb +2 -15
  23. data/lib/graphql/schema/field/scope_extension.rb +0 -4
  24. data/lib/graphql/schema/field.rb +23 -24
  25. data/lib/graphql/schema/field_extension.rb +11 -41
  26. data/lib/graphql/schema/interface.rb +26 -0
  27. data/lib/graphql/schema/list.rb +5 -1
  28. data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
  29. data/lib/graphql/schema/member/has_fields.rb +5 -1
  30. data/lib/graphql/schema/non_null.rb +1 -1
  31. data/lib/graphql/schema/resolver.rb +18 -3
  32. data/lib/graphql/schema/subscription.rb +0 -2
  33. data/lib/graphql/schema/visibility/profile.rb +68 -49
  34. data/lib/graphql/schema/wrapper.rb +7 -1
  35. data/lib/graphql/schema.rb +12 -10
  36. data/lib/graphql/static_validation/base_visitor.rb +90 -66
  37. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  38. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  39. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
  40. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
  41. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
  42. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -2
  43. data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
  44. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
  45. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  46. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
  47. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
  48. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
  49. data/lib/graphql/static_validation/validation_context.rb +1 -1
  50. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
  51. data/lib/graphql/subscriptions/event.rb +1 -0
  52. data/lib/graphql/subscriptions.rb +35 -0
  53. data/lib/graphql/tracing/perfetto_trace.rb +2 -2
  54. data/lib/graphql/tracing/trace.rb +6 -0
  55. data/lib/graphql/unauthorized_error.rb +4 -0
  56. data/lib/graphql/version.rb +1 -1
  57. data/lib/graphql.rb +1 -3
  58. metadata +11 -8
  59. data/lib/graphql/execution/next/field_resolve_step.rb +0 -711
  60. data/lib/graphql/execution/next/load_argument_step.rb +0 -60
  61. data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
  62. data/lib/graphql/execution/next/runner.rb +0 -389
  63. data/lib/graphql/execution/next/selections_step.rb +0 -37
@@ -32,6 +32,7 @@ module GraphQL
32
32
  @multiplex = nil
33
33
  @result_values = nil
34
34
  @result = nil
35
+ @finalizers = @top_level_finalizers = nil
35
36
 
36
37
  if fragment_node
37
38
  @ast_nodes = [fragment_node]
@@ -51,6 +52,10 @@ module GraphQL
51
52
  @leaf
52
53
  end
53
54
 
55
+ def root_value
56
+ object
57
+ end
58
+
54
59
  attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :schema
55
60
 
56
61
  attr_accessor :multiplex, :result_values
@@ -90,10 +95,22 @@ module GraphQL
90
95
  @query.fragments
91
96
  end
92
97
 
98
+ def validate
99
+ @query.validate
100
+ end
101
+
93
102
  def valid?
94
103
  @query.valid?
95
104
  end
96
105
 
106
+ def query?
107
+ true
108
+ end
109
+
110
+ def run_partials(...)
111
+ @query.run_partials(...)
112
+ end
113
+
97
114
  def analyzers
98
115
  EmptyObjects::EMPTY_ARRAY
99
116
  end
@@ -107,7 +124,7 @@ module GraphQL
107
124
  end
108
125
 
109
126
  def selected_operation
110
- ast_nodes.first
127
+ Language::Nodes::OperationDefinition.new(selections: ast_nodes.flat_map(&:selections))
111
128
  end
112
129
 
113
130
  def static_errors
@@ -123,7 +140,6 @@ module GraphQL
123
140
  def set_type_info_from_path
124
141
  selections = [@query.selected_operation]
125
142
  type = @query.root_type
126
- parent_type = nil
127
143
  field_defn = nil
128
144
 
129
145
  @path.each do |name_in_doc|
@@ -162,7 +178,6 @@ module GraphQL
162
178
  end
163
179
  field_name = next_selections.first.name
164
180
  field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
165
- parent_type = type
166
181
  type = field_defn.type
167
182
  if type.non_null?
168
183
  type = type.of_type
data/lib/graphql/query.rb CHANGED
@@ -159,6 +159,7 @@ module GraphQL
159
159
  @root_value = root_value
160
160
  @fragments = nil
161
161
  @operations = nil
162
+ @finalizers = @top_level_finalizers = nil
162
163
  @validate = validate
163
164
  self.static_validator = static_validator if static_validator
164
165
  context_tracers = (context ? context.fetch(:tracers, []) : [])
@@ -262,6 +263,10 @@ module GraphQL
262
263
  with_prepared_ast { @operations }
263
264
  end
264
265
 
266
+ def path
267
+ EmptyObjects::EMPTY_ARRAY
268
+ end
269
+
265
270
  # Run subtree partials of this query and return their results.
266
271
  # Each partial is identified with a `path:` and `object:`
267
272
  # where the path references a field in the AST and the object will be treated
@@ -271,7 +276,11 @@ module GraphQL
271
276
  # @return [Array<GraphQL::Query::Result>]
272
277
  def run_partials(partials_hashes)
273
278
  partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
274
- Execution::Interpreter.run_all(@schema, partials, context: @context)
279
+ if context[:__graphql_execute_next]
280
+ Execution::Next.run_all(@schema, partials, context: @context)
281
+ else
282
+ Execution::Interpreter.run_all(@schema, partials, context: @context)
283
+ end
275
284
  end
276
285
 
277
286
  # Get the result for this query, executing it once
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class RuntimeError < Error
4
+ include GraphQL::Execution::Finalizer
5
+ end
6
+ end
@@ -31,17 +31,25 @@ module GraphQL
31
31
 
32
32
  def locations(*new_locations)
33
33
  if !new_locations.empty?
34
+ is_runtime = false
34
35
  new_locations.each do |new_loc|
35
- if !LOCATIONS.include?(new_loc.to_sym)
36
+ loc_sym = new_loc.to_sym
37
+ if !LOCATIONS.include?(loc_sym)
36
38
  raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
37
39
  end
40
+ is_runtime ||= RUNTIME_LOCATIONS.include?(loc_sym)
38
41
  end
39
42
  @locations = new_locations
43
+ @is_runtime = is_runtime
40
44
  else
41
45
  @locations ||= (superclass.respond_to?(:locations) ? superclass.locations : [])
42
46
  end
43
47
  end
44
48
 
49
+ def runtime?
50
+ @is_runtime
51
+ end
52
+
45
53
  def default_directive(new_default_directive = nil)
46
54
  if new_default_directive != nil
47
55
  @default_directive = new_default_directive
@@ -104,8 +112,12 @@ module GraphQL
104
112
 
105
113
  def inherited(subclass)
106
114
  super
115
+ parent_class = self
107
116
  subclass.class_exec do
108
117
  @default_graphql_name ||= nil
118
+ @locations = parent_class.locations
119
+ @is_runtime = parent_class.runtime?
120
+ @repeatable = false
109
121
  end
110
122
  end
111
123
  end
@@ -177,13 +189,16 @@ module GraphQL
177
189
  end
178
190
 
179
191
  LOCATIONS = [
180
- QUERY = :QUERY,
181
- MUTATION = :MUTATION,
182
- SUBSCRIPTION = :SUBSCRIPTION,
183
- FIELD = :FIELD,
184
- FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
185
- FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
186
- INLINE_FRAGMENT = :INLINE_FRAGMENT,
192
+ *(RUNTIME_LOCATIONS = [
193
+ QUERY = :QUERY,
194
+ MUTATION = :MUTATION,
195
+ SUBSCRIPTION = :SUBSCRIPTION,
196
+ FIELD = :FIELD,
197
+ FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
198
+ FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
199
+ INLINE_FRAGMENT = :INLINE_FRAGMENT,
200
+ VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
201
+ ]),
187
202
  SCHEMA = :SCHEMA,
188
203
  SCALAR = :SCALAR,
189
204
  OBJECT = :OBJECT,
@@ -195,7 +210,6 @@ module GraphQL
195
210
  ENUM_VALUE = :ENUM_VALUE,
196
211
  INPUT_OBJECT = :INPUT_OBJECT,
197
212
  INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
198
- VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
199
213
  ]
200
214
 
201
215
  DEFAULT_DEPRECATION_REASON = 'No longer supported'
@@ -12,22 +12,13 @@ module GraphQL
12
12
  end
13
13
 
14
14
  # Remove pagination args before passing it to a user method
15
- def resolve(object:, arguments:, context:)
15
+ def resolve(object: nil, objects: nil, arguments:, context:)
16
16
  next_args = arguments.dup
17
17
  next_args.delete(:first)
18
18
  next_args.delete(:last)
19
19
  next_args.delete(:before)
20
20
  next_args.delete(:after)
21
- yield(object, next_args, arguments)
22
- end
23
-
24
- def resolve_next(objects:, arguments:, context:)
25
- next_args = arguments.dup
26
- next_args.delete(:first)
27
- next_args.delete(:last)
28
- next_args.delete(:before)
29
- next_args.delete(:after)
30
- yield(objects, next_args, arguments)
21
+ yield(object || objects, next_args, arguments)
31
22
  end
32
23
 
33
24
  def after_resolve(value:, object:, arguments:, context:, memo:)
@@ -36,10 +27,6 @@ module GraphQL
36
27
  context.schema.connections.populate_connection(field, object.object, resolved_value, original_arguments, context)
37
28
  end
38
29
  end
39
-
40
- def after_resolve_next(**kwargs)
41
- raise "This should never be called -- it's hardcoded in execution instead."
42
- end
43
30
  end
44
31
  end
45
32
  end
@@ -28,10 +28,6 @@ module GraphQL
28
28
  value
29
29
  end
30
30
  end
31
-
32
- def after_resolve_next(**kwargs)
33
- raise "This should never be called -- it's hardcoded in execution instead."
34
- end
35
31
  end
36
32
  end
37
33
  end
@@ -270,32 +270,32 @@ module GraphQL
270
270
  @resolver_method = (resolver_method || name_s).to_sym
271
271
 
272
272
  if resolve_static
273
- @execution_next_mode = :resolve_static
274
- @execution_next_mode_key = resolve_static == true ? @method_sym : resolve_static
273
+ @execution_mode = :resolve_static
274
+ @execution_mode_key = resolve_static == true ? @method_sym : resolve_static
275
275
  elsif resolve_batch
276
- @execution_next_mode = :resolve_batch
277
- @execution_next_mode_key = resolve_batch == true ? @method_sym : resolve_batch
276
+ @execution_mode = :resolve_batch
277
+ @execution_mode_key = resolve_batch == true ? @method_sym : resolve_batch
278
278
  elsif resolve_each
279
- @execution_next_mode = :resolve_each
280
- @execution_next_mode_key = resolve_each == true ? @method_sym : resolve_each
279
+ @execution_mode = :resolve_each
280
+ @execution_mode_key = resolve_each == true ? @method_sym : resolve_each
281
281
  elsif hash_key
282
- @execution_next_mode = :hash_key
283
- @execution_next_mode_key = hash_key
282
+ @execution_mode = :hash_key
283
+ @execution_mode_key = hash_key
284
284
  elsif dig
285
- @execution_next_mode = :dig
286
- @execution_next_mode_key = dig
285
+ @execution_mode = :dig
286
+ @execution_mode_key = dig
287
287
  elsif resolver_class
288
- @execution_next_mode = :resolver_class
289
- @execution_next_mode_key = resolver_class
288
+ @execution_mode = :resolver_class
289
+ @execution_mode_key = resolver_class
290
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
291
+ @execution_mode = :resolve_legacy_instance_method
292
+ @execution_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
293
293
  elsif dataload
294
- @execution_next_mode = :dataload
295
- @execution_next_mode_key = dataload
294
+ @execution_mode = :dataload
295
+ @execution_mode_key = dataload
296
296
  else
297
- @execution_next_mode = :direct_send
298
- @execution_next_mode_key = @method_sym
297
+ @execution_mode = :direct_send
298
+ @execution_mode_key = @method_sym
299
299
  end
300
300
 
301
301
  @complexity = complexity
@@ -369,7 +369,7 @@ module GraphQL
369
369
  end
370
370
 
371
371
  # @api private
372
- attr_reader :execution_next_mode_key, :execution_next_mode
372
+ attr_reader :execution_mode_key, :execution_mode
373
373
 
374
374
  # Calls the definition block, if one was given.
375
375
  # This is deferred so that references to the return type
@@ -665,10 +665,9 @@ module GraphQL
665
665
  end
666
666
 
667
667
  def authorizes?(context)
668
- method(:authorized?).owner != GraphQL::Schema::Field || (
669
- (args = context.types.arguments(self)) &&
670
- (args.any? { |a| a.authorizes?(context) })
671
- )
668
+ method(:authorized?).owner != GraphQL::Schema::Field ||
669
+ ((args = context.types.arguments(self)) && (args.any? { |a| a.authorizes?(context) })) ||
670
+ (@resolver_class&.authorizes?(context)) || false
672
671
  end
673
672
 
674
673
  def authorized?(object, args, context)
@@ -930,7 +929,7 @@ ERR
930
929
  def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
931
930
  extension = @extensions[idx]
932
931
  if extension
933
- extension.resolve_next(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
932
+ extension.resolve(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
934
933
  if memo
935
934
  memos = extended.memos ||= {}
936
935
  memos[idx] = memo
@@ -123,33 +123,16 @@ module GraphQL
123
123
  #
124
124
  # Whatever this method returns will be used for execution.
125
125
  #
126
- # @param object [Object] The object the field is being resolved on
126
+ # @param object [Object] The object the field is being resolved on (not passed by new execution)
127
+ # @param objects [Array<Object>] The objects the field is being resolved on (passed by new execution)
127
128
  # @param arguments [Hash] Ruby keyword arguments for resolving this field
128
129
  # @param context [Query::Context] the context for this query
129
- # @yieldparam object [Object] The object to continue resolving the field on
130
+ # @yieldparam object_or_objects [Object, Array<Object>] The object or objects (new execution) to continue resolving the field on
130
131
  # @yieldparam arguments [Hash] The keyword arguments to continue resolving with
131
132
  # @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
132
133
  # @return [Object] The return value for this field.
133
- def resolve(object:, arguments:, context:)
134
- yield(object, arguments, nil)
135
- end
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)
134
+ def resolve(object: nil, objects: nil, arguments:, context:)
135
+ yield(object || objects, arguments, nil)
153
136
  end
154
137
 
155
138
  # Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
@@ -157,29 +140,16 @@ module GraphQL
157
140
  #
158
141
  # Whatever this hook returns will be used as the return value.
159
142
  #
160
- # @param object [Object] The object the field is being resolved on
143
+ # @param object [Object] The object the field is being resolved on (not passed by new execution)
144
+ # @param objects [Array<Object>] The object the field is being resolved on (passed by new execution)
161
145
  # @param arguments [Hash] Ruby keyword arguments for resolving this field
162
146
  # @param context [Query::Context] the context for this query
163
- # @param value [Object] Whatever the field previously returned
147
+ # @param value [Object] Whatever the field previously returned (not passed by new execution)
148
+ # @param values [Array<Object>] Whatever the field previously returned (passed by new execution)
164
149
  # @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
165
150
  # @return [Object] The return value for this field.
166
- def after_resolve(object:, arguments:, context:, value:, memo:)
167
- value
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
151
+ def after_resolve(object: nil, objects: nil, arguments:, context:, values: nil, value: nil, memo:)
152
+ value || values
183
153
  end
184
154
  end
185
155
  end
@@ -33,6 +33,26 @@ module GraphQL
33
33
  self::DefinitionMethods.module_exec(&block)
34
34
  end
35
35
 
36
+ # Instance methods defined in this block will become class methods on objects that implement this interface.
37
+ # Use it to implement `resolve_each:`, `resolve_batch:`, and `resolve_static:` fields.
38
+ # @example
39
+ # field :thing, String, resolve_static: true
40
+ #
41
+ # resolver_methods do
42
+ # def thing
43
+ # Somehow.get.thing
44
+ # end
45
+ # end
46
+ def resolver_methods(&block)
47
+ if !defined?(@_resolver_methods)
48
+ resolver_methods_module = Module.new
49
+ @_resolver_methods = resolver_methods_module
50
+ const_set(:ResolverMethods, resolver_methods_module)
51
+ extend(self::ResolverMethods)
52
+ end
53
+ self::ResolverMethods.module_exec(&block)
54
+ end
55
+
36
56
  # @see {Schema::Warden} hides interfaces without visible implementations
37
57
  def visible?(context)
38
58
  true
@@ -79,6 +99,12 @@ module GraphQL
79
99
  if !backtrace_line
80
100
  raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
81
101
  end
102
+
103
+ child_class.ancestors.reverse_each do |ancestor|
104
+ if ancestor.const_defined?(:ResolverMethods)
105
+ child_class.extend(ancestor::ResolverMethods)
106
+ end
107
+ end
82
108
  end
83
109
 
84
110
  super
@@ -19,7 +19,11 @@ module GraphQL
19
19
  end
20
20
 
21
21
  def to_type_signature
22
- "[#{@of_type.to_type_signature}]"
22
+ @type_signature ||= -"[#{@of_type.to_type_signature}]"
23
+ end
24
+
25
+ def authorizes?(ctx)
26
+ of_type.authorizes?(ctx)
23
27
  end
24
28
 
25
29
  # This is for introspection, where it's expected the name will be `null`
@@ -26,16 +26,6 @@ module GraphQL
26
26
  end
27
27
  end
28
28
 
29
- # Just a convenience method to point out that people should use graphql_name instead
30
- def name(new_name = nil)
31
- return super() if new_name.nil?
32
-
33
- fail(
34
- "The new name override method is `graphql_name`, not `name`. Usage: "\
35
- "graphql_name \"#{new_name}\""
36
- )
37
- end
38
-
39
29
  # Call this method to provide a new description; OR
40
30
  # call it without an argument to get the description
41
31
  # @param new_description [String]
@@ -150,10 +150,14 @@ module GraphQL
150
150
 
151
151
  def global_id_field(field_name, **kwargs)
152
152
  type = self
153
- field field_name, "ID", **kwargs, null: false
153
+ field field_name, "ID", **kwargs, null: false, resolve_each: true
154
154
  define_method(field_name) do
155
155
  context.schema.id_from_object(object, type, context)
156
156
  end
157
+
158
+ define_singleton_method(field_name) do |object, context|
159
+ context.schema.id_from_object(object, type, context)
160
+ end
157
161
  end
158
162
 
159
163
  # @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
@@ -24,7 +24,7 @@ module GraphQL
24
24
  end
25
25
 
26
26
  def to_type_signature
27
- "#{@of_type.to_type_signature}!"
27
+ @type_signature ||= -"#{@of_type.to_type_signature}!"
28
28
  end
29
29
 
30
30
  def inspect
@@ -46,7 +46,7 @@ module GraphQL
46
46
  @prepared_arguments = nil
47
47
  end
48
48
 
49
- attr_accessor :exec_result, :exec_index, :field_resolve_step
49
+ attr_accessor :exec_result, :exec_index, :field_resolve_step, :raw_arguments
50
50
 
51
51
  # @return [Object] The application object this field is being resolved on
52
52
  attr_accessor :object
@@ -66,7 +66,12 @@ module GraphQL
66
66
  q = context.query
67
67
  trace_objs = [object]
68
68
  q.current_trace.begin_execute_field(field, @prepared_arguments, trace_objs, q)
69
- is_authed, new_return_value = authorized?(**@prepared_arguments)
69
+ begin
70
+ is_authed, new_return_value = authorized?(**@prepared_arguments)
71
+ rescue GraphQL::UnauthorizedError => err
72
+ new_return_value = q.schema.unauthorized_object(err)
73
+ is_authed = true # the error was handled
74
+ end
70
75
 
71
76
  if (runner = @field_resolve_step.runner).resolves_lazies && runner.schema.lazy?(is_authed)
72
77
  is_authed, new_return_value = runner.schema.sync_lazy(is_authed)
@@ -74,13 +79,19 @@ module GraphQL
74
79
 
75
80
  result = if is_authed
76
81
  Schema::Validator.validate!(self.class.validators, object, context, @prepared_arguments, as: @field)
82
+ if q.subscription? && @field.owner == context.schema.subscription
83
+ # This needs to use arguments without `loads:`. TODO extract this into subscription-related code somehow?
84
+ @original_arguments = @field_resolve_step.runner.input_values[q].argument_values(@field, @field_resolve_step.ast_node.arguments, nil)
85
+ end
77
86
  call_resolve(@prepared_arguments)
87
+ elsif new_return_value.nil?
88
+ err = UnauthorizedFieldError.new(object: object, type: @field_resolve_step.parent_type, context: context, field: @field)
89
+ context.schema.unauthorized_field(err)
78
90
  else
79
91
  new_return_value
80
92
  end
81
93
  q = context.query
82
94
  q.current_trace.end_execute_field(field, @prepared_arguments, trace_objs, q, [result])
83
-
84
95
  exec_result[exec_index] = result
85
96
  rescue RuntimeError => err
86
97
  exec_result[exec_index] = err
@@ -198,6 +209,10 @@ module GraphQL
198
209
  authorize_arguments(args, inputs)
199
210
  end
200
211
 
212
+ def self.authorizes?(context)
213
+ self.instance_method(:authorized?).owner != GraphQL::Schema::Resolver
214
+ end
215
+
201
216
  # Called when an object loaded by `loads:` fails the `.authorized?` check for its resolved GraphQL object type.
202
217
  #
203
218
  # By default, the error is re-raised and passed along to {{Schema.unauthorized_object}}.
@@ -15,8 +15,6 @@ module GraphQL
15
15
  extend GraphQL::Schema::Resolver::HasPayloadType
16
16
  extend GraphQL::Schema::Member::HasFields
17
17
  NO_UPDATE = :no_update
18
- # The generated payload type is required; If there's no payload,
19
- # propagate null.
20
18
  null false
21
19
 
22
20
  # @api private