grape 1.7.1 → 2.0.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 +4 -4
- data/CHANGELOG.md +37 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +30 -25
- data/UPGRADING.md +35 -0
- data/grape.gemspec +3 -6
- data/lib/grape/api.rb +2 -2
- data/lib/grape/content_types.rb +2 -8
- data/lib/grape/dsl/desc.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +11 -11
- data/lib/grape/dsl/request_response.rb +2 -1
- data/lib/grape/dsl/settings.rb +2 -6
- data/lib/grape/endpoint.rb +28 -18
- data/lib/grape/error_formatter/base.rb +1 -1
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/missing_group_type.rb +1 -6
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
- data/lib/grape/exceptions/validation_errors.rb +1 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
- data/lib/grape/extensions/hash.rb +4 -7
- data/lib/grape/extensions/hashie/mash.rb +3 -3
- data/lib/grape/formatter/serializable_hash.rb +7 -7
- data/lib/grape/http/headers.rb +12 -2
- data/lib/grape/middleware/auth/base.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/error.rb +5 -5
- data/lib/grape/middleware/formatter.rb +6 -6
- data/lib/grape/middleware/versioner/header.rb +11 -19
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +8 -2
- data/lib/grape/router/route.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -11
- data/lib/grape/util/strict_hash_configuration.rb +3 -4
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +8 -2
- data/lib/grape/validations/single_attribute_iterator.rb +3 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
- data/lib/grape/validations/validators/base.rb +9 -20
- data/lib/grape/validations/validators/default_validator.rb +2 -20
- data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
- data/lib/grape/validations/validators/values_validator.rb +14 -5
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +26 -5
- metadata +13 -253
- data/lib/grape/config.rb +0 -34
- data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
- data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
- data/spec/grape/api/custom_validations_spec.rb +0 -256
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -473
- data/spec/grape/api_spec.rb +0 -4347
- data/spec/grape/config_spec.rb +0 -17
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -101
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -535
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -206
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -136
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -696
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2029
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
@@ -1,2029 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Grape::Validations do
|
4
|
-
subject { Class.new(Grape::API) }
|
5
|
-
|
6
|
-
def app
|
7
|
-
subject
|
8
|
-
end
|
9
|
-
|
10
|
-
def declared_params
|
11
|
-
subject.namespace_stackable(:declared_params).flatten
|
12
|
-
end
|
13
|
-
|
14
|
-
describe 'params' do
|
15
|
-
context 'optional' do
|
16
|
-
before do
|
17
|
-
subject.params do
|
18
|
-
optional :a_number, regexp: /^[0-9]+$/
|
19
|
-
optional :attachment, type: File
|
20
|
-
end
|
21
|
-
subject.get '/optional' do
|
22
|
-
'optional works!'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'validates when params is present' do
|
27
|
-
get '/optional', a_number: 'string'
|
28
|
-
expect(last_response.status).to eq(400)
|
29
|
-
expect(last_response.body).to eq('a_number is invalid')
|
30
|
-
|
31
|
-
get '/optional', a_number: 45
|
32
|
-
expect(last_response.status).to eq(200)
|
33
|
-
expect(last_response.body).to eq('optional works!')
|
34
|
-
end
|
35
|
-
|
36
|
-
it "doesn't validate when param not present" do
|
37
|
-
get '/optional', a_number: nil, attachment: nil
|
38
|
-
expect(last_response.status).to eq(200)
|
39
|
-
expect(last_response.body).to eq('optional works!')
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'adds to declared parameters' do
|
43
|
-
subject.params do
|
44
|
-
optional :some_param
|
45
|
-
end
|
46
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([:some_param])
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'optional using Grape::Entity documentation' do
|
51
|
-
def define_optional_using
|
52
|
-
documentation = { field_a: { type: String }, field_b: { type: String } }
|
53
|
-
subject.params do
|
54
|
-
optional :all, using: documentation
|
55
|
-
end
|
56
|
-
end
|
57
|
-
before do
|
58
|
-
define_optional_using
|
59
|
-
subject.get '/optional' do
|
60
|
-
'optional with using works'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'adds entity documentation to declared params' do
|
65
|
-
define_optional_using
|
66
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[field_a field_b])
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'works when field_a and field_b are not present' do
|
70
|
-
get '/optional'
|
71
|
-
expect(last_response.status).to eq(200)
|
72
|
-
expect(last_response.body).to eq('optional with using works')
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'works when field_a is present' do
|
76
|
-
get '/optional', field_a: 'woof'
|
77
|
-
expect(last_response.status).to eq(200)
|
78
|
-
expect(last_response.body).to eq('optional with using works')
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'works when field_b is present' do
|
82
|
-
get '/optional', field_b: 'woof'
|
83
|
-
expect(last_response.status).to eq(200)
|
84
|
-
expect(last_response.body).to eq('optional with using works')
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
context 'required' do
|
89
|
-
before do
|
90
|
-
subject.params do
|
91
|
-
requires :key, type: String
|
92
|
-
end
|
93
|
-
subject.get('/required') { 'required works' }
|
94
|
-
subject.put('/required') { { key: params[:key] }.to_json }
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'errors when param not present' do
|
98
|
-
get '/required'
|
99
|
-
expect(last_response.status).to eq(400)
|
100
|
-
expect(last_response.body).to eq('key is missing')
|
101
|
-
end
|
102
|
-
|
103
|
-
it "doesn't throw a missing param when param is present" do
|
104
|
-
get '/required', key: 'cool'
|
105
|
-
expect(last_response.status).to eq(200)
|
106
|
-
expect(last_response.body).to eq('required works')
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'adds to declared parameters' do
|
110
|
-
subject.params do
|
111
|
-
requires :some_param
|
112
|
-
end
|
113
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([:some_param])
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'works when required field is present but nil' do
|
117
|
-
put '/required', { key: nil }.to_json, 'CONTENT_TYPE' => 'application/json'
|
118
|
-
expect(last_response.status).to eq(200)
|
119
|
-
expect(JSON.parse(last_response.body)).to eq('key' => nil)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
context 'requires with nested params' do
|
124
|
-
before do
|
125
|
-
subject.params do
|
126
|
-
requires :first_level, type: Hash do
|
127
|
-
optional :second_level, type: Array do
|
128
|
-
requires :value, type: Integer
|
129
|
-
optional :name, type: String
|
130
|
-
optional :third_level, type: Array do
|
131
|
-
requires :value, type: Integer
|
132
|
-
optional :name, type: String
|
133
|
-
optional :fourth_level, type: Array do
|
134
|
-
requires :value, type: Integer
|
135
|
-
optional :name, type: String
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
subject.put('/required') { 'required works' }
|
142
|
-
end
|
143
|
-
|
144
|
-
let(:request_params) do
|
145
|
-
{
|
146
|
-
first_level: {
|
147
|
-
second_level: [
|
148
|
-
{ value: 1, name: 'Lisa' },
|
149
|
-
{
|
150
|
-
value: 2,
|
151
|
-
name: 'James',
|
152
|
-
third_level: [
|
153
|
-
{ value: 'three', name: 'Sophie' },
|
154
|
-
{
|
155
|
-
value: 4,
|
156
|
-
name: 'Jenny',
|
157
|
-
fourth_level: [
|
158
|
-
{ name: 'Samuel' }, { value: 6, name: 'Jane' }
|
159
|
-
]
|
160
|
-
}
|
161
|
-
]
|
162
|
-
}
|
163
|
-
]
|
164
|
-
}
|
165
|
-
}
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'validates correctly in deep nested params' do
|
169
|
-
put '/required', request_params.to_json, 'CONTENT_TYPE' => 'application/json'
|
170
|
-
|
171
|
-
expect(last_response.status).to eq(400)
|
172
|
-
expect(last_response.body).to eq(
|
173
|
-
'first_level[second_level][1][third_level][0][value] is invalid, ' \
|
174
|
-
'first_level[second_level][1][third_level][1][fourth_level][0][value] is missing'
|
175
|
-
)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
context 'requires :all using Grape::Entity documentation' do
|
180
|
-
def define_requires_all
|
181
|
-
documentation = {
|
182
|
-
required_field: { type: String },
|
183
|
-
optional_field: { type: String }
|
184
|
-
}
|
185
|
-
subject.params do
|
186
|
-
requires :all, except: :optional_field, using: documentation
|
187
|
-
end
|
188
|
-
end
|
189
|
-
before do
|
190
|
-
define_requires_all
|
191
|
-
subject.get '/required' do
|
192
|
-
'required works'
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
it 'adds entity documentation to declared params' do
|
197
|
-
define_requires_all
|
198
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
|
199
|
-
end
|
200
|
-
|
201
|
-
it 'errors when required_field is not present' do
|
202
|
-
get '/required'
|
203
|
-
expect(last_response.status).to eq(400)
|
204
|
-
expect(last_response.body).to eq('required_field is missing')
|
205
|
-
end
|
206
|
-
|
207
|
-
it 'works when required_field is present' do
|
208
|
-
get '/required', required_field: 'woof'
|
209
|
-
expect(last_response.status).to eq(200)
|
210
|
-
expect(last_response.body).to eq('required works')
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
context 'requires :none using Grape::Entity documentation' do
|
215
|
-
def define_requires_none
|
216
|
-
documentation = {
|
217
|
-
required_field: { type: String },
|
218
|
-
optional_field: { type: String }
|
219
|
-
}
|
220
|
-
subject.params do
|
221
|
-
requires :none, except: :required_field, using: documentation
|
222
|
-
end
|
223
|
-
end
|
224
|
-
before do
|
225
|
-
define_requires_none
|
226
|
-
subject.get '/required' do
|
227
|
-
'required works'
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
it 'adds entity documentation to declared params' do
|
232
|
-
define_requires_none
|
233
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'errors when required_field is not present' do
|
237
|
-
get '/required'
|
238
|
-
expect(last_response.status).to eq(400)
|
239
|
-
expect(last_response.body).to eq('required_field is missing')
|
240
|
-
end
|
241
|
-
|
242
|
-
it 'works when required_field is present' do
|
243
|
-
get '/required', required_field: 'woof'
|
244
|
-
expect(last_response.status).to eq(200)
|
245
|
-
expect(last_response.body).to eq('required works')
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
context 'requires :all or :none but except a non-existent field using Grape::Entity documentation' do
|
250
|
-
context 'requires :all' do
|
251
|
-
def define_requires_all
|
252
|
-
documentation = {
|
253
|
-
required_field: { type: String },
|
254
|
-
optional_field: { type: String }
|
255
|
-
}
|
256
|
-
subject.params do
|
257
|
-
requires :all, except: :non_existent_field, using: documentation
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
it 'adds only the entity documentation to declared params, nothing more' do
|
262
|
-
define_requires_all
|
263
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
context 'requires :none' do
|
268
|
-
def define_requires_none
|
269
|
-
documentation = {
|
270
|
-
required_field: { type: String },
|
271
|
-
optional_field: { type: String }
|
272
|
-
}
|
273
|
-
subject.params do
|
274
|
-
requires :none, except: :non_existent_field, using: documentation
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
it 'adds only the entity documentation to declared params, nothing more' do
|
279
|
-
expect { define_requires_none }.to raise_error(ArgumentError)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
context 'required with an Array block' do
|
285
|
-
before do
|
286
|
-
subject.params do
|
287
|
-
requires :items, type: Array do
|
288
|
-
requires :key
|
289
|
-
end
|
290
|
-
end
|
291
|
-
subject.get('/required') { 'required works' }
|
292
|
-
subject.put('/required') { { items: params[:items] }.to_json }
|
293
|
-
end
|
294
|
-
|
295
|
-
it 'errors when param not present' do
|
296
|
-
get '/required'
|
297
|
-
expect(last_response.status).to eq(400)
|
298
|
-
expect(last_response.body).to eq('items is missing')
|
299
|
-
end
|
300
|
-
|
301
|
-
it 'errors when param is not an Array' do
|
302
|
-
get '/required', items: 'hello'
|
303
|
-
expect(last_response.status).to eq(400)
|
304
|
-
expect(last_response.body).to eq('items is invalid')
|
305
|
-
|
306
|
-
get '/required', items: { key: 'foo' }
|
307
|
-
expect(last_response.status).to eq(400)
|
308
|
-
expect(last_response.body).to eq('items is invalid')
|
309
|
-
end
|
310
|
-
|
311
|
-
it "doesn't throw a missing param when param is present" do
|
312
|
-
get '/required', items: [{ key: 'hello' }, { key: 'world' }]
|
313
|
-
expect(last_response.status).to eq(200)
|
314
|
-
expect(last_response.body).to eq('required works')
|
315
|
-
end
|
316
|
-
|
317
|
-
it "doesn't throw a missing param when param is present but empty" do
|
318
|
-
put '/required', { items: [] }.to_json, 'CONTENT_TYPE' => 'application/json'
|
319
|
-
expect(last_response.status).to eq(200)
|
320
|
-
expect(JSON.parse(last_response.body)).to eq('items' => [])
|
321
|
-
end
|
322
|
-
|
323
|
-
it 'adds to declared parameters' do
|
324
|
-
subject.params do
|
325
|
-
requires :items, type: Array do
|
326
|
-
requires :key
|
327
|
-
end
|
328
|
-
end
|
329
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
# Ensure there is no leakage between declared Array types and
|
334
|
-
# subsequent Hash types
|
335
|
-
context 'required with an Array and a Hash block' do
|
336
|
-
before do
|
337
|
-
subject.params do
|
338
|
-
requires :cats, type: Array[String], default: []
|
339
|
-
requires :items, type: Hash do
|
340
|
-
requires :key
|
341
|
-
end
|
342
|
-
end
|
343
|
-
subject.get '/required' do
|
344
|
-
'required works'
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
it 'does not output index [0] for Hash types' do
|
349
|
-
get '/required', cats: ['Garfield'], items: { foo: 'bar' }
|
350
|
-
expect(last_response.status).to eq(400)
|
351
|
-
expect(last_response.body).to eq('items[key] is missing')
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
context 'required with a Hash block' do
|
356
|
-
before do
|
357
|
-
subject.params do
|
358
|
-
requires :items, type: Hash do
|
359
|
-
requires :key
|
360
|
-
end
|
361
|
-
end
|
362
|
-
subject.get '/required' do
|
363
|
-
'required works'
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
it 'errors when param not present' do
|
368
|
-
get '/required'
|
369
|
-
expect(last_response.status).to eq(400)
|
370
|
-
expect(last_response.body).to eq('items is missing, items[key] is missing')
|
371
|
-
end
|
372
|
-
|
373
|
-
it 'errors when nested param not present' do
|
374
|
-
get '/required', items: { foo: 'bar' }
|
375
|
-
expect(last_response.status).to eq(400)
|
376
|
-
expect(last_response.body).to eq('items[key] is missing')
|
377
|
-
end
|
378
|
-
|
379
|
-
it 'errors when param is not a Hash' do
|
380
|
-
get '/required', items: 'hello'
|
381
|
-
expect(last_response.status).to eq(400)
|
382
|
-
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
383
|
-
|
384
|
-
get '/required', items: [{ key: 'foo' }]
|
385
|
-
expect(last_response.status).to eq(400)
|
386
|
-
expect(last_response.body).to eq('items is invalid')
|
387
|
-
end
|
388
|
-
|
389
|
-
it "doesn't throw a missing param when param is present" do
|
390
|
-
get '/required', items: { key: 'hello' }
|
391
|
-
expect(last_response.status).to eq(200)
|
392
|
-
expect(last_response.body).to eq('required works')
|
393
|
-
end
|
394
|
-
|
395
|
-
it 'adds to declared parameters' do
|
396
|
-
subject.params do
|
397
|
-
requires :items, type: Array do
|
398
|
-
requires :key
|
399
|
-
end
|
400
|
-
end
|
401
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
context 'hash with a required param with validation' do
|
406
|
-
before do
|
407
|
-
subject.params do
|
408
|
-
requires :items, type: Hash do
|
409
|
-
requires :key, type: String, values: %w[a b]
|
410
|
-
end
|
411
|
-
end
|
412
|
-
subject.get '/required' do
|
413
|
-
'required works'
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
it 'errors when param is not a Hash' do
|
418
|
-
get '/required', items: 'not a hash'
|
419
|
-
expect(last_response.status).to eq(400)
|
420
|
-
expect(last_response.body).to eq('items is invalid, items[key] is missing, items[key] is invalid')
|
421
|
-
|
422
|
-
get '/required', items: [{ key: 'hash in array' }]
|
423
|
-
expect(last_response.status).to eq(400)
|
424
|
-
expect(last_response.body).to eq('items is invalid, items[key] does not have a valid value')
|
425
|
-
end
|
426
|
-
|
427
|
-
it 'works when all params match' do
|
428
|
-
get '/required', items: { key: 'a' }
|
429
|
-
expect(last_response.status).to eq(200)
|
430
|
-
expect(last_response.body).to eq('required works')
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
context 'group' do
|
435
|
-
before do
|
436
|
-
subject.params do
|
437
|
-
group :items, type: Array do
|
438
|
-
requires :key
|
439
|
-
end
|
440
|
-
end
|
441
|
-
subject.get '/required' do
|
442
|
-
'required works'
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
it 'errors when param not present' do
|
447
|
-
get '/required'
|
448
|
-
expect(last_response.status).to eq(400)
|
449
|
-
expect(last_response.body).to eq('items is missing')
|
450
|
-
end
|
451
|
-
|
452
|
-
it "doesn't throw a missing param when param is present" do
|
453
|
-
get '/required', items: [key: 'hello']
|
454
|
-
expect(last_response.status).to eq(200)
|
455
|
-
expect(last_response.body).to eq('required works')
|
456
|
-
end
|
457
|
-
|
458
|
-
it 'adds to declared parameters' do
|
459
|
-
subject.params do
|
460
|
-
group :items, type: Array do
|
461
|
-
requires :key
|
462
|
-
end
|
463
|
-
end
|
464
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
context 'group params with nested params which has a type' do
|
469
|
-
let(:invalid_items) { { items: '' } }
|
470
|
-
|
471
|
-
before do
|
472
|
-
subject.params do
|
473
|
-
optional :items, type: Array do
|
474
|
-
optional :key1, type: String
|
475
|
-
optional :key2, type: String
|
476
|
-
end
|
477
|
-
end
|
478
|
-
subject.post '/group_with_nested' do
|
479
|
-
'group with nested works'
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
it 'errors when group param is invalid' do
|
484
|
-
post '/group_with_nested', items: invalid_items
|
485
|
-
expect(last_response.status).to eq(400)
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
context 'custom validator for a Hash' do
|
490
|
-
let(:date_range_validator) do
|
491
|
-
Class.new(Grape::Validations::Validators::Base) do
|
492
|
-
def validate_param!(attr_name, params)
|
493
|
-
return if params[attr_name][:from] <= params[attr_name][:to]
|
494
|
-
|
495
|
-
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
|
496
|
-
end
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
before do
|
501
|
-
described_class.register_validator('date_range', date_range_validator)
|
502
|
-
end
|
503
|
-
|
504
|
-
after do
|
505
|
-
described_class.deregister_validator('date_range')
|
506
|
-
end
|
507
|
-
|
508
|
-
before do
|
509
|
-
subject.params do
|
510
|
-
optional :date_range, date_range: true, type: Hash do
|
511
|
-
requires :from, type: Integer
|
512
|
-
requires :to, type: Integer
|
513
|
-
end
|
514
|
-
end
|
515
|
-
subject.get('/optional') do
|
516
|
-
'optional works'
|
517
|
-
end
|
518
|
-
subject.params do
|
519
|
-
requires :date_range, date_range: true, type: Hash do
|
520
|
-
requires :from, type: Integer
|
521
|
-
requires :to, type: Integer
|
522
|
-
end
|
523
|
-
end
|
524
|
-
subject.get('/required') do
|
525
|
-
'required works'
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
context 'which is optional' do
|
530
|
-
it "doesn't throw an error if the validation passes" do
|
531
|
-
get '/optional', date_range: { from: 1, to: 2 }
|
532
|
-
expect(last_response.status).to eq(200)
|
533
|
-
end
|
534
|
-
|
535
|
-
it 'errors if the validation fails' do
|
536
|
-
get '/optional', date_range: { from: 2, to: 1 }
|
537
|
-
expect(last_response.status).to eq(400)
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
context 'which is required' do
|
542
|
-
it "doesn't throw an error if the validation passes" do
|
543
|
-
get '/required', date_range: { from: 1, to: 2 }
|
544
|
-
expect(last_response.status).to eq(200)
|
545
|
-
end
|
546
|
-
|
547
|
-
it 'errors if the validation fails' do
|
548
|
-
get '/required', date_range: { from: 2, to: 1 }
|
549
|
-
expect(last_response.status).to eq(400)
|
550
|
-
end
|
551
|
-
end
|
552
|
-
end
|
553
|
-
|
554
|
-
context 'validation within arrays' do
|
555
|
-
before do
|
556
|
-
subject.params do
|
557
|
-
group :children, type: Array do
|
558
|
-
requires :name
|
559
|
-
group :parents, type: Array do
|
560
|
-
requires :name, allow_blank: false
|
561
|
-
end
|
562
|
-
end
|
563
|
-
end
|
564
|
-
subject.get '/within_array' do
|
565
|
-
'within array works'
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
|
-
it 'can handle new scopes within child elements' do
|
570
|
-
get '/within_array', children: [
|
571
|
-
{ name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
|
572
|
-
{ name: 'Joe', parents: [{ name: 'Josie' }] }
|
573
|
-
]
|
574
|
-
expect(last_response.status).to eq(200)
|
575
|
-
expect(last_response.body).to eq('within array works')
|
576
|
-
end
|
577
|
-
|
578
|
-
it 'errors when a parameter is not present' do
|
579
|
-
get '/within_array', children: [
|
580
|
-
{ name: 'Jim', parents: [{ name: 'Joy' }] },
|
581
|
-
{ name: 'Job', parents: [{}] }
|
582
|
-
]
|
583
|
-
# NOTE: with body parameters in json or XML or similar this
|
584
|
-
# should actually fail with: children[parents][name] is missing.
|
585
|
-
expect(last_response.status).to eq(400)
|
586
|
-
expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty')
|
587
|
-
end
|
588
|
-
|
589
|
-
it 'errors when a parameter is not present in array within array' do
|
590
|
-
get '/within_array', children: [
|
591
|
-
{ name: 'Jim', parents: [{ name: 'Joy' }] },
|
592
|
-
{ name: 'Job', parents: [{ name: 'Bill' }, { name: '' }] }
|
593
|
-
]
|
594
|
-
|
595
|
-
expect(last_response.status).to eq(400)
|
596
|
-
expect(last_response.body).to eq('children[1][parents][1][name] is empty')
|
597
|
-
end
|
598
|
-
|
599
|
-
it 'handle errors for all array elements' do
|
600
|
-
get '/within_array', children: [
|
601
|
-
{ name: 'Jim', parents: [] },
|
602
|
-
{ name: 'Job', parents: [] }
|
603
|
-
]
|
604
|
-
|
605
|
-
expect(last_response.status).to eq(400)
|
606
|
-
expect(last_response.body).to eq(
|
607
|
-
'children[0][parents][0][name] is missing, ' \
|
608
|
-
'children[1][parents][0][name] is missing'
|
609
|
-
)
|
610
|
-
end
|
611
|
-
|
612
|
-
it 'safely handles empty arrays and blank parameters' do
|
613
|
-
# NOTE: with body parameters in json or XML or similar this
|
614
|
-
# should actually return 200, since an empty array is valid.
|
615
|
-
get '/within_array', children: []
|
616
|
-
expect(last_response.status).to eq(400)
|
617
|
-
expect(last_response.body).to eq(
|
618
|
-
'children[0][name] is missing, ' \
|
619
|
-
'children[0][parents] is missing, ' \
|
620
|
-
'children[0][parents] is invalid, ' \
|
621
|
-
'children[0][parents][0][name] is missing, ' \
|
622
|
-
'children[0][parents][0][name] is empty'
|
623
|
-
)
|
624
|
-
|
625
|
-
get '/within_array', children: [name: 'Jay']
|
626
|
-
expect(last_response.status).to eq(400)
|
627
|
-
expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing, children[0][parents][0][name] is empty')
|
628
|
-
end
|
629
|
-
|
630
|
-
it 'errors when param is not an Array' do
|
631
|
-
get '/within_array', children: 'hello'
|
632
|
-
expect(last_response.status).to eq(400)
|
633
|
-
expect(last_response.body).to eq('children is invalid')
|
634
|
-
|
635
|
-
get '/within_array', children: { name: 'foo' }
|
636
|
-
expect(last_response.status).to eq(400)
|
637
|
-
expect(last_response.body).to eq('children is invalid')
|
638
|
-
|
639
|
-
get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
|
640
|
-
expect(last_response.status).to eq(400)
|
641
|
-
expect(last_response.body).to eq('children[0][parents] is invalid')
|
642
|
-
end
|
643
|
-
end
|
644
|
-
|
645
|
-
context 'with block param' do
|
646
|
-
before do
|
647
|
-
subject.params do
|
648
|
-
requires :planets, type: Array do
|
649
|
-
requires :name
|
650
|
-
end
|
651
|
-
end
|
652
|
-
subject.get '/req' do
|
653
|
-
'within array works'
|
654
|
-
end
|
655
|
-
subject.put '/req' do
|
656
|
-
''
|
657
|
-
end
|
658
|
-
|
659
|
-
subject.params do
|
660
|
-
group :stars, type: Array do
|
661
|
-
requires :name
|
662
|
-
end
|
663
|
-
end
|
664
|
-
subject.get '/grp' do
|
665
|
-
'within array works'
|
666
|
-
end
|
667
|
-
subject.put '/grp' do
|
668
|
-
''
|
669
|
-
end
|
670
|
-
|
671
|
-
subject.params do
|
672
|
-
requires :name
|
673
|
-
optional :moons, type: Array do
|
674
|
-
requires :name
|
675
|
-
end
|
676
|
-
end
|
677
|
-
subject.get '/opt' do
|
678
|
-
'within array works'
|
679
|
-
end
|
680
|
-
subject.put '/opt' do
|
681
|
-
''
|
682
|
-
end
|
683
|
-
end
|
684
|
-
|
685
|
-
it 'requires defaults to Array type' do
|
686
|
-
get '/req', planets: 'Jupiter, Saturn'
|
687
|
-
expect(last_response.status).to eq(400)
|
688
|
-
expect(last_response.body).to eq('planets is invalid')
|
689
|
-
|
690
|
-
get '/req', planets: { name: 'Jupiter' }
|
691
|
-
expect(last_response.status).to eq(400)
|
692
|
-
expect(last_response.body).to eq('planets is invalid')
|
693
|
-
|
694
|
-
get '/req', planets: [{ name: 'Venus' }, { name: 'Mars' }]
|
695
|
-
expect(last_response.status).to eq(200)
|
696
|
-
|
697
|
-
put_with_json '/req', planets: []
|
698
|
-
expect(last_response.status).to eq(200)
|
699
|
-
end
|
700
|
-
|
701
|
-
it 'optional defaults to Array type' do
|
702
|
-
get '/opt', name: 'Jupiter', moons: 'Europa, Ganymede'
|
703
|
-
expect(last_response.status).to eq(400)
|
704
|
-
expect(last_response.body).to eq('moons is invalid')
|
705
|
-
|
706
|
-
get '/opt', name: 'Jupiter', moons: { name: 'Ganymede' }
|
707
|
-
expect(last_response.status).to eq(400)
|
708
|
-
expect(last_response.body).to eq('moons is invalid')
|
709
|
-
|
710
|
-
get '/opt', name: 'Jupiter', moons: [{ name: 'Io' }, { name: 'Callisto' }]
|
711
|
-
expect(last_response.status).to eq(200)
|
712
|
-
|
713
|
-
put_with_json '/opt', name: 'Venus'
|
714
|
-
expect(last_response.status).to eq(200)
|
715
|
-
|
716
|
-
put_with_json '/opt', name: 'Mercury', moons: []
|
717
|
-
expect(last_response.status).to eq(200)
|
718
|
-
end
|
719
|
-
|
720
|
-
it 'group defaults to Array type' do
|
721
|
-
get '/grp', stars: 'Sun'
|
722
|
-
expect(last_response.status).to eq(400)
|
723
|
-
expect(last_response.body).to eq('stars is invalid')
|
724
|
-
|
725
|
-
get '/grp', stars: { name: 'Sun' }
|
726
|
-
expect(last_response.status).to eq(400)
|
727
|
-
expect(last_response.body).to eq('stars is invalid')
|
728
|
-
|
729
|
-
get '/grp', stars: [{ name: 'Sun' }]
|
730
|
-
expect(last_response.status).to eq(200)
|
731
|
-
|
732
|
-
put_with_json '/grp', stars: []
|
733
|
-
expect(last_response.status).to eq(200)
|
734
|
-
end
|
735
|
-
end
|
736
|
-
|
737
|
-
context 'validation within arrays with JSON' do
|
738
|
-
before do
|
739
|
-
subject.params do
|
740
|
-
group :children, type: Array do
|
741
|
-
requires :name
|
742
|
-
group :parents, type: Array do
|
743
|
-
requires :name
|
744
|
-
end
|
745
|
-
end
|
746
|
-
end
|
747
|
-
subject.put '/within_array' do
|
748
|
-
'within array works'
|
749
|
-
end
|
750
|
-
end
|
751
|
-
|
752
|
-
it 'can handle new scopes within child elements' do
|
753
|
-
put_with_json '/within_array', children: [
|
754
|
-
{ name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
|
755
|
-
{ name: 'Joe', parents: [{ name: 'Josie' }] }
|
756
|
-
]
|
757
|
-
expect(last_response.status).to eq(200)
|
758
|
-
expect(last_response.body).to eq('within array works')
|
759
|
-
end
|
760
|
-
|
761
|
-
it 'errors when a parameter is not present' do
|
762
|
-
put_with_json '/within_array', children: [
|
763
|
-
{ name: 'Jim', parents: [{}] },
|
764
|
-
{ name: 'Job', parents: [{ name: 'Joy' }] }
|
765
|
-
]
|
766
|
-
expect(last_response.status).to eq(400)
|
767
|
-
expect(last_response.body).to eq('children[0][parents][0][name] is missing')
|
768
|
-
end
|
769
|
-
|
770
|
-
it 'safely handles empty arrays and blank parameters' do
|
771
|
-
put_with_json '/within_array', children: []
|
772
|
-
expect(last_response.status).to eq(200)
|
773
|
-
put_with_json '/within_array', children: [name: 'Jay']
|
774
|
-
expect(last_response.status).to eq(400)
|
775
|
-
expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
|
776
|
-
end
|
777
|
-
end
|
778
|
-
|
779
|
-
context 'optional with an Array block' do
|
780
|
-
before do
|
781
|
-
subject.params do
|
782
|
-
optional :items, type: Array do
|
783
|
-
requires :key
|
784
|
-
end
|
785
|
-
end
|
786
|
-
subject.get '/optional_group' do
|
787
|
-
'optional group works'
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
it "doesn't throw a missing param when the group isn't present" do
|
792
|
-
get '/optional_group'
|
793
|
-
expect(last_response.status).to eq(200)
|
794
|
-
expect(last_response.body).to eq('optional group works')
|
795
|
-
end
|
796
|
-
|
797
|
-
it "doesn't throw a missing param when both group and param are given" do
|
798
|
-
get '/optional_group', items: [{ key: 'foo' }]
|
799
|
-
expect(last_response.status).to eq(200)
|
800
|
-
expect(last_response.body).to eq('optional group works')
|
801
|
-
end
|
802
|
-
|
803
|
-
it 'errors when group is present, but required param is not' do
|
804
|
-
get '/optional_group', items: [{ not_key: 'foo' }]
|
805
|
-
expect(last_response.status).to eq(400)
|
806
|
-
expect(last_response.body).to eq('items[0][key] is missing')
|
807
|
-
end
|
808
|
-
|
809
|
-
it "errors when param is present but isn't an Array" do
|
810
|
-
get '/optional_group', items: 'hello'
|
811
|
-
expect(last_response.status).to eq(400)
|
812
|
-
expect(last_response.body).to eq('items is invalid')
|
813
|
-
|
814
|
-
get '/optional_group', items: { key: 'foo' }
|
815
|
-
expect(last_response.status).to eq(400)
|
816
|
-
expect(last_response.body).to eq('items is invalid')
|
817
|
-
end
|
818
|
-
|
819
|
-
it 'adds to declared parameters' do
|
820
|
-
subject.params do
|
821
|
-
optional :items, type: Array do
|
822
|
-
requires :key
|
823
|
-
end
|
824
|
-
end
|
825
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
|
826
|
-
end
|
827
|
-
end
|
828
|
-
|
829
|
-
context 'nested optional Array blocks' do
|
830
|
-
before do
|
831
|
-
subject.params do
|
832
|
-
optional :items, type: Array do
|
833
|
-
requires :key
|
834
|
-
optional(:optional_subitems, type: Array) { requires :value }
|
835
|
-
requires(:required_subitems, type: Array) { requires :value }
|
836
|
-
end
|
837
|
-
end
|
838
|
-
subject.get('/nested_optional_group') { 'nested optional group works' }
|
839
|
-
end
|
840
|
-
|
841
|
-
it 'does no internal validations if the outer group is blank' do
|
842
|
-
get '/nested_optional_group'
|
843
|
-
expect(last_response.status).to eq(200)
|
844
|
-
expect(last_response.body).to eq('nested optional group works')
|
845
|
-
end
|
846
|
-
|
847
|
-
it 'does internal validations if the outer group is present' do
|
848
|
-
get '/nested_optional_group', items: [{ key: 'foo' }]
|
849
|
-
expect(last_response.status).to eq(400)
|
850
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
851
|
-
|
852
|
-
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
853
|
-
expect(last_response.status).to eq(200)
|
854
|
-
expect(last_response.body).to eq('nested optional group works')
|
855
|
-
end
|
856
|
-
|
857
|
-
it 'handles deep nesting' do
|
858
|
-
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
|
859
|
-
expect(last_response.status).to eq(400)
|
860
|
-
expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
|
861
|
-
|
862
|
-
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
|
863
|
-
expect(last_response.status).to eq(200)
|
864
|
-
expect(last_response.body).to eq('nested optional group works')
|
865
|
-
end
|
866
|
-
|
867
|
-
it 'handles validation within arrays' do
|
868
|
-
get '/nested_optional_group', items: [{ key: 'foo' }]
|
869
|
-
expect(last_response.status).to eq(400)
|
870
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
871
|
-
|
872
|
-
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
873
|
-
expect(last_response.status).to eq(200)
|
874
|
-
expect(last_response.body).to eq('nested optional group works')
|
875
|
-
|
876
|
-
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
|
877
|
-
expect(last_response.status).to eq(400)
|
878
|
-
expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
|
879
|
-
end
|
880
|
-
|
881
|
-
it 'adds to declared parameters' do
|
882
|
-
subject.params do
|
883
|
-
optional :items, type: Array do
|
884
|
-
requires :key
|
885
|
-
optional(:optional_subitems, type: Array) { requires :value }
|
886
|
-
requires(:required_subitems, type: Array) { requires :value }
|
887
|
-
end
|
888
|
-
end
|
889
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
|
890
|
-
end
|
891
|
-
|
892
|
-
context <<~DESC do
|
893
|
-
Issue occurs whenever:
|
894
|
-
* param structure with at least three levels
|
895
|
-
* 1st level item is a required Array that has >1 entry with an optional item present and >1 entry with an optional item missing#{' '}
|
896
|
-
* 2nd level is an optional Array or Hash#{' '}
|
897
|
-
* 3rd level is a required item (can be any type)
|
898
|
-
* additional levels do not effect the issue from occuring
|
899
|
-
DESC
|
900
|
-
|
901
|
-
it 'example based off actual real world use case' do
|
902
|
-
subject.params do
|
903
|
-
requires :orders, type: Array do
|
904
|
-
requires :id, type: Integer
|
905
|
-
optional :drugs, type: Array do
|
906
|
-
requires :batches, type: Array do
|
907
|
-
requires :batch_no, type: String
|
908
|
-
end
|
909
|
-
end
|
910
|
-
end
|
911
|
-
end
|
912
|
-
|
913
|
-
subject.get '/validate_required_arrays_under_optional_arrays' do
|
914
|
-
'validate_required_arrays_under_optional_arrays works!'
|
915
|
-
end
|
916
|
-
|
917
|
-
data = {
|
918
|
-
orders: [
|
919
|
-
{ id: 77, drugs: [{ batches: [{ batch_no: 'A1234567' }] }] },
|
920
|
-
{ id: 70 }
|
921
|
-
]
|
922
|
-
}
|
923
|
-
|
924
|
-
get '/validate_required_arrays_under_optional_arrays', data
|
925
|
-
expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
|
926
|
-
expect(last_response.status).to eq(200)
|
927
|
-
end
|
928
|
-
|
929
|
-
it 'simplest example using Array -> Array -> Hash -> String' do
|
930
|
-
subject.params do
|
931
|
-
requires :orders, type: Array do
|
932
|
-
requires :id, type: Integer
|
933
|
-
optional :drugs, type: Array do
|
934
|
-
requires :batch_no, type: String
|
935
|
-
end
|
936
|
-
end
|
937
|
-
end
|
938
|
-
|
939
|
-
subject.get '/validate_required_arrays_under_optional_arrays' do
|
940
|
-
'validate_required_arrays_under_optional_arrays works!'
|
941
|
-
end
|
942
|
-
|
943
|
-
data = {
|
944
|
-
orders: [
|
945
|
-
{ id: 77, drugs: [{ batch_no: 'A1234567' }] },
|
946
|
-
{ id: 70 }
|
947
|
-
]
|
948
|
-
}
|
949
|
-
|
950
|
-
get '/validate_required_arrays_under_optional_arrays', data
|
951
|
-
expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
|
952
|
-
expect(last_response.status).to eq(200)
|
953
|
-
end
|
954
|
-
|
955
|
-
it 'simplest example using Array -> Hash -> String' do
|
956
|
-
subject.params do
|
957
|
-
requires :orders, type: Array do
|
958
|
-
requires :id, type: Integer
|
959
|
-
optional :drugs, type: Hash do
|
960
|
-
requires :batch_no, type: String
|
961
|
-
end
|
962
|
-
end
|
963
|
-
end
|
964
|
-
|
965
|
-
subject.get '/validate_required_arrays_under_optional_arrays' do
|
966
|
-
'validate_required_arrays_under_optional_arrays works!'
|
967
|
-
end
|
968
|
-
|
969
|
-
data = {
|
970
|
-
orders: [
|
971
|
-
{ id: 77, drugs: { batch_no: 'A1234567' } },
|
972
|
-
{ id: 70 }
|
973
|
-
]
|
974
|
-
}
|
975
|
-
|
976
|
-
get '/validate_required_arrays_under_optional_arrays', data
|
977
|
-
expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
|
978
|
-
expect(last_response.status).to eq(200)
|
979
|
-
end
|
980
|
-
|
981
|
-
it 'correctly indexes invalida data' do
|
982
|
-
subject.params do
|
983
|
-
requires :orders, type: Array do
|
984
|
-
requires :id, type: Integer
|
985
|
-
optional :drugs, type: Array do
|
986
|
-
requires :batch_no, type: String
|
987
|
-
requires :quantity, type: Integer
|
988
|
-
end
|
989
|
-
end
|
990
|
-
end
|
991
|
-
|
992
|
-
subject.get '/correctly_indexes' do
|
993
|
-
'correctly_indexes works!'
|
994
|
-
end
|
995
|
-
|
996
|
-
data = {
|
997
|
-
orders: [
|
998
|
-
{ id: 70 },
|
999
|
-
{ id: 77, drugs: [{ batch_no: 'A1234567', quantity: 12 }, { batch_no: 'B222222' }] }
|
1000
|
-
]
|
1001
|
-
}
|
1002
|
-
|
1003
|
-
get '/correctly_indexes', data
|
1004
|
-
expect(last_response.body).to eq('orders[1][drugs][1][quantity] is missing')
|
1005
|
-
expect(last_response.status).to eq(400)
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
context 'multiple levels of optional and requires settings' do
|
1009
|
-
before do
|
1010
|
-
subject.params do
|
1011
|
-
requires :top, type: Array do
|
1012
|
-
requires :top_id, type: Integer, allow_blank: false
|
1013
|
-
optional :middle_1, type: Array do
|
1014
|
-
requires :middle_1_id, type: Integer, allow_blank: false
|
1015
|
-
optional :middle_2, type: Array do
|
1016
|
-
requires :middle_2_id, type: String, allow_blank: false
|
1017
|
-
optional :bottom, type: Array do
|
1018
|
-
requires :bottom_id, type: Integer, allow_blank: false
|
1019
|
-
end
|
1020
|
-
end
|
1021
|
-
end
|
1022
|
-
end
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
subject.get '/multi_level' do
|
1026
|
-
'multi_level works!'
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
it 'with valid data' do
|
1031
|
-
data = {
|
1032
|
-
top: [
|
1033
|
-
{ top_id: 1, middle_1: [
|
1034
|
-
{ middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
|
1035
|
-
{ middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: 1221 }] }
|
1036
|
-
] }
|
1037
|
-
] },
|
1038
|
-
{ top_id: 2, middle_1: [
|
1039
|
-
{ middle_1_id: 21 }, { middle_1_id: 22, middle_2: [
|
1040
|
-
{ middle_2_id: 221 }
|
1041
|
-
] }
|
1042
|
-
] },
|
1043
|
-
{ top_id: 3, middle_1: [
|
1044
|
-
{ middle_1_id: 31 }, { middle_1_id: 32 }
|
1045
|
-
] },
|
1046
|
-
{ top_id: 4 }
|
1047
|
-
]
|
1048
|
-
}
|
1049
|
-
|
1050
|
-
get '/multi_level', data
|
1051
|
-
expect(last_response.body).to eq('multi_level works!')
|
1052
|
-
expect(last_response.status).to eq(200)
|
1053
|
-
end
|
1054
|
-
|
1055
|
-
it 'with invalid data' do
|
1056
|
-
data = {
|
1057
|
-
top: [
|
1058
|
-
{ top_id: 1, middle_1: [
|
1059
|
-
{ middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
|
1060
|
-
{ middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: nil }] }
|
1061
|
-
] }
|
1062
|
-
] },
|
1063
|
-
{ top_id: 2, middle_1: [
|
1064
|
-
{ middle_1_id: 21 }, { middle_1_id: 22, middle_2: [{ middle_2_id: nil }] }
|
1065
|
-
] },
|
1066
|
-
{ top_id: 3, middle_1: [
|
1067
|
-
{ middle_1_id: nil }, { middle_1_id: 32 }
|
1068
|
-
] },
|
1069
|
-
{ top_id: nil, missing_top_id: 4 }
|
1070
|
-
]
|
1071
|
-
}
|
1072
|
-
# debugger
|
1073
|
-
get '/multi_level', data
|
1074
|
-
expect(last_response.body.split(', ')).to contain_exactly(
|
1075
|
-
'top[3][top_id] is empty',
|
1076
|
-
'top[2][middle_1][0][middle_1_id] is empty',
|
1077
|
-
'top[1][middle_1][1][middle_2][0][middle_2_id] is empty',
|
1078
|
-
'top[0][middle_1][1][middle_2][1][bottom][0][bottom_id] is empty'
|
1079
|
-
)
|
1080
|
-
expect(last_response.status).to eq(400)
|
1081
|
-
end
|
1082
|
-
end
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
it 'exactly_one_of' do
|
1086
|
-
subject.params do
|
1087
|
-
requires :orders, type: Array do
|
1088
|
-
requires :id, type: Integer
|
1089
|
-
optional :drugs, type: Hash do
|
1090
|
-
optional :batch_no, type: String
|
1091
|
-
optional :batch_id, type: String
|
1092
|
-
exactly_one_of :batch_no, :batch_id
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
end
|
1096
|
-
|
1097
|
-
subject.get '/exactly_one_of' do
|
1098
|
-
'exactly_one_of works!'
|
1099
|
-
end
|
1100
|
-
|
1101
|
-
data = {
|
1102
|
-
orders: [
|
1103
|
-
{ id: 77, drugs: { batch_no: 'A1234567' } },
|
1104
|
-
{ id: 70 }
|
1105
|
-
]
|
1106
|
-
}
|
1107
|
-
|
1108
|
-
get '/exactly_one_of', data
|
1109
|
-
expect(last_response.body).to eq('exactly_one_of works!')
|
1110
|
-
expect(last_response.status).to eq(200)
|
1111
|
-
end
|
1112
|
-
|
1113
|
-
it 'at_least_one_of' do
|
1114
|
-
subject.params do
|
1115
|
-
requires :orders, type: Array do
|
1116
|
-
requires :id, type: Integer
|
1117
|
-
optional :drugs, type: Hash do
|
1118
|
-
optional :batch_no, type: String
|
1119
|
-
optional :batch_id, type: String
|
1120
|
-
at_least_one_of :batch_no, :batch_id
|
1121
|
-
end
|
1122
|
-
end
|
1123
|
-
end
|
1124
|
-
|
1125
|
-
subject.get '/at_least_one_of' do
|
1126
|
-
'at_least_one_of works!'
|
1127
|
-
end
|
1128
|
-
|
1129
|
-
data = {
|
1130
|
-
orders: [
|
1131
|
-
{ id: 77, drugs: { batch_no: 'A1234567' } },
|
1132
|
-
{ id: 70 }
|
1133
|
-
]
|
1134
|
-
}
|
1135
|
-
|
1136
|
-
get '/at_least_one_of', data
|
1137
|
-
expect(last_response.body).to eq('at_least_one_of works!')
|
1138
|
-
expect(last_response.status).to eq(200)
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
it 'all_or_none_of' do
|
1142
|
-
subject.params do
|
1143
|
-
requires :orders, type: Array do
|
1144
|
-
requires :id, type: Integer
|
1145
|
-
optional :drugs, type: Hash do
|
1146
|
-
optional :batch_no, type: String
|
1147
|
-
optional :batch_id, type: String
|
1148
|
-
all_or_none_of :batch_no, :batch_id
|
1149
|
-
end
|
1150
|
-
end
|
1151
|
-
end
|
1152
|
-
|
1153
|
-
subject.get '/all_or_none_of' do
|
1154
|
-
'all_or_none_of works!'
|
1155
|
-
end
|
1156
|
-
|
1157
|
-
data = {
|
1158
|
-
orders: [
|
1159
|
-
{ id: 77, drugs: { batch_no: 'A1234567', batch_id: '12' } },
|
1160
|
-
{ id: 70 }
|
1161
|
-
]
|
1162
|
-
}
|
1163
|
-
|
1164
|
-
get '/all_or_none_of', data
|
1165
|
-
expect(last_response.body).to eq('all_or_none_of works!')
|
1166
|
-
expect(last_response.status).to eq(200)
|
1167
|
-
end
|
1168
|
-
end
|
1169
|
-
|
1170
|
-
context 'multiple validation errors' do
|
1171
|
-
before do
|
1172
|
-
subject.params do
|
1173
|
-
requires :yolo
|
1174
|
-
requires :swag
|
1175
|
-
end
|
1176
|
-
subject.get '/two_required' do
|
1177
|
-
'two required works'
|
1178
|
-
end
|
1179
|
-
end
|
1180
|
-
|
1181
|
-
it 'throws the validation errors' do
|
1182
|
-
get '/two_required'
|
1183
|
-
expect(last_response.status).to eq(400)
|
1184
|
-
expect(last_response.body).to match(/yolo is missing/)
|
1185
|
-
expect(last_response.body).to match(/swag is missing/)
|
1186
|
-
end
|
1187
|
-
end
|
1188
|
-
|
1189
|
-
context 'custom validation' do
|
1190
|
-
let(:custom_validator) do
|
1191
|
-
Class.new(Grape::Validations::Validators::Base) do
|
1192
|
-
def validate_param!(attr_name, params)
|
1193
|
-
return if params[attr_name] == 'im custom'
|
1194
|
-
|
1195
|
-
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
|
1196
|
-
end
|
1197
|
-
end
|
1198
|
-
end
|
1199
|
-
|
1200
|
-
before do
|
1201
|
-
described_class.register_validator('customvalidator', custom_validator)
|
1202
|
-
end
|
1203
|
-
|
1204
|
-
after do
|
1205
|
-
described_class.deregister_validator('customvalidator')
|
1206
|
-
end
|
1207
|
-
|
1208
|
-
context 'when using optional with a custom validator' do
|
1209
|
-
before do
|
1210
|
-
subject.params do
|
1211
|
-
optional :custom, customvalidator: true
|
1212
|
-
end
|
1213
|
-
subject.get '/optional_custom' do
|
1214
|
-
'optional with custom works!'
|
1215
|
-
end
|
1216
|
-
end
|
1217
|
-
|
1218
|
-
it 'validates when param is present' do
|
1219
|
-
get '/optional_custom', custom: 'im custom'
|
1220
|
-
expect(last_response.status).to eq(200)
|
1221
|
-
expect(last_response.body).to eq('optional with custom works!')
|
1222
|
-
|
1223
|
-
get '/optional_custom', custom: 'im wrong'
|
1224
|
-
expect(last_response.status).to eq(400)
|
1225
|
-
expect(last_response.body).to eq('custom is not custom!')
|
1226
|
-
end
|
1227
|
-
|
1228
|
-
it "skips validation when parameter isn't present" do
|
1229
|
-
get '/optional_custom'
|
1230
|
-
expect(last_response.status).to eq(200)
|
1231
|
-
expect(last_response.body).to eq('optional with custom works!')
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
it 'validates with custom validator when param present and incorrect type' do
|
1235
|
-
subject.params do
|
1236
|
-
optional :custom, type: String, customvalidator: true
|
1237
|
-
end
|
1238
|
-
|
1239
|
-
get '/optional_custom', custom: 123
|
1240
|
-
expect(last_response.status).to eq(400)
|
1241
|
-
expect(last_response.body).to eq('custom is not custom!')
|
1242
|
-
end
|
1243
|
-
end
|
1244
|
-
|
1245
|
-
context 'when using requires with a custom validator' do
|
1246
|
-
before do
|
1247
|
-
subject.params do
|
1248
|
-
requires :custom, customvalidator: true
|
1249
|
-
end
|
1250
|
-
subject.get '/required_custom' do
|
1251
|
-
'required with custom works!'
|
1252
|
-
end
|
1253
|
-
end
|
1254
|
-
|
1255
|
-
it 'validates when param is present' do
|
1256
|
-
get '/required_custom', custom: 'im wrong, validate me'
|
1257
|
-
expect(last_response.status).to eq(400)
|
1258
|
-
expect(last_response.body).to eq('custom is not custom!')
|
1259
|
-
|
1260
|
-
get '/required_custom', custom: 'im custom'
|
1261
|
-
expect(last_response.status).to eq(200)
|
1262
|
-
expect(last_response.body).to eq('required with custom works!')
|
1263
|
-
end
|
1264
|
-
|
1265
|
-
it 'validates when param is not present' do
|
1266
|
-
get '/required_custom'
|
1267
|
-
expect(last_response.status).to eq(400)
|
1268
|
-
expect(last_response.body).to eq('custom is missing, custom is not custom!')
|
1269
|
-
end
|
1270
|
-
|
1271
|
-
context 'nested namespaces' do
|
1272
|
-
before do
|
1273
|
-
subject.params do
|
1274
|
-
requires :custom, customvalidator: true
|
1275
|
-
end
|
1276
|
-
subject.namespace 'nested' do
|
1277
|
-
get 'one' do
|
1278
|
-
'validation failed'
|
1279
|
-
end
|
1280
|
-
namespace 'nested' do
|
1281
|
-
get 'two' do
|
1282
|
-
'validation failed'
|
1283
|
-
end
|
1284
|
-
end
|
1285
|
-
end
|
1286
|
-
subject.namespace 'peer' do
|
1287
|
-
get 'one' do
|
1288
|
-
'no validation required'
|
1289
|
-
end
|
1290
|
-
namespace 'nested' do
|
1291
|
-
get 'two' do
|
1292
|
-
'no validation required'
|
1293
|
-
end
|
1294
|
-
end
|
1295
|
-
end
|
1296
|
-
|
1297
|
-
subject.namespace 'unrelated' do
|
1298
|
-
params do
|
1299
|
-
requires :name
|
1300
|
-
end
|
1301
|
-
get 'one' do
|
1302
|
-
'validation required'
|
1303
|
-
end
|
1304
|
-
|
1305
|
-
namespace 'double' do
|
1306
|
-
get 'two' do
|
1307
|
-
'no validation required'
|
1308
|
-
end
|
1309
|
-
end
|
1310
|
-
end
|
1311
|
-
end
|
1312
|
-
|
1313
|
-
specify 'the parent namespace uses the validator' do
|
1314
|
-
get '/nested/one', custom: 'im wrong, validate me'
|
1315
|
-
expect(last_response.status).to eq(400)
|
1316
|
-
expect(last_response.body).to eq('custom is not custom!')
|
1317
|
-
end
|
1318
|
-
|
1319
|
-
specify 'the nested namespace inherits the custom validator' do
|
1320
|
-
get '/nested/nested/two', custom: 'im wrong, validate me'
|
1321
|
-
expect(last_response.status).to eq(400)
|
1322
|
-
expect(last_response.body).to eq('custom is not custom!')
|
1323
|
-
end
|
1324
|
-
|
1325
|
-
specify 'peer namespaces does not have the validator' do
|
1326
|
-
get '/peer/one', custom: 'im not validated'
|
1327
|
-
expect(last_response.status).to eq(200)
|
1328
|
-
expect(last_response.body).to eq('no validation required')
|
1329
|
-
end
|
1330
|
-
|
1331
|
-
specify 'namespaces nested in peers should also not have the validator' do
|
1332
|
-
get '/peer/nested/two', custom: 'im not validated'
|
1333
|
-
expect(last_response.status).to eq(200)
|
1334
|
-
expect(last_response.body).to eq('no validation required')
|
1335
|
-
end
|
1336
|
-
|
1337
|
-
specify 'when nested, specifying a route should clear out the validations for deeper nested params' do
|
1338
|
-
get '/unrelated/one'
|
1339
|
-
expect(last_response.status).to eq(400)
|
1340
|
-
get '/unrelated/double/two'
|
1341
|
-
expect(last_response.status).to eq(200)
|
1342
|
-
end
|
1343
|
-
end
|
1344
|
-
end
|
1345
|
-
|
1346
|
-
context 'when using options on param' do
|
1347
|
-
let(:custom_validator_with_options) do
|
1348
|
-
Class.new(Grape::Validations::Validators::Base) do
|
1349
|
-
def validate_param!(attr_name, params)
|
1350
|
-
return if params[attr_name] == @option[:text]
|
1351
|
-
|
1352
|
-
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
|
1353
|
-
end
|
1354
|
-
end
|
1355
|
-
end
|
1356
|
-
|
1357
|
-
before do
|
1358
|
-
described_class.register_validator('customvalidator_with_options', custom_validator_with_options)
|
1359
|
-
end
|
1360
|
-
|
1361
|
-
after do
|
1362
|
-
described_class.deregister_validator('customvalidator_with_options')
|
1363
|
-
end
|
1364
|
-
|
1365
|
-
before do
|
1366
|
-
subject.params do
|
1367
|
-
optional :custom, customvalidator_with_options: { text: 'im custom with options', message: 'is not custom with options!' }
|
1368
|
-
end
|
1369
|
-
subject.get '/optional_custom' do
|
1370
|
-
'optional with custom works!'
|
1371
|
-
end
|
1372
|
-
end
|
1373
|
-
|
1374
|
-
it 'validates param with custom validator with options' do
|
1375
|
-
get '/optional_custom', custom: 'im custom with options'
|
1376
|
-
expect(last_response.status).to eq(200)
|
1377
|
-
expect(last_response.body).to eq('optional with custom works!')
|
1378
|
-
|
1379
|
-
get '/optional_custom', custom: 'im wrong'
|
1380
|
-
expect(last_response.status).to eq(400)
|
1381
|
-
expect(last_response.body).to eq('custom is not custom with options!')
|
1382
|
-
end
|
1383
|
-
end
|
1384
|
-
end
|
1385
|
-
|
1386
|
-
context 'named' do
|
1387
|
-
context 'can be defined' do
|
1388
|
-
it 'in helpers' do
|
1389
|
-
subject.helpers do
|
1390
|
-
params :pagination do
|
1391
|
-
end
|
1392
|
-
end
|
1393
|
-
end
|
1394
|
-
|
1395
|
-
it 'in helper module which kind of Grape::DSL::Helpers::BaseHelper' do
|
1396
|
-
shared_params = Module.new do
|
1397
|
-
extend Grape::DSL::Helpers::BaseHelper
|
1398
|
-
params :pagination do
|
1399
|
-
end
|
1400
|
-
end
|
1401
|
-
subject.helpers shared_params
|
1402
|
-
end
|
1403
|
-
end
|
1404
|
-
|
1405
|
-
context 'can be included in usual params' do
|
1406
|
-
before do
|
1407
|
-
shared_params = Module.new do
|
1408
|
-
extend Grape::DSL::Helpers::BaseHelper
|
1409
|
-
params :period do
|
1410
|
-
optional :start_date
|
1411
|
-
optional :end_date
|
1412
|
-
end
|
1413
|
-
end
|
1414
|
-
|
1415
|
-
subject.helpers shared_params
|
1416
|
-
|
1417
|
-
subject.helpers do
|
1418
|
-
params :pagination do
|
1419
|
-
optional :page, type: Integer
|
1420
|
-
optional :per_page, type: Integer
|
1421
|
-
end
|
1422
|
-
end
|
1423
|
-
end
|
1424
|
-
|
1425
|
-
it 'by #use' do
|
1426
|
-
subject.params do
|
1427
|
-
use :pagination
|
1428
|
-
end
|
1429
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq %i[page per_page]
|
1430
|
-
end
|
1431
|
-
|
1432
|
-
it 'by #use with multiple params' do
|
1433
|
-
subject.params do
|
1434
|
-
use :pagination, :period
|
1435
|
-
end
|
1436
|
-
expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq %i[page per_page start_date end_date]
|
1437
|
-
end
|
1438
|
-
end
|
1439
|
-
|
1440
|
-
context 'with block' do
|
1441
|
-
before do
|
1442
|
-
subject.helpers do
|
1443
|
-
params :order do |options|
|
1444
|
-
optional :order, type: Symbol, values: %i[asc desc], default: options[:default_order]
|
1445
|
-
optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
|
1446
|
-
end
|
1447
|
-
end
|
1448
|
-
subject.format :json
|
1449
|
-
subject.params do
|
1450
|
-
use :order, default_order: :asc, order_by: %i[name created_at], default_order_by: :created_at
|
1451
|
-
end
|
1452
|
-
subject.get '/order' do
|
1453
|
-
{
|
1454
|
-
order: params[:order],
|
1455
|
-
order_by: params[:order_by]
|
1456
|
-
}
|
1457
|
-
end
|
1458
|
-
end
|
1459
|
-
|
1460
|
-
it 'returns defaults' do
|
1461
|
-
get '/order'
|
1462
|
-
expect(last_response.status).to eq(200)
|
1463
|
-
expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
|
1464
|
-
end
|
1465
|
-
|
1466
|
-
it 'overrides default value for order' do
|
1467
|
-
get '/order?order=desc'
|
1468
|
-
expect(last_response.status).to eq(200)
|
1469
|
-
expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
|
1470
|
-
end
|
1471
|
-
|
1472
|
-
it 'overrides default value for order_by' do
|
1473
|
-
get '/order?order_by=name'
|
1474
|
-
expect(last_response.status).to eq(200)
|
1475
|
-
expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
|
1476
|
-
end
|
1477
|
-
|
1478
|
-
it 'fails with invalid value' do
|
1479
|
-
get '/order?order=invalid'
|
1480
|
-
expect(last_response.status).to eq(400)
|
1481
|
-
expect(last_response.body).to eq('{"error":"order does not have a valid value"}')
|
1482
|
-
end
|
1483
|
-
end
|
1484
|
-
end
|
1485
|
-
|
1486
|
-
context 'with block and keyword argument' do
|
1487
|
-
before do
|
1488
|
-
subject.helpers do
|
1489
|
-
params :shared_params do |type:|
|
1490
|
-
optional :param, default: type
|
1491
|
-
end
|
1492
|
-
end
|
1493
|
-
subject.format :json
|
1494
|
-
subject.params do
|
1495
|
-
use :shared_params, type: 'value'
|
1496
|
-
end
|
1497
|
-
subject.get '/shared_params' do
|
1498
|
-
{
|
1499
|
-
param: params[:param]
|
1500
|
-
}
|
1501
|
-
end
|
1502
|
-
end
|
1503
|
-
|
1504
|
-
it 'works' do
|
1505
|
-
get '/shared_params'
|
1506
|
-
|
1507
|
-
expect(last_response.status).to eq(200)
|
1508
|
-
expect(last_response.body).to eq({ param: 'value' }.to_json)
|
1509
|
-
end
|
1510
|
-
end
|
1511
|
-
|
1512
|
-
context 'with block and empty args' do
|
1513
|
-
before do
|
1514
|
-
subject.helpers do
|
1515
|
-
params :shared_params do |empty_args|
|
1516
|
-
optional :param, default: empty_args[:some]
|
1517
|
-
end
|
1518
|
-
end
|
1519
|
-
subject.format :json
|
1520
|
-
subject.params do
|
1521
|
-
use :shared_params
|
1522
|
-
end
|
1523
|
-
subject.get '/shared_params' do
|
1524
|
-
:ok
|
1525
|
-
end
|
1526
|
-
end
|
1527
|
-
|
1528
|
-
it 'works' do
|
1529
|
-
get '/shared_params'
|
1530
|
-
|
1531
|
-
expect(last_response.status).to eq(200)
|
1532
|
-
end
|
1533
|
-
end
|
1534
|
-
|
1535
|
-
context 'all or none' do
|
1536
|
-
context 'optional params' do
|
1537
|
-
before do
|
1538
|
-
subject.resource :custom_message do
|
1539
|
-
params do
|
1540
|
-
optional :beer
|
1541
|
-
optional :wine
|
1542
|
-
optional :juice
|
1543
|
-
all_or_none_of :beer, :wine, :juice, message: 'all params are required or none is required'
|
1544
|
-
end
|
1545
|
-
get '/all_or_none' do
|
1546
|
-
'all_or_none works!'
|
1547
|
-
end
|
1548
|
-
end
|
1549
|
-
end
|
1550
|
-
|
1551
|
-
context 'with a custom validation message' do
|
1552
|
-
it 'errors when any one is present' do
|
1553
|
-
get '/custom_message/all_or_none', beer: 'string'
|
1554
|
-
expect(last_response.status).to eq(400)
|
1555
|
-
expect(last_response.body).to eq 'beer, wine, juice all params are required or none is required'
|
1556
|
-
end
|
1557
|
-
|
1558
|
-
it 'works when all params are present' do
|
1559
|
-
get '/custom_message/all_or_none', beer: 'string', wine: 'anotherstring', juice: 'anotheranotherstring'
|
1560
|
-
expect(last_response.status).to eq(200)
|
1561
|
-
expect(last_response.body).to eq 'all_or_none works!'
|
1562
|
-
end
|
1563
|
-
|
1564
|
-
it 'works when none are present' do
|
1565
|
-
get '/custom_message/all_or_none'
|
1566
|
-
expect(last_response.status).to eq(200)
|
1567
|
-
expect(last_response.body).to eq 'all_or_none works!'
|
1568
|
-
end
|
1569
|
-
end
|
1570
|
-
end
|
1571
|
-
end
|
1572
|
-
|
1573
|
-
context 'mutually exclusive' do
|
1574
|
-
context 'optional params' do
|
1575
|
-
context 'with custom validation message' do
|
1576
|
-
it 'errors when two or more are present' do
|
1577
|
-
subject.resources :custom_message do
|
1578
|
-
params do
|
1579
|
-
optional :beer
|
1580
|
-
optional :wine
|
1581
|
-
optional :juice
|
1582
|
-
mutually_exclusive :beer, :wine, :juice, message: 'are mutually exclusive cannot pass both params'
|
1583
|
-
end
|
1584
|
-
get '/mutually_exclusive' do
|
1585
|
-
'mutually_exclusive works!'
|
1586
|
-
end
|
1587
|
-
end
|
1588
|
-
get '/custom_message/mutually_exclusive', beer: 'string', wine: 'anotherstring'
|
1589
|
-
expect(last_response.status).to eq(400)
|
1590
|
-
expect(last_response.body).to eq 'beer, wine are mutually exclusive cannot pass both params'
|
1591
|
-
end
|
1592
|
-
end
|
1593
|
-
|
1594
|
-
it 'errors when two or more are present' do
|
1595
|
-
subject.params do
|
1596
|
-
optional :beer
|
1597
|
-
optional :wine
|
1598
|
-
optional :juice
|
1599
|
-
mutually_exclusive :beer, :wine, :juice
|
1600
|
-
end
|
1601
|
-
subject.get '/mutually_exclusive' do
|
1602
|
-
'mutually_exclusive works!'
|
1603
|
-
end
|
1604
|
-
|
1605
|
-
get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
|
1606
|
-
expect(last_response.status).to eq(400)
|
1607
|
-
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
1608
|
-
end
|
1609
|
-
end
|
1610
|
-
|
1611
|
-
context 'more than one set of mutually exclusive params' do
|
1612
|
-
context 'with a custom validation message' do
|
1613
|
-
it 'errors for all sets' do
|
1614
|
-
subject.resources :custom_message do
|
1615
|
-
params do
|
1616
|
-
optional :beer
|
1617
|
-
optional :wine
|
1618
|
-
mutually_exclusive :beer, :wine, message: 'are mutually exclusive pass only one'
|
1619
|
-
optional :nested, type: Hash do
|
1620
|
-
optional :scotch
|
1621
|
-
optional :aquavit
|
1622
|
-
mutually_exclusive :scotch, :aquavit, message: 'are mutually exclusive pass only one'
|
1623
|
-
end
|
1624
|
-
optional :nested2, type: Array do
|
1625
|
-
optional :scotch2
|
1626
|
-
optional :aquavit2
|
1627
|
-
mutually_exclusive :scotch2, :aquavit2, message: 'are mutually exclusive pass only one'
|
1628
|
-
end
|
1629
|
-
end
|
1630
|
-
get '/mutually_exclusive' do
|
1631
|
-
'mutually_exclusive works!'
|
1632
|
-
end
|
1633
|
-
end
|
1634
|
-
get '/custom_message/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
|
1635
|
-
expect(last_response.status).to eq(400)
|
1636
|
-
expect(last_response.body).to eq(
|
1637
|
-
'beer, wine are mutually exclusive pass only one, nested[scotch], nested[aquavit] are mutually exclusive pass only one, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive pass only one'
|
1638
|
-
)
|
1639
|
-
end
|
1640
|
-
end
|
1641
|
-
|
1642
|
-
it 'errors for all sets' do
|
1643
|
-
subject.params do
|
1644
|
-
optional :beer
|
1645
|
-
optional :wine
|
1646
|
-
mutually_exclusive :beer, :wine
|
1647
|
-
optional :nested, type: Hash do
|
1648
|
-
optional :scotch
|
1649
|
-
optional :aquavit
|
1650
|
-
mutually_exclusive :scotch, :aquavit
|
1651
|
-
end
|
1652
|
-
optional :nested2, type: Array do
|
1653
|
-
optional :scotch2
|
1654
|
-
optional :aquavit2
|
1655
|
-
mutually_exclusive :scotch2, :aquavit2
|
1656
|
-
end
|
1657
|
-
end
|
1658
|
-
subject.get '/mutually_exclusive' do
|
1659
|
-
'mutually_exclusive works!'
|
1660
|
-
end
|
1661
|
-
|
1662
|
-
get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
|
1663
|
-
expect(last_response.status).to eq(400)
|
1664
|
-
expect(last_response.body).to eq 'beer, wine are mutually exclusive, nested[scotch], nested[aquavit] are mutually exclusive, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive'
|
1665
|
-
end
|
1666
|
-
end
|
1667
|
-
|
1668
|
-
context 'in a group' do
|
1669
|
-
it 'works when only one from the set is present' do
|
1670
|
-
subject.params do
|
1671
|
-
group :drink, type: Hash do
|
1672
|
-
optional :wine
|
1673
|
-
optional :beer
|
1674
|
-
optional :juice
|
1675
|
-
mutually_exclusive :beer, :wine, :juice
|
1676
|
-
end
|
1677
|
-
end
|
1678
|
-
subject.get '/mutually_exclusive_group' do
|
1679
|
-
'mutually_exclusive_group works!'
|
1680
|
-
end
|
1681
|
-
|
1682
|
-
get '/mutually_exclusive_group', drink: { beer: 'true' }
|
1683
|
-
expect(last_response.status).to eq(200)
|
1684
|
-
end
|
1685
|
-
|
1686
|
-
it 'errors when more than one from the set is present' do
|
1687
|
-
subject.params do
|
1688
|
-
group :drink, type: Hash do
|
1689
|
-
optional :wine
|
1690
|
-
optional :beer
|
1691
|
-
optional :juice
|
1692
|
-
|
1693
|
-
mutually_exclusive :beer, :wine, :juice
|
1694
|
-
end
|
1695
|
-
end
|
1696
|
-
subject.get '/mutually_exclusive_group' do
|
1697
|
-
'mutually_exclusive_group works!'
|
1698
|
-
end
|
1699
|
-
|
1700
|
-
get '/mutually_exclusive_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
|
1701
|
-
expect(last_response.status).to eq(400)
|
1702
|
-
end
|
1703
|
-
end
|
1704
|
-
|
1705
|
-
context 'mutually exclusive params inside Hash group' do
|
1706
|
-
it 'invalidates if request param is invalid type' do
|
1707
|
-
subject.params do
|
1708
|
-
optional :wine, type: Hash do
|
1709
|
-
optional :grape
|
1710
|
-
optional :country
|
1711
|
-
mutually_exclusive :grape, :country
|
1712
|
-
end
|
1713
|
-
end
|
1714
|
-
subject.post '/mutually_exclusive' do
|
1715
|
-
'mutually_exclusive works!'
|
1716
|
-
end
|
1717
|
-
|
1718
|
-
post '/mutually_exclusive', wine: '2015 sauvignon'
|
1719
|
-
expect(last_response.status).to eq(400)
|
1720
|
-
expect(last_response.body).to eq 'wine is invalid'
|
1721
|
-
end
|
1722
|
-
end
|
1723
|
-
end
|
1724
|
-
|
1725
|
-
context 'exactly one of' do
|
1726
|
-
context 'params' do
|
1727
|
-
before do
|
1728
|
-
subject.resources :custom_message do
|
1729
|
-
params do
|
1730
|
-
optional :beer
|
1731
|
-
optional :wine
|
1732
|
-
optional :juice
|
1733
|
-
exactly_one_of :beer, :wine, :juice, message: 'are missing, exactly one parameter is required'
|
1734
|
-
end
|
1735
|
-
get '/exactly_one_of' do
|
1736
|
-
'exactly_one_of works!'
|
1737
|
-
end
|
1738
|
-
end
|
1739
|
-
|
1740
|
-
subject.params do
|
1741
|
-
optional :beer
|
1742
|
-
optional :wine
|
1743
|
-
optional :juice
|
1744
|
-
exactly_one_of :beer, :wine, :juice
|
1745
|
-
end
|
1746
|
-
subject.get '/exactly_one_of' do
|
1747
|
-
'exactly_one_of works!'
|
1748
|
-
end
|
1749
|
-
end
|
1750
|
-
|
1751
|
-
context 'with a custom validation message' do
|
1752
|
-
it 'errors when none are present' do
|
1753
|
-
get '/custom_message/exactly_one_of'
|
1754
|
-
expect(last_response.status).to eq(400)
|
1755
|
-
expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter is required'
|
1756
|
-
end
|
1757
|
-
|
1758
|
-
it 'succeeds when one is present' do
|
1759
|
-
get '/custom_message/exactly_one_of', beer: 'string'
|
1760
|
-
expect(last_response.status).to eq(200)
|
1761
|
-
expect(last_response.body).to eq 'exactly_one_of works!'
|
1762
|
-
end
|
1763
|
-
|
1764
|
-
it 'errors when two or more are present' do
|
1765
|
-
get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1766
|
-
expect(last_response.status).to eq(400)
|
1767
|
-
expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
|
1768
|
-
end
|
1769
|
-
end
|
1770
|
-
|
1771
|
-
it 'errors when none are present' do
|
1772
|
-
get '/exactly_one_of'
|
1773
|
-
expect(last_response.status).to eq(400)
|
1774
|
-
expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
|
1775
|
-
end
|
1776
|
-
|
1777
|
-
it 'succeeds when one is present' do
|
1778
|
-
get '/exactly_one_of', beer: 'string'
|
1779
|
-
expect(last_response.status).to eq(200)
|
1780
|
-
expect(last_response.body).to eq 'exactly_one_of works!'
|
1781
|
-
end
|
1782
|
-
|
1783
|
-
it 'errors when two or more are present' do
|
1784
|
-
get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1785
|
-
expect(last_response.status).to eq(400)
|
1786
|
-
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
1787
|
-
end
|
1788
|
-
end
|
1789
|
-
|
1790
|
-
context 'nested params' do
|
1791
|
-
before do
|
1792
|
-
subject.params do
|
1793
|
-
requires :nested, type: Hash do
|
1794
|
-
optional :beer_nested
|
1795
|
-
optional :wine_nested
|
1796
|
-
optional :juice_nested
|
1797
|
-
exactly_one_of :beer_nested, :wine_nested, :juice_nested
|
1798
|
-
end
|
1799
|
-
optional :nested2, type: Array do
|
1800
|
-
optional :beer_nested2
|
1801
|
-
optional :wine_nested2
|
1802
|
-
optional :juice_nested2
|
1803
|
-
exactly_one_of :beer_nested2, :wine_nested2, :juice_nested2
|
1804
|
-
end
|
1805
|
-
end
|
1806
|
-
subject.get '/exactly_one_of_nested' do
|
1807
|
-
'exactly_one_of works!'
|
1808
|
-
end
|
1809
|
-
end
|
1810
|
-
|
1811
|
-
it 'errors when none are present' do
|
1812
|
-
get '/exactly_one_of_nested'
|
1813
|
-
expect(last_response.status).to eq(400)
|
1814
|
-
expect(last_response.body).to eq 'nested is missing, nested[beer_nested], nested[wine_nested], nested[juice_nested] are missing, exactly one parameter must be provided'
|
1815
|
-
end
|
1816
|
-
|
1817
|
-
it 'succeeds when one is present' do
|
1818
|
-
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }
|
1819
|
-
expect(last_response.status).to eq(200)
|
1820
|
-
expect(last_response.body).to eq 'exactly_one_of works!'
|
1821
|
-
end
|
1822
|
-
|
1823
|
-
it 'errors when two or more are present' do
|
1824
|
-
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
|
1825
|
-
expect(last_response.status).to eq(400)
|
1826
|
-
expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
|
1827
|
-
end
|
1828
|
-
end
|
1829
|
-
end
|
1830
|
-
|
1831
|
-
context 'at least one of' do
|
1832
|
-
context 'params' do
|
1833
|
-
before do
|
1834
|
-
subject.resources :custom_message do
|
1835
|
-
params do
|
1836
|
-
optional :beer
|
1837
|
-
optional :wine
|
1838
|
-
optional :juice
|
1839
|
-
at_least_one_of :beer, :wine, :juice, message: 'are missing, please specify at least one param'
|
1840
|
-
end
|
1841
|
-
get '/at_least_one_of' do
|
1842
|
-
'at_least_one_of works!'
|
1843
|
-
end
|
1844
|
-
end
|
1845
|
-
|
1846
|
-
subject.params do
|
1847
|
-
optional :beer
|
1848
|
-
optional :wine
|
1849
|
-
optional :juice
|
1850
|
-
at_least_one_of :beer, :wine, :juice
|
1851
|
-
end
|
1852
|
-
subject.get '/at_least_one_of' do
|
1853
|
-
'at_least_one_of works!'
|
1854
|
-
end
|
1855
|
-
end
|
1856
|
-
|
1857
|
-
context 'with a custom validation message' do
|
1858
|
-
it 'errors when none are present' do
|
1859
|
-
get '/custom_message/at_least_one_of'
|
1860
|
-
expect(last_response.status).to eq(400)
|
1861
|
-
expect(last_response.body).to eq 'beer, wine, juice are missing, please specify at least one param'
|
1862
|
-
end
|
1863
|
-
|
1864
|
-
it 'does not error when one is present' do
|
1865
|
-
get '/custom_message/at_least_one_of', beer: 'string'
|
1866
|
-
expect(last_response.status).to eq(200)
|
1867
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1868
|
-
end
|
1869
|
-
|
1870
|
-
it 'does not error when two are present' do
|
1871
|
-
get '/custom_message/at_least_one_of', beer: 'string', wine: 'string'
|
1872
|
-
expect(last_response.status).to eq(200)
|
1873
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1874
|
-
end
|
1875
|
-
end
|
1876
|
-
|
1877
|
-
it 'errors when none are present' do
|
1878
|
-
get '/at_least_one_of'
|
1879
|
-
expect(last_response.status).to eq(400)
|
1880
|
-
expect(last_response.body).to eq 'beer, wine, juice are missing, at least one parameter must be provided'
|
1881
|
-
end
|
1882
|
-
|
1883
|
-
it 'does not error when one is present' do
|
1884
|
-
get '/at_least_one_of', beer: 'string'
|
1885
|
-
expect(last_response.status).to eq(200)
|
1886
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1887
|
-
end
|
1888
|
-
|
1889
|
-
it 'does not error when two are present' do
|
1890
|
-
get '/at_least_one_of', beer: 'string', wine: 'string'
|
1891
|
-
expect(last_response.status).to eq(200)
|
1892
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1893
|
-
end
|
1894
|
-
end
|
1895
|
-
|
1896
|
-
context 'nested params' do
|
1897
|
-
before do
|
1898
|
-
subject.params do
|
1899
|
-
requires :nested, type: Hash do
|
1900
|
-
optional :beer
|
1901
|
-
optional :wine
|
1902
|
-
optional :juice
|
1903
|
-
at_least_one_of :beer, :wine, :juice
|
1904
|
-
end
|
1905
|
-
optional :nested2, type: Array do
|
1906
|
-
optional :beer
|
1907
|
-
optional :wine
|
1908
|
-
optional :juice
|
1909
|
-
at_least_one_of :beer, :wine, :juice
|
1910
|
-
end
|
1911
|
-
end
|
1912
|
-
subject.get '/at_least_one_of_nested' do
|
1913
|
-
'at_least_one_of works!'
|
1914
|
-
end
|
1915
|
-
end
|
1916
|
-
|
1917
|
-
it 'errors when none are present' do
|
1918
|
-
get '/at_least_one_of_nested'
|
1919
|
-
expect(last_response.status).to eq(400)
|
1920
|
-
expect(last_response.body).to eq 'nested is missing, nested[beer], nested[wine], nested[juice] are missing, at least one parameter must be provided'
|
1921
|
-
end
|
1922
|
-
|
1923
|
-
it 'does not error when one is present' do
|
1924
|
-
get '/at_least_one_of_nested', nested: { beer: 'string' }, nested2: [{ beer: 'string' }]
|
1925
|
-
expect(last_response.status).to eq(200)
|
1926
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1927
|
-
end
|
1928
|
-
|
1929
|
-
it 'does not error when two are present' do
|
1930
|
-
get '/at_least_one_of_nested', nested: { beer: 'string', wine: 'string' }, nested2: [{ beer: 'string', wine: 'string' }]
|
1931
|
-
expect(last_response.status).to eq(200)
|
1932
|
-
expect(last_response.body).to eq 'at_least_one_of works!'
|
1933
|
-
end
|
1934
|
-
end
|
1935
|
-
end
|
1936
|
-
|
1937
|
-
context 'in a group' do
|
1938
|
-
it 'works when only one from the set is present' do
|
1939
|
-
subject.params do
|
1940
|
-
group :drink, type: Hash do
|
1941
|
-
optional :wine
|
1942
|
-
optional :beer
|
1943
|
-
optional :juice
|
1944
|
-
|
1945
|
-
exactly_one_of :beer, :wine, :juice
|
1946
|
-
end
|
1947
|
-
end
|
1948
|
-
subject.get '/exactly_one_of_group' do
|
1949
|
-
'exactly_one_of_group works!'
|
1950
|
-
end
|
1951
|
-
|
1952
|
-
get '/exactly_one_of_group', drink: { beer: 'true' }
|
1953
|
-
expect(last_response.status).to eq(200)
|
1954
|
-
end
|
1955
|
-
|
1956
|
-
it 'errors when no parameter from the set is present' do
|
1957
|
-
subject.params do
|
1958
|
-
group :drink, type: Hash do
|
1959
|
-
optional :wine
|
1960
|
-
optional :beer
|
1961
|
-
optional :juice
|
1962
|
-
|
1963
|
-
exactly_one_of :beer, :wine, :juice
|
1964
|
-
end
|
1965
|
-
end
|
1966
|
-
subject.get '/exactly_one_of_group' do
|
1967
|
-
'exactly_one_of_group works!'
|
1968
|
-
end
|
1969
|
-
|
1970
|
-
get '/exactly_one_of_group', drink: {}
|
1971
|
-
expect(last_response.status).to eq(400)
|
1972
|
-
end
|
1973
|
-
|
1974
|
-
it 'errors when more than one from the set is present' do
|
1975
|
-
subject.params do
|
1976
|
-
group :drink, type: Hash do
|
1977
|
-
optional :wine
|
1978
|
-
optional :beer
|
1979
|
-
optional :juice
|
1980
|
-
|
1981
|
-
exactly_one_of :beer, :wine, :juice
|
1982
|
-
end
|
1983
|
-
end
|
1984
|
-
subject.get '/exactly_one_of_group' do
|
1985
|
-
'exactly_one_of_group works!'
|
1986
|
-
end
|
1987
|
-
|
1988
|
-
get '/exactly_one_of_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
|
1989
|
-
expect(last_response.status).to eq(400)
|
1990
|
-
end
|
1991
|
-
|
1992
|
-
it 'does not falsely think the param is there if it is provided outside the block' do
|
1993
|
-
subject.params do
|
1994
|
-
group :drink, type: Hash do
|
1995
|
-
optional :wine
|
1996
|
-
optional :beer
|
1997
|
-
optional :juice
|
1998
|
-
|
1999
|
-
exactly_one_of :beer, :wine, :juice
|
2000
|
-
end
|
2001
|
-
end
|
2002
|
-
subject.get '/exactly_one_of_group' do
|
2003
|
-
'exactly_one_of_group works!'
|
2004
|
-
end
|
2005
|
-
|
2006
|
-
get '/exactly_one_of_group', drink: { foo: 'bar' }, beer: 'true'
|
2007
|
-
expect(last_response.status).to eq(400)
|
2008
|
-
end
|
2009
|
-
end
|
2010
|
-
end
|
2011
|
-
|
2012
|
-
describe 'require_validator' do
|
2013
|
-
subject { described_class.require_validator(short_name) }
|
2014
|
-
|
2015
|
-
context 'when found' do
|
2016
|
-
let(:short_name) { :presence }
|
2017
|
-
|
2018
|
-
it { is_expected.to be(Grape::Validations::Validators::PresenceValidator) }
|
2019
|
-
end
|
2020
|
-
|
2021
|
-
context 'when not found' do
|
2022
|
-
let(:short_name) { :test }
|
2023
|
-
|
2024
|
-
it 'raises an error' do
|
2025
|
-
expect { subject }.to raise_error(Grape::Exceptions::UnknownValidator)
|
2026
|
-
end
|
2027
|
-
end
|
2028
|
-
end
|
2029
|
-
end
|