graphql 1.11.1 → 1.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +8 -0
  3. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  4. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  5. data/lib/generators/graphql/templates/base_field.erb +2 -0
  6. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  7. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  8. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  9. data/lib/generators/graphql/templates/base_object.erb +2 -0
  10. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  11. data/lib/generators/graphql/templates/base_union.erb +2 -0
  12. data/lib/generators/graphql/templates/enum.erb +2 -0
  13. data/lib/generators/graphql/templates/graphql_controller.erb +13 -9
  14. data/lib/generators/graphql/templates/interface.erb +2 -0
  15. data/lib/generators/graphql/templates/loader.erb +2 -0
  16. data/lib/generators/graphql/templates/mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  18. data/lib/generators/graphql/templates/object.erb +2 -0
  19. data/lib/generators/graphql/templates/query_type.erb +2 -0
  20. data/lib/generators/graphql/templates/scalar.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/generators/graphql/templates/union.erb +3 -1
  23. data/lib/graphql.rb +16 -0
  24. data/lib/graphql/argument.rb +3 -3
  25. data/lib/graphql/backtrace/tracer.rb +2 -1
  26. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  27. data/lib/graphql/directive.rb +4 -0
  28. data/lib/graphql/execution/interpreter.rb +10 -0
  29. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  30. data/lib/graphql/execution/interpreter/runtime.rb +59 -45
  31. data/lib/graphql/field.rb +4 -0
  32. data/lib/graphql/input_object_type.rb +4 -0
  33. data/lib/graphql/introspection.rb +96 -0
  34. data/lib/graphql/introspection/field_type.rb +7 -3
  35. data/lib/graphql/introspection/input_value_type.rb +6 -0
  36. data/lib/graphql/introspection/introspection_query.rb +6 -92
  37. data/lib/graphql/introspection/type_type.rb +7 -3
  38. data/lib/graphql/language/block_string.rb +24 -5
  39. data/lib/graphql/language/lexer.rb +7 -3
  40. data/lib/graphql/language/lexer.rl +7 -3
  41. data/lib/graphql/language/nodes.rb +2 -1
  42. data/lib/graphql/language/parser.rb +107 -103
  43. data/lib/graphql/language/parser.y +4 -0
  44. data/lib/graphql/language/sanitized_printer.rb +59 -26
  45. data/lib/graphql/language/visitor.rb +2 -2
  46. data/lib/graphql/name_validator.rb +6 -7
  47. data/lib/graphql/pagination/connection.rb +6 -8
  48. data/lib/graphql/pagination/connections.rb +23 -3
  49. data/lib/graphql/query.rb +2 -2
  50. data/lib/graphql/query/context.rb +30 -3
  51. data/lib/graphql/query/fingerprint.rb +2 -0
  52. data/lib/graphql/query/validation_pipeline.rb +3 -0
  53. data/lib/graphql/relay/range_add.rb +14 -5
  54. data/lib/graphql/schema.rb +40 -31
  55. data/lib/graphql/schema/argument.rb +56 -5
  56. data/lib/graphql/schema/build_from_definition.rb +67 -38
  57. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  58. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  59. data/lib/graphql/schema/enum_value.rb +1 -0
  60. data/lib/graphql/schema/field.rb +17 -10
  61. data/lib/graphql/schema/field/connection_extension.rb +44 -34
  62. data/lib/graphql/schema/input_object.rb +21 -18
  63. data/lib/graphql/schema/interface.rb +1 -1
  64. data/lib/graphql/schema/late_bound_type.rb +2 -2
  65. data/lib/graphql/schema/loader.rb +20 -1
  66. data/lib/graphql/schema/member/build_type.rb +14 -4
  67. data/lib/graphql/schema/member/has_arguments.rb +19 -1
  68. data/lib/graphql/schema/member/has_fields.rb +17 -7
  69. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  70. data/lib/graphql/schema/mutation.rb +4 -0
  71. data/lib/graphql/schema/relay_classic_mutation.rb +3 -1
  72. data/lib/graphql/schema/resolver.rb +6 -0
  73. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -1
  74. data/lib/graphql/schema/subscription.rb +2 -12
  75. data/lib/graphql/schema/timeout.rb +29 -15
  76. data/lib/graphql/schema/union.rb +29 -0
  77. data/lib/graphql/schema/unique_within_type.rb +1 -2
  78. data/lib/graphql/schema/validation.rb +8 -0
  79. data/lib/graphql/schema/warden.rb +8 -3
  80. data/lib/graphql/static_validation/literal_validator.rb +7 -7
  81. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  82. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  83. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -2
  84. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
  85. data/lib/graphql/static_validation/validator.rb +7 -4
  86. data/lib/graphql/subscriptions.rb +32 -22
  87. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +45 -20
  88. data/lib/graphql/subscriptions/serialize.rb +22 -4
  89. data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
  90. data/lib/graphql/types/iso_8601_date_time.rb +2 -1
  91. data/lib/graphql/types/relay/base_connection.rb +6 -5
  92. data/lib/graphql/unauthorized_error.rb +1 -1
  93. data/lib/graphql/version.rb +1 -1
  94. metadata +3 -3
@@ -45,7 +45,8 @@ module GraphQL
45
45
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
46
46
  # @param from_resolver [Boolean] if true, a Resolver class defined this argument
47
47
  # @param method_access [Boolean] If false, don't build method access on legacy {Query::Arguments} instances.
48
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, &definition_block)
48
+ # @param deprecation_reason [String]
49
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, deprecation_reason: nil, &definition_block)
49
50
  arg_name ||= name
50
51
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
51
52
  @type_expr = type_expr || type
@@ -60,6 +61,7 @@ module GraphQL
60
61
  @ast_node = ast_node
61
62
  @from_resolver = from_resolver
62
63
  @method_access = method_access
64
+ self.deprecation_reason = deprecation_reason
63
65
 
64
66
  if definition_block
65
67
  if definition_block.arity == 1
@@ -89,6 +91,18 @@ module GraphQL
89
91
  end
90
92
  end
91
93
 
94
+ # @return [String] Deprecation reason for this argument
95
+ def deprecation_reason(text = nil)
96
+ if text
97
+ validate_deprecated_or_optional(null: @null, deprecation_reason: text)
98
+ @deprecation_reason = text
99
+ else
100
+ @deprecation_reason
101
+ end
102
+ end
103
+
104
+ alias_method :deprecation_reason=, :deprecation_reason
105
+
92
106
  def visible?(context)
93
107
  true
94
108
  end
@@ -143,15 +157,32 @@ module GraphQL
143
157
  if NO_DEFAULT != @default_value
144
158
  argument.default_value = @default_value
145
159
  end
160
+ if @deprecation_reason
161
+ argument.deprecation_reason = @deprecation_reason
162
+ end
146
163
  argument
147
164
  end
148
165
 
149
- attr_writer :type
166
+ def type=(new_type)
167
+ validate_input_type(new_type)
168
+ # This isn't true for LateBoundTypes, but we can assume those will
169
+ # be updated via this codepath later in schema setup.
170
+ if new_type.respond_to?(:non_null?)
171
+ validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason)
172
+ end
173
+ @type = new_type
174
+ end
150
175
 
151
176
  def type
152
- @type ||= Member::BuildType.parse_type(@type_expr, null: @null)
153
- rescue StandardError => err
154
- raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
177
+ @type ||= begin
178
+ parsed_type = begin
179
+ Member::BuildType.parse_type(@type_expr, null: @null)
180
+ rescue StandardError => err
181
+ raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
182
+ end
183
+ # Use the setter method to get validations
184
+ self.type = parsed_type
185
+ end
155
186
  end
156
187
 
157
188
  def statically_coercible?
@@ -186,6 +217,26 @@ module GraphQL
186
217
  raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
187
218
  end
188
219
  end
220
+
221
+ private
222
+
223
+ def validate_input_type(input_type)
224
+ if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
225
+ # Do nothing; assume this will be validated later
226
+ elsif input_type.kind.non_null? || input_type.kind.list?
227
+ validate_input_type(input_type.unwrap)
228
+ elsif !input_type.kind.input?
229
+ raise ArgumentError, "Invalid input type for #{path}: #{input_type.graphql_name}. Must be scalar, enum, or input object, not #{input_type.kind.name}."
230
+ else
231
+ # It's an input type, we're OK
232
+ end
233
+ end
234
+
235
+ def validate_deprecated_or_optional(null:, deprecation_reason:)
236
+ if deprecation_reason && !null
237
+ raise ArgumentError, "Required arguments cannot be deprecated: #{path}."
238
+ end
239
+ end
189
240
  end
190
241
  end
191
242
  end
@@ -4,17 +4,24 @@ require "graphql/schema/build_from_definition/resolve_map"
4
4
  module GraphQL
5
5
  class Schema
6
6
  module BuildFromDefinition
7
+ if !String.method_defined?(:-@)
8
+ using GraphQL::StringDedupBackport
9
+ end
10
+
7
11
  class << self
8
12
  # @see {Schema.from_definition}
9
- def from_definition(definition_string, default_resolve:, using: {}, relay: false, interpreter: true, parser: DefaultParser)
10
- document = parser.parse(definition_string)
11
- default_resolve ||= {}
12
- Builder.build(document, default_resolve: default_resolve, relay: relay, using: using, interpreter: interpreter)
13
+ def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
14
+ from_document(parser.parse(definition_string), **kwargs)
13
15
  end
14
- end
15
16
 
16
- # @api private
17
- DefaultParser = GraphQL::Language::Parser
17
+ def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
18
+ from_document(parser.parse_file(definition_path), **kwargs)
19
+ end
20
+
21
+ def from_document(document, default_resolve:, using: {}, relay: false, interpreter: true)
22
+ Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using, interpreter: interpreter)
23
+ end
24
+ end
18
25
 
19
26
  # @api private
20
27
  module Builder
@@ -43,7 +50,7 @@ module GraphQL
43
50
  when GraphQL::Language::Nodes::EnumTypeDefinition
44
51
  types[definition.name] = build_enum_type(definition, type_resolver)
45
52
  when GraphQL::Language::Nodes::ObjectTypeDefinition
46
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
53
+ types[definition.name] = build_object_type(definition, type_resolver)
47
54
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
48
55
  types[definition.name] = build_interface_type(definition, type_resolver)
49
56
  when GraphQL::Language::Nodes::UnionTypeDefinition
@@ -111,11 +118,11 @@ module GraphQL
111
118
  end
112
119
 
113
120
  if default_resolve.respond_to?(:resolve_type)
114
- define_singleton_method(:resolve_type) do |*args|
115
- default_resolve.resolve_type(*args)
121
+ def self.resolve_type(*args)
122
+ self.definition_default_resolve.resolve_type(*args)
116
123
  end
117
124
  else
118
- define_singleton_method(:resolve_type) do |*args|
125
+ def self.resolve_type(*args)
119
126
  NullResolveType.call(*args)
120
127
  end
121
128
  end
@@ -141,6 +148,20 @@ module GraphQL
141
148
 
142
149
  # Empty `orphan_types` -- this will make unreachable types ... unreachable.
143
150
  own_orphan_types.clear
151
+
152
+ class << self
153
+ attr_accessor :definition_default_resolve
154
+ end
155
+
156
+ self.definition_default_resolve = default_resolve
157
+
158
+ def definition_default_resolve
159
+ self.class.definition_default_resolve
160
+ end
161
+
162
+ def self.inherited(child_class)
163
+ child_class.definition_default_resolve = self.definition_default_resolve
164
+ end
144
165
  end
145
166
  end
146
167
 
@@ -176,23 +197,27 @@ module GraphQL
176
197
  end
177
198
 
178
199
  def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
200
+ builder = self
179
201
  Class.new(GraphQL::Schema::Scalar) do
180
202
  graphql_name(scalar_type_definition.name)
181
203
  description(scalar_type_definition.description)
182
204
  ast_node(scalar_type_definition)
183
205
 
184
206
  if default_resolve.respond_to?(:coerce_input)
185
- define_singleton_method(:coerce_input) do |val, ctx|
186
- default_resolve.coerce_input(self, val, ctx)
187
- end
188
-
189
- define_singleton_method(:coerce_result) do |val, ctx|
190
- default_resolve.coerce_result(self, val, ctx)
191
- end
207
+ # Put these method definitions in another method to avoid retaining `type_resolve`
208
+ # from this method's bindiing
209
+ builder.build_scalar_type_coerce_method(self, :coerce_input, default_resolve)
210
+ builder.build_scalar_type_coerce_method(self, :coerce_result, default_resolve)
192
211
  end
193
212
  end
194
213
  end
195
214
 
215
+ def build_scalar_type_coerce_method(scalar_class, method_name, default_definition_resolve)
216
+ scalar_class.define_singleton_method(method_name) do |val, ctx|
217
+ default_definition_resolve.public_send(method_name, self, val, ctx)
218
+ end
219
+ end
220
+
196
221
  def build_union_type(union_type_definition, type_resolver)
197
222
  Class.new(GraphQL::Schema::Union) do
198
223
  graphql_name(union_type_definition.name)
@@ -202,13 +227,10 @@ module GraphQL
202
227
  end
203
228
  end
204
229
 
205
- def build_object_type(object_type_definition, type_resolver, default_resolve:)
230
+ def build_object_type(object_type_definition, type_resolver)
206
231
  builder = self
207
- type_def = nil
208
- typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
209
232
 
210
233
  Class.new(GraphQL::Schema::Object) do
211
- type_def = self
212
234
  graphql_name(object_type_definition.name)
213
235
  description(object_type_definition.description)
214
236
  ast_node(object_type_definition)
@@ -218,7 +240,7 @@ module GraphQL
218
240
  implements(interface_defn)
219
241
  end
220
242
 
221
- builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: typed_resolve_fn)
243
+ builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
222
244
  end
223
245
  end
224
246
 
@@ -247,13 +269,16 @@ module GraphQL
247
269
  end
248
270
  end
249
271
 
272
+ NO_DEFAULT_VALUE = {}.freeze
273
+
250
274
  def build_arguments(type_class, arguments, type_resolver)
251
275
  builder = self
252
276
 
253
277
  arguments.each do |argument_defn|
254
- default_value_kwargs = {}
255
- if !argument_defn.default_value.nil?
256
- default_value_kwargs[:default_value] = builder.build_default_value(argument_defn.default_value)
278
+ default_value_kwargs = if !argument_defn.default_value.nil?
279
+ { default_value: builder.build_default_value(argument_defn.default_value) }
280
+ else
281
+ NO_DEFAULT_VALUE
257
282
  end
258
283
 
259
284
  type_class.argument(
@@ -261,6 +286,7 @@ module GraphQL
261
286
  type: type_resolver.call(argument_defn.type),
262
287
  required: false,
263
288
  description: argument_defn.description,
289
+ deprecation_reason: builder.build_deprecation_reason(argument_defn.directives),
264
290
  ast_node: argument_defn,
265
291
  camelize: false,
266
292
  method_access: false,
@@ -295,10 +321,10 @@ module GraphQL
295
321
  def build_fields(owner, field_definitions, type_resolver, default_resolve:)
296
322
  builder = self
297
323
 
298
- field_definitions.map do |field_definition|
324
+ field_definitions.each do |field_definition|
299
325
  type_name = resolve_type_name(field_definition.type)
300
- resolve_method_name = "resolve_field_#{field_definition.name}"
301
- owner.field(
326
+ resolve_method_name = -"resolve_field_#{field_definition.name}"
327
+ schema_field_defn = owner.field(
302
328
  field_definition.name,
303
329
  description: field_definition.description,
304
330
  type: type_resolver.call(field_definition.type),
@@ -310,16 +336,19 @@ module GraphQL
310
336
  method_conflict_warning: false,
311
337
  camelize: false,
312
338
  resolver_method: resolve_method_name,
313
- ) do
314
- builder.build_arguments(self, field_definition.arguments, type_resolver)
315
-
316
- # Don't do this for interfaces
317
- if default_resolve
318
- owner.send(:define_method, resolve_method_name) do |**args|
319
- field_instance = self.class.get_field(field_definition.name)
320
- default_resolve.call(field_instance, object, args, context)
339
+ )
340
+
341
+ builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver)
342
+
343
+ # Don't do this for interfaces
344
+ if default_resolve
345
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
346
+ # frozen_string_literal: true
347
+ def #{resolve_method_name}(**args)
348
+ field_instance = self.class.get_field("#{field_definition.name}")
349
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
321
350
  end
322
- end
351
+ RUBY
323
352
  end
324
353
  end
325
354
  end
@@ -45,8 +45,10 @@ module GraphQL
45
45
  @resolve_hash[type_name_s][field_name.to_s] = resolve_fn
46
46
  end
47
47
  when Proc
48
- # for example, __resolve_type
48
+ # for example, "resolve_type"
49
49
  @resolve_hash[type_name_s] = fields
50
+ else
51
+ raise ArgumentError, "Unexpected resolve hash value for #{type_name.inspect}: #{fields.inspect} (#{fields.class})"
50
52
  end
51
53
  end
52
54
 
@@ -4,7 +4,7 @@ module GraphQL
4
4
  class Directive < GraphQL::Schema::Member
5
5
  class Deprecated < GraphQL::Schema::Directive
6
6
  description "Marks an element of a GraphQL schema as no longer supported."
7
- locations(GraphQL::Schema::Directive::FIELD_DEFINITION, GraphQL::Schema::Directive::ENUM_VALUE)
7
+ locations(GraphQL::Schema::Directive::FIELD_DEFINITION, GraphQL::Schema::Directive::ENUM_VALUE, GraphQL::Schema::Directive::ARGUMENT_DEFINITION, GraphQL::Schema::Directive::INPUT_FIELD_DEFINITION)
8
8
 
9
9
  reason_description = "Explains why this element was deprecated, usually also including a "\
10
10
  "suggestion for how to access supported similar data. Formatted "\
@@ -41,6 +41,7 @@ module GraphQL
41
41
 
42
42
  def initialize(graphql_name, desc = nil, owner:, ast_node: nil, description: nil, value: nil, deprecation_reason: nil, &block)
43
43
  @graphql_name = graphql_name.to_s
44
+ GraphQL::NameValidator.validate!(@graphql_name)
44
45
  @description = desc || description
45
46
  @value = value.nil? ? @graphql_name : value
46
47
  @deprecation_reason = deprecation_reason
@@ -203,7 +203,7 @@ module GraphQL
203
203
  # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
204
204
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
205
205
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
206
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], 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: nil, arguments: EMPTY_HASH, &definition_block)
206
+ def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, &definition_block)
207
207
  if name.nil?
208
208
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
209
209
  end
@@ -250,7 +250,7 @@ module GraphQL
250
250
  method_name = method || hash_key || name_s
251
251
  resolver_method ||= name_s.to_sym
252
252
 
253
- @method_str = method_name.to_s
253
+ @method_str = -method_name.to_s
254
254
  @method_sym = method_name.to_sym
255
255
  @resolver_method = resolver_method
256
256
  @complexity = complexity
@@ -274,7 +274,7 @@ module GraphQL
274
274
  if arg.is_a?(Hash)
275
275
  argument(name: name, **arg)
276
276
  else
277
- own_arguments[name] = arg
277
+ add_argument(arg)
278
278
  end
279
279
  end
280
280
 
@@ -282,7 +282,7 @@ module GraphQL
282
282
  @subscription_scope = subscription_scope
283
283
 
284
284
  # Do this last so we have as much context as possible when initializing them:
285
- @extensions = []
285
+ @extensions = EMPTY_ARRAY
286
286
  if extensions.any?
287
287
  self.extensions(extensions)
288
288
  end
@@ -343,6 +343,9 @@ module GraphQL
343
343
  # Read the value
344
344
  @extensions
345
345
  else
346
+ if @extensions.frozen?
347
+ @extensions = @extensions.dup
348
+ end
346
349
  new_extensions.each do |extension|
347
350
  if extension.is_a?(Hash)
348
351
  extension = extension.to_a[0]
@@ -380,6 +383,9 @@ module GraphQL
380
383
  # Read the value
381
384
  @extras
382
385
  else
386
+ if @extras.frozen?
387
+ @extras = @extras.dup
388
+ end
383
389
  # Append to the set of extras on this field
384
390
  @extras.concat(new_extras)
385
391
  end
@@ -719,20 +725,21 @@ module GraphQL
719
725
  if @extensions.empty?
720
726
  yield(obj, args)
721
727
  else
722
- # Save these so that the originals can be re-given to `after_resolve` handlers.
723
- original_args = args
724
- original_obj = obj
728
+ extended_obj = obj
729
+ extended_args = args
725
730
 
726
731
  memos = []
727
- value = run_extensions_before_resolve(memos, obj, args, ctx) do |extended_obj, extended_args|
728
- yield(extended_obj, extended_args)
732
+ value = run_extensions_before_resolve(memos, obj, args, ctx) do |obj, args|
733
+ extended_obj = obj
734
+ extended_args = args
735
+ yield(obj, args)
729
736
  end
730
737
 
731
738
  ctx.schema.after_lazy(value) do |resolved_value|
732
739
  @extensions.each_with_index do |ext, idx|
733
740
  memo = memos[idx]
734
741
  # TODO after_lazy?
735
- resolved_value = ext.after_resolve(object: original_obj, arguments: original_args, context: ctx, value: resolved_value, memo: memo)
742
+ resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
736
743
  end
737
744
  resolved_value
738
745
  end
@@ -18,44 +18,54 @@ module GraphQL
18
18
  next_args.delete(:last)
19
19
  next_args.delete(:before)
20
20
  next_args.delete(:after)
21
- yield(object, next_args)
21
+ yield(object, next_args, arguments)
22
22
  end
23
23
 
24
24
  def after_resolve(value:, object:, arguments:, context:, memo:)
25
- if value.is_a? GraphQL::ExecutionError
26
- # This isn't even going to work because context doesn't have ast_node anymore
27
- context.add_error(value)
28
- nil
29
- elsif value.nil?
30
- nil
31
- elsif value.is_a?(GraphQL::Pagination::Connection)
32
- # update the connection with some things that may not have been provided
33
- value.context ||= context
34
- value.parent ||= object.object
35
- value.first_value ||= arguments[:first]
36
- value.after_value ||= arguments[:after]
37
- value.last_value ||= arguments[:last]
38
- value.before_value ||= arguments[:before]
39
- if field.has_max_page_size? && !value.has_max_page_size_override?
40
- value.max_page_size = field.max_page_size
25
+ original_arguments = memo
26
+ # rename some inputs to avoid conflicts inside the block
27
+ maybe_lazy = value
28
+ value = nil
29
+ context.schema.after_lazy(maybe_lazy) do |resolved_value|
30
+ value = resolved_value
31
+ if value.is_a? GraphQL::ExecutionError
32
+ # This isn't even going to work because context doesn't have ast_node anymore
33
+ context.add_error(value)
34
+ nil
35
+ elsif value.nil?
36
+ nil
37
+ elsif value.is_a?(GraphQL::Pagination::Connection)
38
+ # update the connection with some things that may not have been provided
39
+ value.context ||= context
40
+ value.parent ||= object.object
41
+ value.first_value ||= original_arguments[:first]
42
+ value.after_value ||= original_arguments[:after]
43
+ value.last_value ||= original_arguments[:last]
44
+ value.before_value ||= original_arguments[:before]
45
+ if field.has_max_page_size? && !value.has_max_page_size_override?
46
+ value.max_page_size = field.max_page_size
47
+ end
48
+ if context.schema.new_connections? && (custom_t = context.schema.connections.edge_class_for_field(@field))
49
+ value.edge_class = custom_t
50
+ end
51
+ value
52
+ elsif context.schema.new_connections?
53
+ wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
54
+ context.schema.connections.wrap(field, object.object, value, original_arguments, context, wrappers: wrappers)
55
+ else
56
+ if object.is_a?(GraphQL::Schema::Object)
57
+ object = object.object
58
+ end
59
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
60
+ connection_class.new(
61
+ value,
62
+ original_arguments,
63
+ field: field,
64
+ max_page_size: field.max_page_size,
65
+ parent: object,
66
+ context: context,
67
+ )
41
68
  end
42
- value
43
- elsif context.schema.new_connections?
44
- wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
45
- context.schema.connections.wrap(field, object.object, value, arguments, context, wrappers: wrappers)
46
- else
47
- if object.is_a?(GraphQL::Schema::Object)
48
- object = object.object
49
- end
50
- connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
51
- connection_class.new(
52
- value,
53
- arguments,
54
- field: field,
55
- max_page_size: field.max_page_size,
56
- parent: object,
57
- context: context,
58
- )
59
69
  end
60
70
  end
61
71
  end