graphql 1.11.1 → 1.11.6

Sign up to get free protection for your applications and to get access to all the features.
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