graphql 1.11.12 → 1.12.0

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 (124) 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/analysis/analyze_query.rb +7 -0
  11. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  14. data/lib/graphql/backtrace/table.rb +22 -2
  15. data/lib/graphql/backtrace/tracer.rb +40 -9
  16. data/lib/graphql/backtrace.rb +28 -19
  17. data/lib/graphql/backwards_compatibility.rb +1 -0
  18. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  19. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  20. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  21. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  22. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  23. data/lib/graphql/dataloader/request.rb +24 -0
  24. data/lib/graphql/dataloader/request_all.rb +22 -0
  25. data/lib/graphql/dataloader/source.rb +93 -0
  26. data/lib/graphql/dataloader.rb +197 -0
  27. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  28. data/lib/graphql/define/instance_definable.rb +32 -2
  29. data/lib/graphql/define/type_definer.rb +5 -5
  30. data/lib/graphql/deprecated_dsl.rb +5 -0
  31. data/lib/graphql/enum_type.rb +2 -0
  32. data/lib/graphql/execution/errors.rb +4 -0
  33. data/lib/graphql/execution/execute.rb +7 -0
  34. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  35. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  36. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  37. data/lib/graphql/execution/interpreter.rb +10 -6
  38. data/lib/graphql/execution/multiplex.rb +20 -6
  39. data/lib/graphql/function.rb +4 -0
  40. data/lib/graphql/input_object_type.rb +2 -0
  41. data/lib/graphql/interface_type.rb +3 -1
  42. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  43. data/lib/graphql/language/nodes.rb +0 -5
  44. data/lib/graphql/language/visitor.rb +0 -1
  45. data/lib/graphql/object_type.rb +2 -0
  46. data/lib/graphql/pagination/connection.rb +5 -1
  47. data/lib/graphql/pagination/connections.rb +6 -16
  48. data/lib/graphql/query/context.rb +4 -0
  49. data/lib/graphql/query/serial_execution.rb +1 -0
  50. data/lib/graphql/query/validation_pipeline.rb +1 -1
  51. data/lib/graphql/query.rb +2 -0
  52. data/lib/graphql/relay/base_connection.rb +7 -0
  53. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  54. data/lib/graphql/relay/connection_type.rb +1 -1
  55. data/lib/graphql/relay/mutation.rb +1 -0
  56. data/lib/graphql/relay/node.rb +3 -0
  57. data/lib/graphql/relay/type_extensions.rb +2 -0
  58. data/lib/graphql/scalar_type.rb +2 -0
  59. data/lib/graphql/schema/argument.rb +30 -10
  60. data/lib/graphql/schema/build_from_definition.rb +145 -58
  61. data/lib/graphql/schema/directive/flagged.rb +57 -0
  62. data/lib/graphql/schema/directive.rb +76 -0
  63. data/lib/graphql/schema/enum.rb +3 -0
  64. data/lib/graphql/schema/enum_value.rb +13 -7
  65. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  66. data/lib/graphql/schema/field.rb +28 -10
  67. data/lib/graphql/schema/input_object.rb +36 -28
  68. data/lib/graphql/schema/interface.rb +1 -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/member.rb +4 -0
  77. data/lib/graphql/schema/object.rb +11 -0
  78. data/lib/graphql/schema/printer.rb +5 -4
  79. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  80. data/lib/graphql/schema/resolver.rb +7 -0
  81. data/lib/graphql/schema/subscription.rb +19 -1
  82. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  83. data/lib/graphql/schema/validation.rb +2 -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/schema/validator.rb +163 -0
  91. data/lib/graphql/schema.rb +72 -49
  92. data/lib/graphql/static_validation/base_visitor.rb +0 -3
  93. data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
  94. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  95. data/lib/graphql/static_validation/validation_context.rb +1 -6
  96. data/lib/graphql/static_validation/validator.rb +12 -14
  97. data/lib/graphql/subscriptions.rb +17 -20
  98. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  99. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  100. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  101. data/lib/graphql/tracing.rb +2 -2
  102. data/lib/graphql/types/relay/base_connection.rb +2 -92
  103. data/lib/graphql/types/relay/base_edge.rb +2 -35
  104. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  105. data/lib/graphql/types/relay/default_relay.rb +27 -0
  106. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  107. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  108. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  109. data/lib/graphql/types/relay/node.rb +2 -4
  110. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  111. data/lib/graphql/types/relay/node_field.rb +1 -19
  112. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  113. data/lib/graphql/types/relay/page_info.rb +2 -14
  114. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  115. data/lib/graphql/types/relay.rb +11 -3
  116. data/lib/graphql/union_type.rb +2 -0
  117. data/lib/graphql/upgrader/member.rb +1 -0
  118. data/lib/graphql/upgrader/schema.rb +1 -0
  119. data/lib/graphql/version.rb +1 -1
  120. data/lib/graphql.rb +38 -4
  121. metadata +34 -9
  122. data/lib/graphql/types/relay/base_field.rb +0 -22
  123. data/lib/graphql/types/relay/base_interface.rb +0 -29
  124. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -6,6 +6,8 @@ module GraphQL
6
6
  # Its {RULES} contain objects that respond to `#call(type)`. Rules are
7
7
  # looked up for given types (by class ancestry), then applied to
8
8
  # the object until an error is returned.
9
+ #
10
+ # Remove this in GraphQL-Ruby 2.0 when schema instances are removed.
9
11
  class Validation
10
12
  # Lookup the rules for `object` based on its class,
11
13
  # Then returns an error message or `nil`
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to specifically reject values from an argument.
7
+ #
8
+ # @example disallow certain values
9
+ #
10
+ # argument :favorite_non_prime, Integer, required: true,
11
+ # validates: { exclusion: { in: [2, 3, 5, 7, ... ]} }
12
+ #
13
+ class ExclusionValidator < Validator
14
+ # @param message [String]
15
+ # @param in [Array] The values to reject
16
+ def initialize(message: "%{validated} is reserved", in:, **default_options)
17
+ # `in` is a reserved word, so work around that
18
+ @in_list = binding.local_variable_get(:in)
19
+ @message = message
20
+ super(**default_options)
21
+ end
22
+
23
+ def validate(_object, _context, value)
24
+ if @in_list.include?(value)
25
+ @message
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to assert that string values match (or don't match) the given RegExp.
7
+ #
8
+ # @example requiring input to match a pattern
9
+ #
10
+ # argument :handle, String, required: true,
11
+ # validates: { format: { with: /\A[a-z0-9_]+\Z/ } }
12
+ #
13
+ # @example reject inputs that match a pattern
14
+ #
15
+ # argument :word_that_doesnt_begin_with_a_vowel, String, required: true,
16
+ # validates: { format: { without: /\A[aeiou]/ } }
17
+ #
18
+ # # It's pretty hard to come up with a legitimate use case for `without:`
19
+ #
20
+ class FormatValidator < Validator
21
+ if !String.method_defined?(:match?)
22
+ using GraphQL::StringMatchBackport
23
+ end
24
+
25
+ # @param with [RegExp, nil]
26
+ # @param without [Regexp, nil]
27
+ # @param message [String]
28
+ def initialize(
29
+ with: nil,
30
+ without: nil,
31
+ message: "%{validated} is invalid",
32
+ **default_options
33
+ )
34
+ @with_pattern = with
35
+ @without_pattern = without
36
+ @message = message
37
+ super(**default_options)
38
+ end
39
+
40
+ def validate(_object, _context, value)
41
+ if (@with_pattern && !value.match?(@with_pattern)) ||
42
+ (@without_pattern && value.match?(@without_pattern))
43
+ @message
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # You can use this to allow certain values for an argument.
7
+ #
8
+ # Usually, a {GraphQL::Schema::Enum} is better for this, because it's self-documenting.
9
+ #
10
+ # @example only allow certain values for an argument
11
+ #
12
+ # argument :favorite_prime, Integer, required: true,
13
+ # validates: { inclusion: { in: [2, 3, 5, 7, 11, ... ] } }
14
+ #
15
+ class InclusionValidator < Validator
16
+ # @param message [String]
17
+ # @param in [Array] The values to allow
18
+ def initialize(in:, message: "%{validated} is not included in the list", **default_options)
19
+ # `in` is a reserved word, so work around that
20
+ @in_list = binding.local_variable_get(:in)
21
+ @message = message
22
+ super(**default_options)
23
+ end
24
+
25
+ def validate(_object, _context, value)
26
+ if !@in_list.include?(value)
27
+ @message
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to enforce a `.length` restriction on incoming values. It works for both Strings and Lists.
7
+ #
8
+ # @example Allow no more than 10 IDs
9
+ #
10
+ # argument :ids, [ID], required: true, validates: { length: { maximum: 10 } }
11
+ #
12
+ # @example Require three selections
13
+ #
14
+ # argument :ice_cream_preferences, [ICE_CREAM_FLAVOR], required: true, validates: { length: { is: 3 } }
15
+ #
16
+ class LengthValidator < Validator
17
+ # @param maximum [Integer]
18
+ # @param too_long [String] Used when `maximum` is exceeded or value is greater than `within`
19
+ # @param minimum [Integer]
20
+ # @param too_short [String] Used with value is less than `minimum` or less than `within`
21
+ # @param is [Integer] Exact length requirement
22
+ # @param wrong_length [String] Used when value doesn't match `is`
23
+ # @param within [Range] An allowed range (becomes `minimum:` and `maximum:` under the hood)
24
+ # @param message [String]
25
+ def initialize(
26
+ maximum: nil, too_long: "%{validated} is too long (maximum is %{count})",
27
+ minimum: nil, too_short: "%{validated} is too short (minimum is %{count})",
28
+ is: nil, within: nil, wrong_length: "%{validated} is the wrong length (should be %{count})",
29
+ message: nil,
30
+ **default_options
31
+ )
32
+ if within && (minimum || maximum)
33
+ raise ArgumentError, "`length: { ... }` may include `within:` _or_ `minimum:`/`maximum:`, but not both"
34
+ end
35
+ # Under the hood, `within` is decomposed into `minimum` and `maximum`
36
+ @maximum = maximum || (within && within.max)
37
+ @too_long = message || too_long
38
+ @minimum = minimum || (within && within.min)
39
+ @too_short = message || too_short
40
+ @is = is
41
+ @wrong_length = message || wrong_length
42
+ super(**default_options)
43
+ end
44
+
45
+ def validate(_object, _context, value)
46
+ if @maximum && value.length > @maximum
47
+ partial_format(@too_long, { count: @maximum })
48
+ elsif @minimum && value.length < @minimum
49
+ partial_format(@too_short, { count: @minimum })
50
+ elsif @is && value.length != @is
51
+ partial_format(@wrong_length, { count: @is })
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,71 @@
1
+ module GraphQL
2
+ class Schema
3
+ class Validator
4
+ # Use this to assert numerical comparisons hold true for inputs.
5
+ #
6
+ # @example Require a number between 0 and 1
7
+ #
8
+ # argument :batting_average, Float, required: true, validates: { numericality: { within: 0..1 } }
9
+ #
10
+ # @example Require the number 42
11
+ #
12
+ # argument :the_answer, Integer, required: true, validates: { numericality: { equal_to: 42 } }
13
+ #
14
+ # @example Require a real number
15
+ #
16
+ # argument :items_count, Integer, required: true, validates: { numericality: { greater_than_or_equal_to: 0 } }
17
+ #
18
+ class NumericalityValidator < Validator
19
+ # @param greater_than [Integer]
20
+ # @param greater_than_or_equal_to [Integer]
21
+ # @param less_than [Integer]
22
+ # @param less_than_or_equal_to [Integer]
23
+ # @param equal_to [Integer]
24
+ # @param other_than [Integer]
25
+ # @param odd [Boolean]
26
+ # @param even [Boolean]
27
+ # @param message [String] used for all validation failures
28
+ def initialize(
29
+ greater_than: nil, greater_than_or_equal_to: nil,
30
+ less_than: nil, less_than_or_equal_to: nil,
31
+ equal_to: nil, other_than: nil,
32
+ odd: nil, even: nil,
33
+ message: "%{validated} must be %{comparison} %{target}",
34
+ **default_options
35
+ )
36
+
37
+ @greater_than = greater_than
38
+ @greater_than_or_equal_to = greater_than_or_equal_to
39
+ @less_than = less_than
40
+ @less_than_or_equal_to = less_than_or_equal_to
41
+ @equal_to = equal_to
42
+ @other_than = other_than
43
+ @odd = odd
44
+ @even = even
45
+ @message = message
46
+ super(**default_options)
47
+ end
48
+
49
+ def validate(object, context, value)
50
+ if @greater_than && value <= @greater_than
51
+ partial_format(@message, { comparison: "greater than", target: @greater_than })
52
+ elsif @greater_than_or_equal_to && value < @greater_than_or_equal_to
53
+ partial_format(@message, { comparison: "greater than or equal to", target: @greater_than_or_equal_to })
54
+ elsif @less_than && value >= @less_than
55
+ partial_format(@message, { comparison: "less than", target: @less_than })
56
+ elsif @less_than_or_equal_to && value > @less_than_or_equal_to
57
+ partial_format(@message, { comparison: "less than or equal to", target: @less_than_or_equal_to })
58
+ elsif @equal_to && value != @equal_to
59
+ partial_format(@message, { comparison: "equal to", target: @equal_to })
60
+ elsif @other_than && value == @other_than
61
+ partial_format(@message, { comparison: "something other than", target: @other_than })
62
+ elsif @even && !value.even?
63
+ (partial_format(@message, { comparison: "even", target: "" })).strip
64
+ elsif @odd && !value.odd?
65
+ (partial_format(@message, { comparison: "odd", target: "" })).strip
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this validator to require _one_ of the named arguments to be present.
7
+ # Or, use Arrays of symbols to name a valid _set_ of arguments.
8
+ #
9
+ # (This is for specifying mutually exclusive sets of arguments.)
10
+ #
11
+ # @example Require exactly one of these arguments
12
+ #
13
+ # field :update_amount, IngredientAmount, null: false do
14
+ # argument :ingredient_id, ID, required: true
15
+ # argument :cups, Integer, required: false
16
+ # argument :tablespoons, Integer, required: false
17
+ # argument :teaspoons, Integer, required: true
18
+ # validates required: { one_of: [:cups, :tablespoons, :teaspoons] }
19
+ # end
20
+ #
21
+ # @example Require one of these _sets_ of arguments
22
+ #
23
+ # field :find_object, Node, null: true do
24
+ # argument :node_id, ID, required: false
25
+ # argument :object_type, String, required: false
26
+ # argument :object_id, Integer, required: false
27
+ # # either a global `node_id` or an `object_type`/`object_id` pair is required:
28
+ # validates required: { one_of: [:node_id, [:object_type, :object_id]] }
29
+ # end
30
+ #
31
+ class RequiredValidator < Validator
32
+ # @param one_of [Symbol, Array<Symbol>] An argument, or a list of arguments, that represents a valid set of inputs for this field
33
+ # @param message [String]
34
+ def initialize(one_of:, message: "%{validated} has the wrong arguments", **default_options)
35
+ @one_of = one_of
36
+ @message = message
37
+ super(**default_options)
38
+ end
39
+
40
+ def validate(_object, _context, value)
41
+ matched_conditions = 0
42
+
43
+ @one_of.each do |one_of_condition|
44
+ case one_of_condition
45
+ when Symbol
46
+ if value.key?(one_of_condition)
47
+ matched_conditions += 1
48
+ end
49
+ when Array
50
+ if one_of_condition.all? { |k| value.key?(k) }
51
+ matched_conditions += 1
52
+ break
53
+ end
54
+ else
55
+ raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
56
+ end
57
+ end
58
+
59
+ if matched_conditions == 1
60
+ nil # OK
61
+ else
62
+ @message
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # The thing being validated
7
+ # @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
8
+ attr_reader :validated
9
+
10
+ # TODO should this implement `if:` and `unless:` ?
11
+ # @param validated [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>] The argument or argument owner this validator is attached to
12
+ # @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
13
+ # @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
14
+ def initialize(validated:, allow_blank: false, allow_null: false)
15
+ @validated = validated
16
+ @allow_blank = allow_blank
17
+ @allow_null = allow_null
18
+ end
19
+
20
+ # @param object [Object] The application object that this argument's field is being resolved for
21
+ # @param context [GraphQL::Query::Context]
22
+ # @param value [Object] The client-provided value for this argument (after parsing and coercing by the input type)
23
+ # @return [nil, Array<String>, String] Error message or messages to add
24
+ def validate(object, context, value)
25
+ raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
26
+ end
27
+
28
+ # This is called by the validation system and eventually calls {#validate}.
29
+ # @api private
30
+ def apply(object, context, value)
31
+ if value.nil?
32
+ if @allow_null
33
+ nil # skip this
34
+ else
35
+ "%{validated} can't be null"
36
+ end
37
+ elsif value.respond_to?(:blank?) && value.blank?
38
+ if @allow_blank
39
+ nil # skip this
40
+ else
41
+ "%{validated} can't be blank"
42
+ end
43
+ else
44
+ validate(object, context, value)
45
+ end
46
+ end
47
+
48
+ # This is like `String#%`, but it supports the case that only some of `string`'s
49
+ # values are present in `substitutions`
50
+ def partial_format(string, substitutions)
51
+ substitutions.each do |key, value|
52
+ sub_v = value.is_a?(String) ? value : value.to_s
53
+ string = string.gsub("%{#{key}}", sub_v)
54
+ end
55
+ string
56
+ end
57
+
58
+ # @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
59
+ # @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
60
+ # @return [Array<Validator>]
61
+ def self.from_config(schema_member, validates_hash)
62
+ if validates_hash.nil? || validates_hash.empty?
63
+ EMPTY_ARRAY
64
+ else
65
+ validates_hash.map do |validator_name, options|
66
+ validator_class = case validator_name
67
+ when Class
68
+ validator_name
69
+ else
70
+ all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
71
+ end
72
+ validator_class.new(validated: schema_member, **options)
73
+ end
74
+ end
75
+ end
76
+
77
+ # Add `validator_class` to be initialized when `validates:` is given `name`.
78
+ # (It's initialized with whatever options are given by the key `name`).
79
+ # @param name [Symbol]
80
+ # @param validator_class [Class]
81
+ # @return [void]
82
+ def self.install(name, validator_class)
83
+ all_validators[name] = validator_class
84
+ nil
85
+ end
86
+
87
+ # Remove whatever validator class is {.install}ed at `name`, if there is one
88
+ # @param name [Symbol]
89
+ # @return [void]
90
+ def self.uninstall(name)
91
+ all_validators.delete(name)
92
+ nil
93
+ end
94
+
95
+ class << self
96
+ attr_accessor :all_validators
97
+ end
98
+
99
+ self.all_validators = {}
100
+
101
+ include Schema::FindInheritedValue::EmptyObjects
102
+
103
+ class ValidationFailedError < GraphQL::ExecutionError
104
+ attr_reader :errors
105
+
106
+ def initialize(errors:)
107
+ @errors = errors
108
+ super(errors.join(", "))
109
+ end
110
+ end
111
+
112
+ # @param validators [Array<Validator>]
113
+ # @param object [Object]
114
+ # @param context [Query::Context]
115
+ # @param value [Object]
116
+ # @return [void]
117
+ # @raises [ValidationFailedError]
118
+ def self.validate!(validators, object, context, value, as: nil)
119
+ # Assuming the default case is no errors, reduce allocations in that case.
120
+ # This will be replaced with a mutable array if we actually get any errors.
121
+ all_errors = EMPTY_ARRAY
122
+
123
+ validators.each do |validator|
124
+ validated = as || validator.validated
125
+ errors = validator.apply(object, context, value)
126
+ if errors &&
127
+ (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
128
+ (errors.is_a?(String))
129
+ if all_errors.frozen? # It's empty
130
+ all_errors = []
131
+ end
132
+ interpolation_vars = { validated: validated.graphql_name }
133
+ if errors.is_a?(String)
134
+ all_errors << (errors % interpolation_vars)
135
+ else
136
+ errors = errors.map { |e| e % interpolation_vars }
137
+ all_errors.concat(errors)
138
+ end
139
+ end
140
+ end
141
+
142
+ if all_errors.any?
143
+ raise ValidationFailedError.new(errors: all_errors)
144
+ end
145
+ nil
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ require "graphql/schema/validator/length_validator"
153
+ GraphQL::Schema::Validator.install(:length, GraphQL::Schema::Validator::LengthValidator)
154
+ require "graphql/schema/validator/numericality_validator"
155
+ GraphQL::Schema::Validator.install(:numericality, GraphQL::Schema::Validator::NumericalityValidator)
156
+ require "graphql/schema/validator/format_validator"
157
+ GraphQL::Schema::Validator.install(:format, GraphQL::Schema::Validator::FormatValidator)
158
+ require "graphql/schema/validator/inclusion_validator"
159
+ GraphQL::Schema::Validator.install(:inclusion, GraphQL::Schema::Validator::InclusionValidator)
160
+ require "graphql/schema/validator/exclusion_validator"
161
+ GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
162
+ require "graphql/schema/validator/required_validator"
163
+ GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)