request_params_validation 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +557 -0
- data/Rakefile +32 -0
- data/lib/request_params_validation.rb +346 -0
- data/lib/request_params_validation/definitions.rb +57 -0
- data/lib/request_params_validation/definitions/action.rb +23 -0
- data/lib/request_params_validation/definitions/param.rb +182 -0
- data/lib/request_params_validation/definitions/request.rb +31 -0
- data/lib/request_params_validation/definitions/resource.rb +30 -0
- data/lib/request_params_validation/engine.rb +18 -0
- data/lib/request_params_validation/exceptions/base_errors.rb +10 -0
- data/lib/request_params_validation/exceptions/definitions_errors.rb +31 -0
- data/lib/request_params_validation/exceptions/validator_errors.rb +49 -0
- data/lib/request_params_validation/handler.rb +26 -0
- data/lib/request_params_validation/helpers.rb +17 -0
- data/lib/request_params_validation/params.rb +60 -0
- data/lib/request_params_validation/params/constants.rb +22 -0
- data/lib/request_params_validation/params/converter.rb +33 -0
- data/lib/request_params_validation/params/types/conversions.rb +41 -0
- data/lib/request_params_validation/params/types/validations.rb +57 -0
- data/lib/request_params_validation/params/validator.rb +74 -0
- data/lib/request_params_validation/params/validators/custom.rb +18 -0
- data/lib/request_params_validation/params/validators/format.rb +27 -0
- data/lib/request_params_validation/params/validators/inclusion.rb +27 -0
- data/lib/request_params_validation/params/validators/length.rb +37 -0
- data/lib/request_params_validation/params/validators/presence.rb +13 -0
- data/lib/request_params_validation/params/validators/type.rb +59 -0
- data/lib/request_params_validation/params/validators/value.rb +37 -0
- data/lib/request_params_validation/version.rb +3 -0
- metadata +87 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'request_params_validation/params'
|
2
|
+
require 'request_params_validation/exceptions/definitions_errors'
|
3
|
+
|
4
|
+
module RequestParamsValidation
|
5
|
+
module Definitions
|
6
|
+
class Param
|
7
|
+
attr_reader :key, :required, :allow_blank, :type, :transform, :decimal_precision,
|
8
|
+
:inclusion, :length, :value, :format, :custom_validation, :elements
|
9
|
+
|
10
|
+
def initialize(options, &block)
|
11
|
+
@key = options[:key]
|
12
|
+
@required = options[:required]
|
13
|
+
@allow_blank = options[:allow_blank]
|
14
|
+
@type = options[:type].try(:to_sym)
|
15
|
+
@default = options[:default]
|
16
|
+
|
17
|
+
@transform = options[:transform]
|
18
|
+
@decimal_precision = options[:precision]
|
19
|
+
@element_of_array = options[:element_of_array]
|
20
|
+
|
21
|
+
@inclusion = build_inclusion_option(options[:inclusion])
|
22
|
+
@length = build_length_option(options[:length])
|
23
|
+
@value = build_value_option(options[:value])
|
24
|
+
@format = build_format_option(options[:format])
|
25
|
+
@custom_validation = build_custom_validation_option(options[:validate])
|
26
|
+
|
27
|
+
@elements = build_elements_option(options[:elements], &block)
|
28
|
+
@sub_definition = build_sub_definition(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_default?
|
32
|
+
!@default.nil? # default value could be `false`
|
33
|
+
end
|
34
|
+
|
35
|
+
def default
|
36
|
+
@default.respond_to?(:call) ? @default.call : @default
|
37
|
+
end
|
38
|
+
|
39
|
+
def sub_definition
|
40
|
+
@sub_definition || @elements.try(:sub_definition)
|
41
|
+
end
|
42
|
+
|
43
|
+
def element_of_array?
|
44
|
+
!!@element_of_array
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_presence?
|
48
|
+
!!@required
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_type?
|
52
|
+
!!@type
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_inclusion?
|
56
|
+
!!@inclusion
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_length?
|
60
|
+
!!@length
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_value?
|
64
|
+
!!@value
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_format?
|
68
|
+
return false if [Params::DATE_TYPE, Params::DATETIME_TYPE].include?(@type)
|
69
|
+
|
70
|
+
!!@format
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_custom_validation?
|
74
|
+
!!@custom_validation
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def build_inclusion_option(inclusion)
|
80
|
+
case inclusion
|
81
|
+
when Array
|
82
|
+
include_in = inclusion
|
83
|
+
when Hash
|
84
|
+
include_in = inclusion[:in]
|
85
|
+
message = inclusion[:message]
|
86
|
+
end
|
87
|
+
|
88
|
+
return unless include_in
|
89
|
+
|
90
|
+
Struct.new(:in, :message).new(include_in, message)
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_length_option(length)
|
94
|
+
case length
|
95
|
+
when Integer
|
96
|
+
min = length
|
97
|
+
max = length
|
98
|
+
when Hash
|
99
|
+
min = length[:min]
|
100
|
+
max = length[:max]
|
101
|
+
message = length[:message]
|
102
|
+
end
|
103
|
+
|
104
|
+
return unless min || max
|
105
|
+
|
106
|
+
Struct.new(:min, :max, :message).new(min, max, message)
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_value_option(value)
|
110
|
+
case value
|
111
|
+
when Hash
|
112
|
+
min = value[:min]
|
113
|
+
max = value[:max]
|
114
|
+
message = value[:message]
|
115
|
+
end
|
116
|
+
|
117
|
+
return unless min || max
|
118
|
+
|
119
|
+
Struct.new(:min, :max, :message).new(min, max, message)
|
120
|
+
end
|
121
|
+
|
122
|
+
def build_format_option(format)
|
123
|
+
case format
|
124
|
+
when Regexp
|
125
|
+
regexp = format
|
126
|
+
when String
|
127
|
+
strptime = format
|
128
|
+
when Hash
|
129
|
+
strptime = format[:strptime]
|
130
|
+
regexp = format[:regexp]
|
131
|
+
message = format[:message]
|
132
|
+
end
|
133
|
+
|
134
|
+
return if regexp.nil? && !strptime
|
135
|
+
|
136
|
+
Struct.new(:regexp, :strptime, :message).new(regexp, strptime, message)
|
137
|
+
end
|
138
|
+
|
139
|
+
def build_custom_validation_option(validation)
|
140
|
+
case validation
|
141
|
+
when Proc
|
142
|
+
function = validation
|
143
|
+
when Hash
|
144
|
+
function = validation[:function]
|
145
|
+
message = validation[:message]
|
146
|
+
end
|
147
|
+
|
148
|
+
return unless function
|
149
|
+
|
150
|
+
Struct.new(:function, :message).new(function, message)
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_elements_option(elements, &block)
|
154
|
+
return unless @type == Params::ARRAY_TYPE
|
155
|
+
|
156
|
+
elements_options = {
|
157
|
+
key: @key,
|
158
|
+
element_of_array: true
|
159
|
+
}
|
160
|
+
|
161
|
+
case elements
|
162
|
+
when Hash
|
163
|
+
elements_options.merge!(elements)
|
164
|
+
when String, Symbol
|
165
|
+
elements_options.merge!(type: elements)
|
166
|
+
end
|
167
|
+
|
168
|
+
self.class.new(elements_options, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_sub_definition(&block)
|
172
|
+
return unless @type == Params::HASH_TYPE
|
173
|
+
|
174
|
+
request = Request.new
|
175
|
+
|
176
|
+
block.call(request) if block_given?
|
177
|
+
|
178
|
+
request.params
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'request_params_validation/definitions/param'
|
2
|
+
|
3
|
+
module RequestParamsValidation
|
4
|
+
module Definitions
|
5
|
+
class Request
|
6
|
+
attr_reader :params
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@params = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def required(param_name, options = {}, &block)
|
13
|
+
options = options.merge({ required: true })
|
14
|
+
add_parameter(param_name, options, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def optional(param_name, options = {}, &block)
|
18
|
+
options = options.merge({ required: false })
|
19
|
+
add_parameter(param_name, options, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def add_parameter(name, options, &block)
|
25
|
+
options = options.merge({ key: name })
|
26
|
+
|
27
|
+
@params << Param.new(options, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'request_params_validation/definitions/action'
|
2
|
+
require 'request_params_validation/exceptions/definitions_errors'
|
3
|
+
|
4
|
+
module RequestParamsValidation
|
5
|
+
module Definitions
|
6
|
+
class Resource
|
7
|
+
attr_reader :name, :actions
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@actions = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def action(action_name)
|
15
|
+
unless block_given?
|
16
|
+
raise DefinitionArgumentError.new("Expecting block for action '#{action_name}'")
|
17
|
+
end
|
18
|
+
|
19
|
+
action_definition = Action.new(action_name.to_s)
|
20
|
+
|
21
|
+
yield action_definition
|
22
|
+
|
23
|
+
@actions[action_name.to_s] = action_definition
|
24
|
+
rescue DefinitionArgumentError => e
|
25
|
+
e.resource = name
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RequestParamsValidation
|
2
|
+
autoload :Definitions, 'request_params_validation/definitions'
|
3
|
+
autoload :Helpers, 'request_params_validation/helpers'
|
4
|
+
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace RequestParamsValidation
|
7
|
+
|
8
|
+
initializer 'request_params_validation.load_definitions' do
|
9
|
+
RequestParamsValidation::Definitions.load_all
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer 'request_params_validation.add_helpers' do
|
13
|
+
ActiveSupport.on_load :action_controller do
|
14
|
+
include RequestParamsValidation::Helpers
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'request_params_validation/exceptions/base_errors'
|
2
|
+
|
3
|
+
module RequestParamsValidation
|
4
|
+
class DefinitionNotFoundError < DefinitionsError
|
5
|
+
def initialize(resource, action)
|
6
|
+
msg = "The request definition for the resource '#{resource}' and action '#{action}' " \
|
7
|
+
"couldn't be found"
|
8
|
+
|
9
|
+
super(msg)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class DefinitionArgumentError < DefinitionsError
|
14
|
+
attr_accessor :resource, :action
|
15
|
+
|
16
|
+
def initialize(error_msg, options = {})
|
17
|
+
@error_msg = error_msg
|
18
|
+
@resource = options[:resource]
|
19
|
+
|
20
|
+
super(message)
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
if resource
|
25
|
+
"Argument error for resource '#{resource}'. #{@error_msg}"
|
26
|
+
else
|
27
|
+
@error_msg
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'request_params_validation/exceptions/base_errors'
|
2
|
+
|
3
|
+
module RequestParamsValidation
|
4
|
+
class MissingParameterError < RequestParamError
|
5
|
+
attr_reader :param_key, :param_type
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@param_key = options[:param_key]
|
9
|
+
@param_type = options[:param_type]
|
10
|
+
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
"The parameter '#{param_key}' is missing"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class InvalidParameterValueError < RequestParamError
|
20
|
+
attr_reader :param_key, :param_value, :param_type, :details
|
21
|
+
|
22
|
+
def initialize(options)
|
23
|
+
@param_key = options[:param_key]
|
24
|
+
@param_value = options[:param_value]
|
25
|
+
@param_type = options[:param_type]
|
26
|
+
@details = options[:details]
|
27
|
+
|
28
|
+
super(message)
|
29
|
+
end
|
30
|
+
|
31
|
+
def message
|
32
|
+
message = "The value for the parameter '#{param_key}' is invalid"
|
33
|
+
|
34
|
+
if details.present?
|
35
|
+
"#{message}. #{details}"
|
36
|
+
else
|
37
|
+
message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class UnsupportedTypeError < GeneralError
|
43
|
+
def initialize(options)
|
44
|
+
msg = "Unsupported type '#{options[:param_type]}' for the parameter '#{options[:param_key]}'"
|
45
|
+
|
46
|
+
super(msg)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'request_params_validation/definitions'
|
2
|
+
require 'request_params_validation/params'
|
3
|
+
require 'request_params_validation/exceptions/definitions_errors'
|
4
|
+
|
5
|
+
module RequestParamsValidation
|
6
|
+
module Handler
|
7
|
+
def self.handle_request_params(resource, action, params)
|
8
|
+
request_definition = Definitions.get_request(resource, action)
|
9
|
+
|
10
|
+
unless request_definition
|
11
|
+
case RequestParamsValidation.on_definition_not_found
|
12
|
+
when :raise
|
13
|
+
raise DefinitionNotFoundError.new(resource, action)
|
14
|
+
else
|
15
|
+
return
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RequestParamsValidation.remove_keys_from_params.each { |key| params.delete(key) }
|
20
|
+
|
21
|
+
Params.validate!(request_definition.params, params)
|
22
|
+
|
23
|
+
Params.filter!(request_definition.params, params) if RequestParamsValidation.filter_params
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'request_params_validation/handler'
|
2
|
+
|
3
|
+
module RequestParamsValidation
|
4
|
+
module Helpers
|
5
|
+
define_method RequestParamsValidation.helper_method_name do
|
6
|
+
resource = params[:controller]
|
7
|
+
action = params[:action]
|
8
|
+
|
9
|
+
if RequestParamsValidation.save_original_params
|
10
|
+
original_params = params.deep_dup
|
11
|
+
instance_variable_set(RequestParamsValidation.save_original_params, original_params)
|
12
|
+
end
|
13
|
+
|
14
|
+
Handler.handle_request_params(resource, action, params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'request_params_validation/params/constants'
|
2
|
+
require 'request_params_validation/params/validator'
|
3
|
+
|
4
|
+
module RequestParamsValidation
|
5
|
+
module Params
|
6
|
+
include Constants
|
7
|
+
|
8
|
+
def self.validate!(definition, params)
|
9
|
+
definition.each do |param_definition|
|
10
|
+
validate_and_coerce_param(param_definition, params)
|
11
|
+
end
|
12
|
+
|
13
|
+
params
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.filter!(definition, params)
|
17
|
+
extra_keys = [:controller, :action] # Keys added by Rails
|
18
|
+
|
19
|
+
filter_params(definition, params, extra_keys).tap do |params|
|
20
|
+
params.permit! if params.respond_to?(:permit!)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.validate_and_coerce_param(param_definition, params)
|
25
|
+
key = param_definition.key
|
26
|
+
value = params[key]
|
27
|
+
|
28
|
+
value = Validator.new(param_definition, value).validate_and_coerce
|
29
|
+
|
30
|
+
params[key] = value
|
31
|
+
end
|
32
|
+
private_class_method :validate_and_coerce_param
|
33
|
+
|
34
|
+
def self.filter_params(definition, params, extra_keys = [])
|
35
|
+
return unless params
|
36
|
+
return params if definition.empty?
|
37
|
+
|
38
|
+
params_keys = definition.map do |param_definition|
|
39
|
+
key = param_definition.key
|
40
|
+
|
41
|
+
if param_definition.sub_definition
|
42
|
+
filter_params(param_definition.sub_definition, params[key])
|
43
|
+
end
|
44
|
+
|
45
|
+
key
|
46
|
+
end.compact
|
47
|
+
|
48
|
+
params_keys += extra_keys
|
49
|
+
|
50
|
+
if params.is_a?(Array)
|
51
|
+
params.map { |param| param.slice!(*params_keys) }
|
52
|
+
else
|
53
|
+
params.slice!(*params_keys)
|
54
|
+
end
|
55
|
+
|
56
|
+
params
|
57
|
+
end
|
58
|
+
private_class_method :filter_params
|
59
|
+
end
|
60
|
+
end
|