graphql 1.11.7 → 1.12.4

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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +7 -5
  3. data/lib/generators/graphql/relay.rb +55 -0
  4. data/lib/generators/graphql/relay_generator.rb +20 -0
  5. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  6. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  7. data/lib/generators/graphql/templates/node_type.erb +9 -0
  8. data/lib/generators/graphql/templates/object.erb +1 -1
  9. data/lib/generators/graphql/templates/query_type.erb +1 -3
  10. data/lib/generators/graphql/templates/schema.erb +8 -35
  11. data/lib/graphql.rb +38 -4
  12. data/lib/graphql/analysis/analyze_query.rb +7 -0
  13. data/lib/graphql/analysis/ast.rb +11 -2
  14. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  15. data/lib/graphql/backtrace.rb +28 -19
  16. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  17. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  18. data/lib/graphql/backtrace/table.rb +22 -3
  19. data/lib/graphql/backtrace/traced_error.rb +0 -1
  20. data/lib/graphql/backtrace/tracer.rb +37 -10
  21. data/lib/graphql/backwards_compatibility.rb +2 -1
  22. data/lib/graphql/base_type.rb +1 -1
  23. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  24. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  25. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  26. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  27. data/lib/graphql/dataloader.rb +208 -0
  28. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  29. data/lib/graphql/dataloader/request.rb +19 -0
  30. data/lib/graphql/dataloader/request_all.rb +19 -0
  31. data/lib/graphql/dataloader/source.rb +107 -0
  32. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  33. data/lib/graphql/define/instance_definable.rb +32 -2
  34. data/lib/graphql/define/type_definer.rb +5 -5
  35. data/lib/graphql/deprecated_dsl.rb +7 -2
  36. data/lib/graphql/deprecation.rb +13 -0
  37. data/lib/graphql/enum_type.rb +2 -0
  38. data/lib/graphql/execution/errors.rb +4 -0
  39. data/lib/graphql/execution/execute.rb +7 -0
  40. data/lib/graphql/execution/interpreter.rb +11 -7
  41. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  42. data/lib/graphql/execution/interpreter/arguments_cache.rb +37 -14
  43. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  44. data/lib/graphql/execution/interpreter/resolve.rb +33 -25
  45. data/lib/graphql/execution/interpreter/runtime.rb +173 -123
  46. data/lib/graphql/execution/multiplex.rb +36 -23
  47. data/lib/graphql/function.rb +4 -0
  48. data/lib/graphql/input_object_type.rb +2 -0
  49. data/lib/graphql/interface_type.rb +3 -1
  50. data/lib/graphql/internal_representation/document.rb +2 -2
  51. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  52. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  53. data/lib/graphql/object_type.rb +2 -2
  54. data/lib/graphql/pagination/connection.rb +5 -1
  55. data/lib/graphql/pagination/connections.rb +6 -16
  56. data/lib/graphql/parse_error.rb +0 -1
  57. data/lib/graphql/query.rb +10 -2
  58. data/lib/graphql/query/arguments.rb +1 -1
  59. data/lib/graphql/query/arguments_cache.rb +0 -1
  60. data/lib/graphql/query/context.rb +4 -2
  61. data/lib/graphql/query/executor.rb +0 -1
  62. data/lib/graphql/query/null_context.rb +3 -2
  63. data/lib/graphql/query/serial_execution.rb +1 -0
  64. data/lib/graphql/query/variable_validation_error.rb +1 -1
  65. data/lib/graphql/relay/base_connection.rb +7 -0
  66. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  67. data/lib/graphql/relay/connection_type.rb +1 -1
  68. data/lib/graphql/relay/mutation.rb +1 -0
  69. data/lib/graphql/relay/node.rb +3 -0
  70. data/lib/graphql/relay/type_extensions.rb +2 -0
  71. data/lib/graphql/scalar_type.rb +2 -0
  72. data/lib/graphql/schema.rb +64 -26
  73. data/lib/graphql/schema/argument.rb +86 -7
  74. data/lib/graphql/schema/build_from_definition.rb +139 -51
  75. data/lib/graphql/schema/directive.rb +76 -0
  76. data/lib/graphql/schema/directive/flagged.rb +57 -0
  77. data/lib/graphql/schema/enum.rb +3 -0
  78. data/lib/graphql/schema/enum_value.rb +12 -6
  79. data/lib/graphql/schema/field.rb +40 -16
  80. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  81. data/lib/graphql/schema/find_inherited_value.rb +3 -1
  82. data/lib/graphql/schema/input_object.rb +39 -24
  83. data/lib/graphql/schema/interface.rb +1 -0
  84. data/lib/graphql/schema/member.rb +4 -0
  85. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  86. data/lib/graphql/schema/member/build_type.rb +3 -3
  87. data/lib/graphql/schema/member/has_arguments.rb +54 -49
  88. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  89. data/lib/graphql/schema/member/has_directives.rb +98 -0
  90. data/lib/graphql/schema/member/has_fields.rb +1 -4
  91. data/lib/graphql/schema/member/has_validators.rb +31 -0
  92. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  93. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  94. data/lib/graphql/schema/middleware_chain.rb +1 -1
  95. data/lib/graphql/schema/object.rb +11 -0
  96. data/lib/graphql/schema/printer.rb +5 -4
  97. data/lib/graphql/schema/resolver.rb +7 -0
  98. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  99. data/lib/graphql/schema/subscription.rb +19 -1
  100. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  101. data/lib/graphql/schema/validation.rb +4 -2
  102. data/lib/graphql/schema/validator.rb +163 -0
  103. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  104. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  105. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  106. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  107. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  108. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  109. data/lib/graphql/static_validation/validator.rb +4 -0
  110. data/lib/graphql/subscriptions.rb +17 -20
  111. data/lib/graphql/subscriptions/event.rb +0 -1
  112. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  113. data/lib/graphql/subscriptions/serialize.rb +0 -1
  114. data/lib/graphql/subscriptions/subscription_root.rb +1 -1
  115. data/lib/graphql/tracing.rb +2 -2
  116. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  117. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  118. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  119. data/lib/graphql/types/relay.rb +11 -3
  120. data/lib/graphql/types/relay/base_connection.rb +2 -92
  121. data/lib/graphql/types/relay/base_edge.rb +2 -35
  122. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  123. data/lib/graphql/types/relay/default_relay.rb +27 -0
  124. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  125. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  126. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  127. data/lib/graphql/types/relay/node.rb +2 -4
  128. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  129. data/lib/graphql/types/relay/node_field.rb +1 -19
  130. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  131. data/lib/graphql/types/relay/page_info.rb +2 -14
  132. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  133. data/lib/graphql/union_type.rb +2 -0
  134. data/lib/graphql/upgrader/member.rb +1 -0
  135. data/lib/graphql/upgrader/schema.rb +1 -0
  136. data/lib/graphql/version.rb +1 -1
  137. metadata +50 -93
  138. data/lib/graphql/types/relay/base_field.rb +0 -22
  139. data/lib/graphql/types/relay/base_interface.rb +0 -29
  140. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -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
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../object.rb
3
2
  require "graphql/schema/field/connection_extension"
4
3
  require "graphql/schema/field/scope_extension"
5
4
 
@@ -15,8 +14,11 @@ module GraphQL
15
14
  include GraphQL::Schema::Member::HasArguments
16
15
  include GraphQL::Schema::Member::HasAstNode
17
16
  include GraphQL::Schema::Member::HasPath
17
+ include GraphQL::Schema::Member::HasValidators
18
18
  extend GraphQL::Schema::FindInheritedValue
19
19
  include GraphQL::Schema::FindInheritedValue::EmptyObjects
20
+ include GraphQL::Schema::Member::HasDirectives
21
+ include GraphQL::Schema::Member::HasDeprecationReason
20
22
 
21
23
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
22
24
  attr_reader :name
@@ -24,9 +26,6 @@ module GraphQL
24
26
 
25
27
  attr_writer :description
26
28
 
27
- # @return [String, nil] If present, the field is marked as deprecated with this documentation
28
- attr_accessor :deprecation_reason
29
-
30
29
  # @return [Symbol] Method or hash key on the underlying object to look up
31
30
  attr_reader :method_sym
32
31
 
@@ -61,6 +60,10 @@ module GraphQL
61
60
  @introspection
62
61
  end
63
62
 
63
+ def inspect
64
+ "#<#{self.class} #{path}#{arguments.any? ? "(...)" : ""}: #{type.to_type_signature}>"
65
+ end
66
+
64
67
  alias :mutation :resolver
65
68
 
66
69
  # @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
@@ -82,11 +85,11 @@ module GraphQL
82
85
  # @see {.initialize} for other options
83
86
  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
84
87
  if kwargs[:field]
85
- if kwargs[:field] == GraphQL::Relay::Node.field
86
- warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
88
+ if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
89
+ GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
87
90
  return GraphQL::Types::Relay::NodeField
88
- elsif kwargs[:field] == GraphQL::Relay::Node.plural_field
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.")
91
+ elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
92
+ GraphQL::Deprecation.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
93
  return GraphQL::Types::Relay::NodesField
91
94
  end
92
95
  end
@@ -199,11 +202,14 @@ module GraphQL
199
202
  # @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
200
203
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
201
204
  # @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
205
+ # @param directives [Hash{Class => Hash}] Directives to apply to this field
202
206
  # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
203
207
  # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
204
208
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
205
209
  # @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)
210
+ # @param validates [Array<Hash>] Configurations for validating this field
211
+ # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
212
+ 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
213
  if name.nil?
208
214
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
209
215
  end
@@ -230,7 +236,7 @@ module GraphQL
230
236
  end
231
237
  @function = function
232
238
  @resolve = resolve
233
- @deprecation_reason = deprecation_reason
239
+ self.deprecation_reason = deprecation_reason
234
240
 
235
241
  if method && hash_key
236
242
  raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
@@ -269,6 +275,7 @@ module GraphQL
269
275
  @relay_nodes_field = relay_nodes_field
270
276
  @ast_node = ast_node
271
277
  @method_conflict_warning = method_conflict_warning
278
+ @legacy_edge_class = legacy_edge_class
272
279
 
273
280
  arguments.each do |name, arg|
274
281
  if arg.is_a?(Hash)
@@ -281,22 +288,32 @@ module GraphQL
281
288
  @owner = owner
282
289
  @subscription_scope = subscription_scope
283
290
 
284
- # Do this last so we have as much context as possible when initializing them:
285
291
  @extensions = EMPTY_ARRAY
286
- if extensions.any?
287
- self.extensions(extensions)
288
- end
289
292
  # This should run before connection extension,
290
293
  # but should it run after the definition block?
291
294
  if scoped?
292
295
  self.extension(ScopeExtension)
293
296
  end
297
+
294
298
  # The problem with putting this after the definition_block
295
299
  # is that it would override arguments
296
300
  if connection? && connection_extension
297
301
  self.extension(connection_extension)
298
302
  end
299
303
 
304
+ # Do this last so we have as much context as possible when initializing them:
305
+ if extensions.any?
306
+ self.extensions(extensions)
307
+ end
308
+
309
+ if directives.any?
310
+ directives.each do |(dir_class, options)|
311
+ self.directive(dir_class, **options)
312
+ end
313
+ end
314
+
315
+ self.validates(validates)
316
+
300
317
  if definition_block
301
318
  if definition_block.arity == 1
302
319
  yield self
@@ -438,8 +455,8 @@ module GraphQL
438
455
  field_defn.description = @description
439
456
  end
440
457
 
441
- if @deprecation_reason
442
- field_defn.deprecation_reason = @deprecation_reason
458
+ if self.deprecation_reason
459
+ field_defn.deprecation_reason = self.deprecation_reason
443
460
  end
444
461
 
445
462
  if @resolver_class
@@ -461,6 +478,10 @@ module GraphQL
461
478
  field_defn.relay_nodes_field = @relay_nodes_field
462
479
  end
463
480
 
481
+ if @legacy_edge_class
482
+ field_defn.edge_class = @legacy_edge_class
483
+ end
484
+
464
485
  field_defn.resolve = self.method(:resolve_field)
465
486
  field_defn.connection = connection?
466
487
  field_defn.connection_max_page_size = max_page_size
@@ -580,6 +601,9 @@ module GraphQL
580
601
  begin
581
602
  # Unwrap the GraphQL object to get the application object.
582
603
  application_object = object.object
604
+
605
+ Schema::Validator.validate!(validators, application_object, ctx, args)
606
+
583
607
  ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
584
608
  if is_authorized
585
609
  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
@@ -20,7 +20,9 @@ module GraphQL
20
20
  if self.is_a?(Class)
21
21
  superclass.respond_to?(method_name, true) ? superclass.send(method_name) : default_value
22
22
  else
23
- ancestors[1..-1].each do |ancestor|
23
+ ancestors_except_self = ancestors
24
+ ancestors_except_self.delete(self)
25
+ ancestors_except_self.each do |ancestor|
24
26
  if ancestor.respond_to?(method_name, true)
25
27
  return ancestor.send(method_name)
26
28
  end
@@ -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
@@ -213,8 +214,12 @@ module GraphQL
213
214
  arguments = coerce_arguments(nil, value, ctx)
214
215
 
215
216
  ctx.schema.after_lazy(arguments) do |resolved_arguments|
216
- input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
217
- input_obj_instance.prepare
217
+ if resolved_arguments.is_a?(GraphQL::Error)
218
+ raise resolved_arguments
219
+ else
220
+ input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
221
+ input_obj_instance.prepare
222
+ end
218
223
  end
219
224
  end
220
225
 
@@ -240,6 +245,16 @@ module GraphQL
240
245
  result
241
246
  end
242
247
  end
248
+
249
+ private
250
+
251
+ def overwrite_argument(key, value)
252
+ # Argument keywords come in frozen from the interpreter, dup them before modifying them.
253
+ if @ruby_style_hash.frozen?
254
+ @ruby_style_hash = @ruby_style_hash.dup
255
+ end
256
+ @ruby_style_hash[key] = value
257
+ end
243
258
  end
244
259
  end
245
260
  end
@@ -15,6 +15,7 @@ module GraphQL
15
15
  include GraphQL::Schema::Member::Scoped
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasUnresolvedTypeError
18
+ include GraphQL::Schema::Member::HasDirectives
18
19
 
19
20
  # Methods defined in this block will be:
20
21
  # - Added as class methods to this interface
@@ -4,8 +4,11 @@ require 'graphql/schema/member/base_dsl_methods'
4
4
  require 'graphql/schema/member/cached_graphql_definition'
5
5
  require 'graphql/schema/member/graphql_type_names'
6
6
  require 'graphql/schema/member/has_ast_node'
7
+ require 'graphql/schema/member/has_directives'
8
+ require 'graphql/schema/member/has_deprecation_reason'
7
9
  require 'graphql/schema/member/has_path'
8
10
  require 'graphql/schema/member/has_unresolved_type_error'
11
+ require 'graphql/schema/member/has_validators'
9
12
  require 'graphql/schema/member/relay_shortcuts'
10
13
  require 'graphql/schema/member/scoped'
11
14
  require 'graphql/schema/member/type_system_helpers'
@@ -30,6 +33,7 @@ module GraphQL
30
33
  extend RelayShortcuts
31
34
  extend HasPath
32
35
  extend HasAstNode
36
+ extend HasDirectives
33
37
  end
34
38
  end
35
39
  end
@@ -19,6 +19,7 @@ module GraphQL
19
19
  # @return [String]
20
20
  def graphql_name(new_name = nil)
21
21
  if new_name
22
+ GraphQL::NameValidator.validate!(new_name)
22
23
  @graphql_name = new_name
23
24
  else
24
25
  overridden_graphql_name || default_graphql_name
@@ -60,11 +60,11 @@ module GraphQL
60
60
  parse_type(type_expr.first, null: false)
61
61
  when 2
62
62
  inner_type, nullable_option = type_expr
63
- if nullable_option.keys != [:null] || nullable_option.values != [true]
63
+ if nullable_option.keys != [:null] || (nullable_option[:null] != true && nullable_option[:null] != false)
64
64
  raise ArgumentError, LIST_TYPE_ERROR
65
65
  end
66
66
  list_type = true
67
- parse_type(inner_type, null: true)
67
+ parse_type(inner_type, null: nullable_option[:null])
68
68
  else
69
69
  raise ArgumentError, LIST_TYPE_ERROR
70
70
  end
@@ -75,7 +75,7 @@ module GraphQL
75
75
  if type_expr.respond_to?(:graphql_definition)
76
76
  type_expr
77
77
  else
78
- # Eg `String` => GraphQL::STRING_TYPE
78
+ # Eg `String` => GraphQL::Types::String
79
79
  parse_type(type_expr.name, null: true)
80
80
  end
81
81
  when Proc
@@ -81,72 +81,77 @@ module GraphQL
81
81
  end
82
82
 
83
83
  # @api private
84
+ # If given a block, it will eventually yield the loaded args to the block.
85
+ #
86
+ # If no block is given, it will immediately dataload (but might return a Lazy).
87
+ #
84
88
  # @param values [Hash<String, Object>]
85
89
  # @param context [GraphQL::Query::Context]
86
- # @return [Hash<Symbol, Object>, Execution::Lazy<Hash>]
87
- def coerce_arguments(parent_object, values, context)
90
+ # @yield [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
91
+ # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
92
+ def coerce_arguments(parent_object, values, context, &block)
88
93
  # Cache this hash to avoid re-merging it
89
94
  arg_defns = self.arguments
95
+ total_args_count = arg_defns.size
90
96
 
91
- if arg_defns.empty?
92
- GraphQL::Execution::Interpreter::Arguments.new(argument_values: nil)
97
+ if total_args_count == 0
98
+ final_args = GraphQL::Execution::Interpreter::Arguments::EMPTY
99
+ if block_given?
100
+ block.call(final_args)
101
+ nil
102
+ else
103
+ final_args
104
+ end
93
105
  else
106
+ finished_args = nil
94
107
  argument_values = {}
95
- arg_lazies = arg_defns.map do |arg_name, arg_defn|
96
- arg_key = arg_defn.keyword
97
- has_value = false
98
- default_used = false
99
- if values.key?(arg_name)
100
- has_value = true
101
- value = values[arg_name]
102
- elsif values.key?(arg_key)
103
- has_value = true
104
- value = values[arg_key]
105
- elsif arg_defn.default_value?
106
- has_value = true
107
- value = arg_defn.default_value
108
- default_used = true
109
- end
110
-
111
- if has_value
112
- loads = arg_defn.loads
113
- loaded_value = nil
114
- if loads && !arg_defn.from_resolver?
115
- loaded_value = if arg_defn.type.list?
116
- loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, context) }
117
- context.schema.after_any_lazies(loaded_values) { |result| result }
108
+ resolved_args_count = 0
109
+ raised_error = false
110
+ arg_defns.each do |arg_name, arg_defn|
111
+ context.dataloader.append_job do
112
+ begin
113
+ arg_defn.coerce_into_values(parent_object, values, context, argument_values)
114
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
115
+ raised_error = true
116
+ if block_given?
117
+ block.call(err)
118
118
  else
119
- load_application_object(arg_defn, loads, value, context)
119
+ finished_args = err
120
120
  end
121
121
  end
122
122
 
123
- coerced_value = if loaded_value
124
- loaded_value
125
- else
126
- context.schema.error_handler.with_error_handling(context) do
127
- arg_defn.type.coerce_input(value, context)
128
- end
129
- end
123
+ resolved_args_count += 1
124
+ if resolved_args_count == total_args_count && !raised_error
125
+ finished_args = context.schema.after_any_lazies(argument_values.values) {
126
+ GraphQL::Execution::Interpreter::Arguments.new(
127
+ argument_values: argument_values,
128
+ )
129
+ }
130
130
 
131
- context.schema.after_lazy(coerced_value) do |coerced_value|
132
- prepared_value = context.schema.error_handler.with_error_handling(context) do
133
- arg_defn.prepare_value(parent_object, coerced_value, context: context)
131
+ if block_given?
132
+ block.call(finished_args)
134
133
  end
135
-
136
- # TODO code smell to access such a deeply-nested constant in a distant module
137
- argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
138
- value: prepared_value,
139
- definition: arg_defn,
140
- default_used: default_used,
141
- )
142
134
  end
143
135
  end
144
136
  end
145
137
 
146
- context.schema.after_any_lazies(arg_lazies) do
147
- GraphQL::Execution::Interpreter::Arguments.new(
148
- argument_values: argument_values,
149
- )
138
+ if block_given?
139
+ nil
140
+ else
141
+ # This API returns eagerly, gotta run it now
142
+ context.dataloader.run
143
+ finished_args
144
+ end
145
+ end
146
+ end
147
+
148
+ # Usually, this is validated statically by RequiredArgumentsArePresent,
149
+ # but not for directives.
150
+ # TODO apply static validations on schema definitions?
151
+ def validate_directive_argument(arg_defn, value)
152
+ if arg_defn.owner.is_a?(Class) && arg_defn.owner < GraphQL::Schema::Directive
153
+ if value.nil? && arg_defn.type.non_null?
154
+ raise ArgumentError, "#{arg_defn.path} is required, but no value was given"
150
155
  end
151
156
  end
152
157
  end