graphql 1.11.7 → 1.12.4

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