graphql 1.11.8 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +5 -5
  3. data/lib/generators/graphql/relay_generator.rb +63 -0
  4. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  5. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  6. data/lib/generators/graphql/templates/node_type.erb +9 -0
  7. data/lib/generators/graphql/templates/object.erb +1 -1
  8. data/lib/generators/graphql/templates/query_type.erb +1 -3
  9. data/lib/generators/graphql/templates/schema.erb +8 -35
  10. data/lib/graphql.rb +38 -4
  11. data/lib/graphql/analysis/analyze_query.rb +7 -0
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  14. data/lib/graphql/backtrace.rb +28 -19
  15. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  16. data/lib/graphql/backtrace/table.rb +22 -2
  17. data/lib/graphql/backtrace/tracer.rb +40 -9
  18. data/lib/graphql/backwards_compatibility.rb +1 -0
  19. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  20. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  21. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  22. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  23. data/lib/graphql/dataloader.rb +197 -0
  24. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  25. data/lib/graphql/dataloader/request.rb +24 -0
  26. data/lib/graphql/dataloader/request_all.rb +22 -0
  27. data/lib/graphql/dataloader/source.rb +93 -0
  28. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  29. data/lib/graphql/define/instance_definable.rb +32 -2
  30. data/lib/graphql/define/type_definer.rb +5 -5
  31. data/lib/graphql/deprecated_dsl.rb +5 -0
  32. data/lib/graphql/enum_type.rb +2 -0
  33. data/lib/graphql/execution/errors.rb +4 -0
  34. data/lib/graphql/execution/execute.rb +7 -0
  35. data/lib/graphql/execution/interpreter.rb +10 -6
  36. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  37. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  38. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  39. data/lib/graphql/execution/multiplex.rb +20 -6
  40. data/lib/graphql/function.rb +4 -0
  41. data/lib/graphql/input_object_type.rb +2 -0
  42. data/lib/graphql/interface_type.rb +3 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  44. data/lib/graphql/object_type.rb +2 -0
  45. data/lib/graphql/pagination/connection.rb +5 -1
  46. data/lib/graphql/pagination/connections.rb +6 -16
  47. data/lib/graphql/query.rb +2 -0
  48. data/lib/graphql/query/context.rb +4 -0
  49. data/lib/graphql/query/serial_execution.rb +1 -0
  50. data/lib/graphql/relay/base_connection.rb +7 -0
  51. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  52. data/lib/graphql/relay/connection_type.rb +1 -1
  53. data/lib/graphql/relay/mutation.rb +1 -0
  54. data/lib/graphql/relay/node.rb +3 -0
  55. data/lib/graphql/relay/type_extensions.rb +2 -0
  56. data/lib/graphql/scalar_type.rb +2 -0
  57. data/lib/graphql/schema.rb +69 -32
  58. data/lib/graphql/schema/argument.rb +25 -7
  59. data/lib/graphql/schema/build_from_definition.rb +139 -51
  60. data/lib/graphql/schema/directive.rb +76 -0
  61. data/lib/graphql/schema/directive/flagged.rb +57 -0
  62. data/lib/graphql/schema/enum.rb +3 -0
  63. data/lib/graphql/schema/enum_value.rb +12 -6
  64. data/lib/graphql/schema/field.rb +28 -9
  65. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  66. data/lib/graphql/schema/input_object.rb +33 -22
  67. data/lib/graphql/schema/interface.rb +1 -0
  68. data/lib/graphql/schema/member.rb +4 -0
  69. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  70. data/lib/graphql/schema/member/build_type.rb +3 -3
  71. data/lib/graphql/schema/member/has_arguments.rb +24 -6
  72. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  73. data/lib/graphql/schema/member/has_directives.rb +98 -0
  74. data/lib/graphql/schema/member/has_validators.rb +31 -0
  75. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  76. data/lib/graphql/schema/object.rb +11 -0
  77. data/lib/graphql/schema/printer.rb +5 -4
  78. data/lib/graphql/schema/resolver.rb +7 -0
  79. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  80. data/lib/graphql/schema/subscription.rb +19 -1
  81. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  82. data/lib/graphql/schema/validation.rb +2 -0
  83. data/lib/graphql/schema/validator.rb +163 -0
  84. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  85. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  86. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  87. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  88. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  89. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  90. data/lib/graphql/static_validation/validator.rb +2 -0
  91. data/lib/graphql/subscriptions.rb +17 -20
  92. data/lib/graphql/tracing.rb +2 -2
  93. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  94. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  95. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  96. data/lib/graphql/types/relay.rb +11 -3
  97. data/lib/graphql/types/relay/base_connection.rb +2 -92
  98. data/lib/graphql/types/relay/base_edge.rb +2 -35
  99. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  100. data/lib/graphql/types/relay/default_relay.rb +27 -0
  101. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  102. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  103. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  104. data/lib/graphql/types/relay/node.rb +2 -4
  105. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  106. data/lib/graphql/types/relay/node_field.rb +1 -19
  107. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  108. data/lib/graphql/types/relay/page_info.rb +2 -14
  109. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  110. data/lib/graphql/union_type.rb +2 -0
  111. data/lib/graphql/upgrader/member.rb +1 -0
  112. data/lib/graphql/upgrader/schema.rb +1 -0
  113. data/lib/graphql/version.rb +1 -1
  114. metadata +34 -9
  115. data/lib/graphql/types/relay/base_field.rb +0 -22
  116. data/lib/graphql/types/relay/base_interface.rb +0 -29
  117. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -9,6 +9,13 @@ module GraphQL
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
11
  class << self
12
+ # Directives aren't types, they don't have kinds.
13
+ undef_method :kind
14
+
15
+ def path
16
+ "@#{super}"
17
+ end
18
+
12
19
  # Return a name based on the class name,
13
20
  # but downcase the first letter.
14
21
  def default_graphql_name
@@ -21,6 +28,11 @@ module GraphQL
21
28
 
22
29
  def locations(*new_locations)
23
30
  if new_locations.any?
31
+ new_locations.each do |new_loc|
32
+ if !LOCATIONS.include?(new_loc.to_sym)
33
+ raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
34
+ end
35
+ end
24
36
  @locations = new_locations
25
37
  else
26
38
  @locations ||= (superclass.respond_to?(:locations) ? superclass.locations : [])
@@ -87,6 +99,23 @@ module GraphQL
87
99
  end
88
100
  end
89
101
 
102
+ # @return [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class, Module]
103
+ attr_reader :owner
104
+
105
+ # @return [GraphQL::Interpreter::Arguments]
106
+ attr_reader :arguments
107
+
108
+ def initialize(owner, **arguments)
109
+ @owner = owner
110
+ assert_valid_owner
111
+ # It's be nice if we had the real context here, but we don't. What we _would_ get is:
112
+ # - error handling
113
+ # - lazy resolution
114
+ # Probably, those won't be needed here, since these are configuration arguments,
115
+ # not runtime arguments.
116
+ @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
117
+ end
118
+
90
119
  LOCATIONS = [
91
120
  QUERY = :QUERY,
92
121
  MUTATION = :MUTATION,
@@ -129,6 +158,53 @@ module GraphQL
129
158
  INPUT_OBJECT: 'Location adjacent to an input object type definition.',
130
159
  INPUT_FIELD_DEFINITION: 'Location adjacent to an input object field definition.',
131
160
  }
161
+
162
+ private
163
+
164
+ def assert_valid_owner
165
+ case @owner
166
+ when Class
167
+ if @owner < GraphQL::Schema::Object
168
+ assert_has_location(OBJECT)
169
+ elsif @owner < GraphQL::Schema::Union
170
+ assert_has_location(UNION)
171
+ elsif @owner < GraphQL::Schema::Enum
172
+ assert_has_location(ENUM)
173
+ elsif @owner < GraphQL::Schema::InputObject
174
+ assert_has_location(INPUT_OBJECT)
175
+ elsif @owner < GraphQL::Schema::Scalar
176
+ assert_has_location(SCALAR)
177
+ elsif @owner < GraphQL::Schema
178
+ assert_has_location(SCHEMA)
179
+ else
180
+ raise "Unexpected directive owner class: #{@owner}"
181
+ end
182
+ when Module
183
+ assert_has_location(INTERFACE)
184
+ when GraphQL::Schema::Argument
185
+ if @owner.owner.is_a?(GraphQL::Schema::Field)
186
+ assert_has_location(ARGUMENT_DEFINITION)
187
+ else
188
+ assert_has_location(INPUT_FIELD_DEFINITION)
189
+ end
190
+ when GraphQL::Schema::Field
191
+ assert_has_location(FIELD_DEFINITION)
192
+ when GraphQL::Schema::EnumValue
193
+ assert_has_location(ENUM_VALUE)
194
+ else
195
+ raise "Unexpected directive owner: #{@owner.inspect}"
196
+ end
197
+ end
198
+
199
+ def assert_has_location(location)
200
+ if !self.class.locations.include?(location)
201
+ raise ArgumentError, <<-MD
202
+ Directive `@#{self.class.graphql_name}` can't be attached to #{@owner.graphql_name} because #{location} isn't included in its locations (#{self.class.locations.join(", ")}).
203
+
204
+ Use `locations(#{location})` to update this directive's definition, or remove it from #{@owner.graphql_name}.
205
+ MD
206
+ end
207
+ end
132
208
  end
133
209
  end
134
210
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ # This is _similar_ to {Directive::Feature}, except it's prescribed by the server, not the client.
6
+ #
7
+ # In this case, the server hides types and fields _entirely_, unless the current context has certain `:flags` present.
8
+ class Flagged < GraphQL::Schema::Directive
9
+ def initialize(target, **options)
10
+ if target.is_a?(Module) && !target.ancestors.include?(VisibleByFlag)
11
+ # This is type class of some kind, `include` will put this module
12
+ # in between the type class itself and its super class, so `super` will work fine
13
+ target.include(VisibleByFlag)
14
+ elsif !target.is_a?(VisibleByFlag)
15
+ # This is an instance of a base class. `include` won't put this in front of the
16
+ # base class implementation, so we need to `.prepend`.
17
+ # `#visible?` could probably be moved to a module and then this could use `include` instead.
18
+ target.class.prepend(VisibleByFlag)
19
+ end
20
+ super
21
+ end
22
+
23
+ description "Hides this part of the schema unless the named flag is present in context[:flags]"
24
+
25
+ locations(
26
+ GraphQL::Schema::Directive::FIELD_DEFINITION,
27
+ GraphQL::Schema::Directive::OBJECT,
28
+ GraphQL::Schema::Directive::SCALAR,
29
+ GraphQL::Schema::Directive::ENUM,
30
+ GraphQL::Schema::Directive::UNION,
31
+ GraphQL::Schema::Directive::INTERFACE,
32
+ GraphQL::Schema::Directive::INPUT_OBJECT,
33
+ GraphQL::Schema::Directive::ENUM_VALUE,
34
+ GraphQL::Schema::Directive::ARGUMENT_DEFINITION,
35
+ GraphQL::Schema::Directive::INPUT_FIELD_DEFINITION,
36
+ )
37
+
38
+ argument :by, [String], "Flags to check for this schema member", required: true
39
+
40
+ module VisibleByFlag
41
+ def self.included(schema_class)
42
+ schema_class.extend(self)
43
+ end
44
+
45
+ def visible?(context)
46
+ if dir = self.directives.find { |d| d.is_a?(Flagged) }
47
+ relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f
48
+ relevant_flags && relevant_flags.any? && super
49
+ else
50
+ super
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -37,6 +37,9 @@ module GraphQL
37
37
  def value(*args, **kwargs, &block)
38
38
  kwargs[:owner] = self
39
39
  value = enum_value_class.new(*args, **kwargs, &block)
40
+ if own_values.key?(value.graphql_name)
41
+ raise ArgumentError, "#{value.graphql_name} is already defined for #{self.graphql_name}, please remove one of the definitions."
42
+ end
40
43
  own_values[value.graphql_name] = value
41
44
  nil
42
45
  end
@@ -30,23 +30,29 @@ module GraphQL
30
30
  include GraphQL::Schema::Member::AcceptsDefinition
31
31
  include GraphQL::Schema::Member::HasPath
32
32
  include GraphQL::Schema::Member::HasAstNode
33
+ include GraphQL::Schema::Member::HasDirectives
34
+ include GraphQL::Schema::Member::HasDeprecationReason
33
35
 
34
36
  attr_reader :graphql_name
35
37
 
36
38
  # @return [Class] The enum type that owns this value
37
39
  attr_reader :owner
38
40
 
39
- # @return [String] Explains why this value was deprecated (if present, this will be marked deprecated in introspection)
40
- attr_accessor :deprecation_reason
41
-
42
- def initialize(graphql_name, desc = nil, owner:, ast_node: nil, description: nil, value: nil, deprecation_reason: nil, &block)
41
+ def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, value: nil, deprecation_reason: nil, &block)
43
42
  @graphql_name = graphql_name.to_s
44
43
  GraphQL::NameValidator.validate!(@graphql_name)
45
44
  @description = desc || description
46
45
  @value = value.nil? ? @graphql_name : value
47
- @deprecation_reason = deprecation_reason
46
+ if deprecation_reason
47
+ self.deprecation_reason = deprecation_reason
48
+ end
48
49
  @owner = owner
49
50
  @ast_node = ast_node
51
+ if directives
52
+ directives.each do |dir_class, dir_options|
53
+ directive(dir_class, **dir_options)
54
+ end
55
+ end
50
56
 
51
57
  if block_given?
52
58
  instance_eval(&block)
@@ -73,7 +79,7 @@ module GraphQL
73
79
  enum_value.name = @graphql_name
74
80
  enum_value.description = @description
75
81
  enum_value.value = @value
76
- enum_value.deprecation_reason = @deprecation_reason
82
+ enum_value.deprecation_reason = self.deprecation_reason
77
83
  enum_value.metadata[:type_class] = self
78
84
  enum_value.ast_node = ast_node
79
85
  enum_value
@@ -15,8 +15,11 @@ module GraphQL
15
15
  include GraphQL::Schema::Member::HasArguments
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasPath
18
+ include GraphQL::Schema::Member::HasValidators
18
19
  extend GraphQL::Schema::FindInheritedValue
19
20
  include GraphQL::Schema::FindInheritedValue::EmptyObjects
21
+ include GraphQL::Schema::Member::HasDirectives
22
+ include GraphQL::Schema::Member::HasDeprecationReason
20
23
 
21
24
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
22
25
  attr_reader :name
@@ -24,9 +27,6 @@ module GraphQL
24
27
 
25
28
  attr_writer :description
26
29
 
27
- # @return [String, nil] If present, the field is marked as deprecated with this documentation
28
- attr_accessor :deprecation_reason
29
-
30
30
  # @return [Symbol] Method or hash key on the underlying object to look up
31
31
  attr_reader :method_sym
32
32
 
@@ -82,10 +82,10 @@ module GraphQL
82
82
  # @see {.initialize} for other options
83
83
  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
84
84
  if kwargs[:field]
85
- if kwargs[:field] == GraphQL::Relay::Node.field
85
+ if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
86
86
  warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
87
87
  return GraphQL::Types::Relay::NodeField
88
- elsif kwargs[:field] == GraphQL::Relay::Node.plural_field
88
+ elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
89
89
  warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
90
90
  return GraphQL::Types::Relay::NodesField
91
91
  end
@@ -199,11 +199,14 @@ module GraphQL
199
199
  # @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
200
200
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
201
201
  # @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
202
+ # @param directives [Hash{Class => Hash}] Directives to apply to this field
202
203
  # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
203
204
  # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
204
205
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
205
206
  # @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: 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
+ # @param validates [Array<Hash>] Configurations for validating this field
208
+ # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
209
+ 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, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
207
210
  if name.nil?
208
211
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
209
212
  end
@@ -230,7 +233,7 @@ module GraphQL
230
233
  end
231
234
  @function = function
232
235
  @resolve = resolve
233
- @deprecation_reason = deprecation_reason
236
+ self.deprecation_reason = deprecation_reason
234
237
 
235
238
  if method && hash_key
236
239
  raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
@@ -269,6 +272,7 @@ module GraphQL
269
272
  @relay_nodes_field = relay_nodes_field
270
273
  @ast_node = ast_node
271
274
  @method_conflict_warning = method_conflict_warning
275
+ @legacy_edge_class = legacy_edge_class
272
276
 
273
277
  arguments.each do |name, arg|
274
278
  if arg.is_a?(Hash)
@@ -297,6 +301,14 @@ module GraphQL
297
301
  self.extension(connection_extension)
298
302
  end
299
303
 
304
+ if directives.any?
305
+ directives.each do |(dir_class, options)|
306
+ self.directive(dir_class, **options)
307
+ end
308
+ end
309
+
310
+ self.validates(validates)
311
+
300
312
  if definition_block
301
313
  if definition_block.arity == 1
302
314
  yield self
@@ -438,8 +450,8 @@ module GraphQL
438
450
  field_defn.description = @description
439
451
  end
440
452
 
441
- if @deprecation_reason
442
- field_defn.deprecation_reason = @deprecation_reason
453
+ if self.deprecation_reason
454
+ field_defn.deprecation_reason = self.deprecation_reason
443
455
  end
444
456
 
445
457
  if @resolver_class
@@ -461,6 +473,10 @@ module GraphQL
461
473
  field_defn.relay_nodes_field = @relay_nodes_field
462
474
  end
463
475
 
476
+ if @legacy_edge_class
477
+ field_defn.edge_class = @legacy_edge_class
478
+ end
479
+
464
480
  field_defn.resolve = self.method(:resolve_field)
465
481
  field_defn.connection = connection?
466
482
  field_defn.connection_max_page_size = max_page_size
@@ -580,6 +596,9 @@ module GraphQL
580
596
  begin
581
597
  # Unwrap the GraphQL object to get the application object.
582
598
  application_object = object.object
599
+
600
+ Schema::Validator.validate!(validators, application_object, ctx, args)
601
+
583
602
  ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
584
603
  if is_authorized
585
604
  public_send_field(object, args, ctx)
@@ -42,6 +42,7 @@ module GraphQL
42
42
  value.after_value ||= original_arguments[:after]
43
43
  value.last_value ||= original_arguments[:last]
44
44
  value.before_value ||= original_arguments[:before]
45
+ value.field ||= field
45
46
  if field.has_max_page_size? && !value.has_max_page_size_override?
46
47
  value.max_page_size = field.max_page_size
47
48
  end
@@ -50,8 +51,8 @@ module GraphQL
50
51
  end
51
52
  value
52
53
  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)
54
+ context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
55
+ context.schema.connections.wrap(field, object.object, value, original_arguments, context)
55
56
  else
56
57
  if object.is_a?(GraphQL::Schema::Object)
57
58
  object = object.object
@@ -7,6 +7,7 @@ module GraphQL
7
7
  extend GraphQL::Schema::Member::HasArguments
8
8
  extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader
9
9
  extend GraphQL::Schema::Member::ValidatesInput
10
+ extend GraphQL::Schema::Member::HasValidators
10
11
 
11
12
  include GraphQL::Dig
12
13
 
@@ -37,14 +38,15 @@ module GraphQL
37
38
  load_application_object(arg_defn, loads, value, context)
38
39
  end
39
40
  maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
40
- @ruby_style_hash[ruby_kwargs_key] = loaded_value
41
+ overwrite_argument(ruby_kwargs_key, loaded_value)
41
42
  end
42
43
  end
43
44
 
44
45
  # Weirdly, procs are applied during coercion, but not methods.
45
46
  # Probably because these methods require a `self`.
46
47
  if arg_defn.prepare.is_a?(Symbol) || context.nil? || !context.interpreter?
47
- @ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
48
+ prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
49
+ overwrite_argument(ruby_kwargs_key, prepared_value)
48
50
  end
49
51
  end
50
52
  end
@@ -74,6 +76,9 @@ module GraphQL
74
76
  def prepare
75
77
  if context
76
78
  context.schema.after_any_lazies(@maybe_lazies) do
79
+ object = context[:current_object]
80
+ # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
81
+ Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
77
82
  self
78
83
  end
79
84
  else
@@ -168,17 +173,10 @@ module GraphQL
168
173
  return result
169
174
  end
170
175
 
171
- input = begin
172
- input.to_h
173
- rescue
174
- begin
175
- # Handle ActionController::Parameters:
176
- input.to_unsafe_h
177
- rescue
178
- # We're not sure it'll act like a hash, so reject it:
179
- result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
180
- return result
181
- end
176
+ if !(input.respond_to?(:to_h) || input.respond_to?(:to_unsafe_h))
177
+ # We're not sure it'll act like a hash, so reject it:
178
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
179
+ return result
182
180
  end
183
181
 
184
182
  # Inject missing required arguments
@@ -190,16 +188,19 @@ module GraphQL
190
188
  m
191
189
  end
192
190
 
193
- input.merge(missing_required_inputs).each do |argument_name, value|
194
- argument = warden.get_argument(self, argument_name)
195
- # Items in the input that are unexpected
196
- unless argument
197
- result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
198
- next
191
+
192
+ [input, missing_required_inputs].each do |args_to_validate|
193
+ args_to_validate.each do |argument_name, value|
194
+ argument = warden.get_argument(self, argument_name)
195
+ # Items in the input that are unexpected
196
+ unless argument
197
+ result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
198
+ next
199
+ end
200
+ # Items in the input that are expected, but have invalid values
201
+ argument_result = argument.type.validate_input(value, ctx)
202
+ result.merge_result!(argument_name, argument_result) unless argument_result.valid?
199
203
  end
200
- # Items in the input that are expected, but have invalid values
201
- argument_result = argument.type.validate_input(value, ctx)
202
- result.merge_result!(argument_name, argument_result) unless argument_result.valid?
203
204
  end
204
205
 
205
206
  result
@@ -240,6 +241,16 @@ module GraphQL
240
241
  result
241
242
  end
242
243
  end
244
+
245
+ private
246
+
247
+ def overwrite_argument(key, value)
248
+ # Argument keywords come in frozen from the interpreter, dup them before modifying them.
249
+ if @ruby_style_hash.frozen?
250
+ @ruby_style_hash = @ruby_style_hash.dup
251
+ end
252
+ @ruby_style_hash[key] = value
253
+ end
243
254
  end
244
255
  end
245
256
  end