-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathvalidator.rb
More file actions
174 lines (153 loc) · 7.13 KB
/
validator.rb
File metadata and controls
174 lines (153 loc) · 7.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# frozen_string_literal: true
module GraphQL
class Schema
class Validator
# The thing being validated
# @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
attr_reader :validated
# @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
# @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
# @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
def initialize(validated:, allow_blank: false, allow_null: false)
@validated = validated
@allow_blank = allow_blank
@allow_null = allow_null
end
# @param object [Object] The application object that this argument's field is being resolved for
# @param context [GraphQL::Query::Context]
# @param value [Object] The client-provided value for this argument (after parsing and coercing by the input type)
# @return [nil, Array<String>, String] Error message or messages to add
def validate(object, context, value)
raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
end
# This is like `String#%`, but it supports the case that only some of `string`'s
# values are present in `substitutions`
def partial_format(string, substitutions)
substitutions.each do |key, value|
sub_v = value.is_a?(String) ? value : value.to_s
string = string.gsub("%{#{key}}", sub_v)
end
string
end
# @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true`
def permitted_empty_value?(value)
(value.nil? && @allow_null) ||
(@allow_blank && value.respond_to?(:blank?) && value.blank?)
end
# @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
# @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
# @return [Array<Validator>]
def self.from_config(schema_member, validates_hash)
if validates_hash.nil? || validates_hash.empty?
EMPTY_ARRAY
else
validates_hash = validates_hash.dup
default_options = {}
if validates_hash[:allow_null]
default_options[:allow_null] = validates_hash.delete(:allow_null)
end
if validates_hash[:allow_blank]
default_options[:allow_blank] = validates_hash.delete(:allow_blank)
end
# allow_nil or allow_blank are the _only_ validations:
if validates_hash.empty?
validates_hash = default_options
end
validates_hash.map do |validator_name, options|
validator_class = case validator_name
when Class
validator_name
else
all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
end
if options.is_a?(Hash)
validator_class.new(validated: schema_member, **(default_options.merge(options)))
else
validator_class.new(options, validated: schema_member, **default_options)
end
end
end
end
# Add `validator_class` to be initialized when `validates:` is given `name`.
# (It's initialized with whatever options are given by the key `name`).
# @param name [Symbol]
# @param validator_class [Class]
# @return [void]
def self.install(name, validator_class)
all_validators[name] = validator_class
nil
end
# Remove whatever validator class is {.install}ed at `name`, if there is one
# @param name [Symbol]
# @return [void]
def self.uninstall(name)
all_validators.delete(name)
nil
end
class << self
attr_accessor :all_validators
end
self.all_validators = {}
include GraphQL::EmptyObjects
class ValidationFailedError < GraphQL::ExecutionError
attr_reader :errors
def initialize(errors:)
@errors = errors
super(errors.join(", "))
end
end
# @param validators [Array<Validator>]
# @param object [Object]
# @param context [Query::Context]
# @param value [Object]
# @return [void]
# @raises [ValidationFailedError]
def self.validate!(validators, object, context, value, as: nil)
# Assuming the default case is no errors, reduce allocations in that case.
# This will be replaced with a mutable array if we actually get any errors.
all_errors = EMPTY_ARRAY
validators.each do |validator|
validated = as || validator.validated
errors = validator.validate(object, context, value)
if errors &&
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
(errors.is_a?(String))
if all_errors.frozen? # It's empty
all_errors = []
end
interpolation_vars = { validated: validated.graphql_name, value: value.inspect }
if errors.is_a?(String)
all_errors << (errors % interpolation_vars)
else
errors = errors.map { |e| e % interpolation_vars }
all_errors.concat(errors)
end
end
end
if !all_errors.empty?
raise ValidationFailedError.new(errors: all_errors)
end
nil
end
end
end
end
require "graphql/schema/validator/length_validator"
GraphQL::Schema::Validator.install(:length, GraphQL::Schema::Validator::LengthValidator)
require "graphql/schema/validator/numericality_validator"
GraphQL::Schema::Validator.install(:numericality, GraphQL::Schema::Validator::NumericalityValidator)
require "graphql/schema/validator/format_validator"
GraphQL::Schema::Validator.install(:format, GraphQL::Schema::Validator::FormatValidator)
require "graphql/schema/validator/inclusion_validator"
GraphQL::Schema::Validator.install(:inclusion, GraphQL::Schema::Validator::InclusionValidator)
require "graphql/schema/validator/exclusion_validator"
GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
require "graphql/schema/validator/required_validator"
GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)
require "graphql/schema/validator/allow_null_validator"
GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
require "graphql/schema/validator/allow_blank_validator"
GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
require "graphql/schema/validator/all_validator"
GraphQL::Schema::Validator.install(:all, GraphQL::Schema::Validator::AllValidator)
require "graphql/schema/validator/custom_validator_warning"