grape 1.1.0 → 1.2.5
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 +128 -43
- data/LICENSE +1 -1
- data/README.md +394 -47
- data/UPGRADING.md +111 -0
- data/grape.gemspec +3 -1
- data/lib/grape.rb +98 -66
- data/lib/grape/api.rb +136 -175
- data/lib/grape/api/instance.rb +280 -0
- data/lib/grape/config.rb +32 -0
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/desc.rb +39 -7
- data/lib/grape/dsl/inside_route.rb +12 -6
- data/lib/grape/dsl/middleware.rb +7 -0
- data/lib/grape/dsl/parameters.rb +9 -4
- data/lib/grape/dsl/routing.rb +5 -1
- data/lib/grape/dsl/validations.rb +4 -3
- data/lib/grape/eager_load.rb +18 -0
- data/lib/grape/endpoint.rb +42 -26
- data/lib/grape/error_formatter.rb +1 -1
- data/lib/grape/exceptions/base.rb +9 -1
- data/lib/grape/exceptions/invalid_response.rb +9 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -2
- data/lib/grape/formatter.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/base.rb +2 -4
- data/lib/grape/middleware/base.rb +2 -0
- data/lib/grape/middleware/error.rb +9 -4
- data/lib/grape/middleware/helpers.rb +10 -0
- data/lib/grape/middleware/stack.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/parser.rb +1 -1
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router/attribute_translator.rb +2 -0
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/util/base_inheritable.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +6 -0
- data/lib/grape/util/inheritable_values.rb +5 -25
- data/lib/grape/util/lazy_block.rb +25 -0
- data/lib/grape/util/lazy_value.rb +95 -0
- data/lib/grape/util/reverse_stackable_values.rb +7 -36
- data/lib/grape/util/stackable_values.rb +19 -22
- data/lib/grape/validations/attributes_iterator.rb +5 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
- data/lib/grape/validations/params_scope.rb +20 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
- data/lib/grape/validations/types/file.rb +1 -1
- data/lib/grape/validations/validator_factory.rb +6 -11
- data/lib/grape/validations/validators/all_or_none.rb +6 -13
- data/lib/grape/validations/validators/as.rb +2 -3
- data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
- data/lib/grape/validations/validators/base.rb +11 -10
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
- data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
- data/lib/grape/validations/validators/same_as.rb +23 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api_remount_spec.rb +466 -0
- data/spec/grape/api_spec.rb +379 -1
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/desc_spec.rb +40 -16
- data/spec/grape/dsl/middleware_spec.rb +8 -0
- data/spec/grape/dsl/routing_spec.rb +10 -0
- data/spec/grape/endpoint_spec.rb +40 -4
- data/spec/grape/exceptions/base_spec.rb +65 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
- data/spec/grape/integration/rack_spec.rb +22 -6
- data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
- data/spec/grape/middleware/base_spec.rb +8 -0
- data/spec/grape/middleware/exception_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +15 -5
- data/spec/grape/middleware/versioner/header_spec.rb +6 -0
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/request_spec.rb +24 -0
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
- data/spec/grape/validations/params_scope_spec.rb +184 -8
- data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
- data/spec/grape/validations/validators/coerce_spec.rb +10 -2
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
- data/spec/grape/validations/validators/same_as_spec.rb +63 -0
- data/spec/grape/validations_spec.rb +33 -21
- data/spec/spec_helper.rb +4 -1
- metadata +35 -23
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
@@ -2,65 +2,209 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Grape::Validations::AtLeastOneOfValidator do
|
4
4
|
describe '#validate!' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
subject(:validate) { post path, params }
|
6
|
+
|
7
|
+
module ValidationsSpec
|
8
|
+
module AtLeastOneOfValidatorSpec
|
9
|
+
class API < Grape::API
|
10
|
+
rescue_from Grape::Exceptions::ValidationErrors do |e|
|
11
|
+
error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
|
12
|
+
end
|
13
|
+
|
14
|
+
params do
|
15
|
+
optional :beer, :wine, :grapefruit
|
16
|
+
at_least_one_of :beer, :wine, :grapefruit
|
17
|
+
end
|
18
|
+
post do
|
19
|
+
end
|
20
|
+
|
21
|
+
params do
|
22
|
+
optional :beer, :wine, :grapefruit, :other
|
23
|
+
at_least_one_of :beer, :wine, :grapefruit
|
24
|
+
end
|
25
|
+
post 'mixed-params' do
|
26
|
+
end
|
27
|
+
|
28
|
+
params do
|
29
|
+
optional :beer, :wine, :grapefruit
|
30
|
+
at_least_one_of :beer, :wine, :grapefruit, message: 'you should choose something'
|
31
|
+
end
|
32
|
+
post '/custom-message' do
|
33
|
+
end
|
34
|
+
|
35
|
+
params do
|
36
|
+
requires :item, type: Hash do
|
37
|
+
optional :beer, :wine, :grapefruit
|
38
|
+
at_least_one_of :beer, :wine, :grapefruit, message: 'fail'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
post '/nested-hash' do
|
42
|
+
end
|
10
43
|
|
11
|
-
|
44
|
+
params do
|
45
|
+
requires :items, type: Array do
|
46
|
+
optional :beer, :wine, :grapefruit
|
47
|
+
at_least_one_of :beer, :wine, :grapefruit, message: 'fail'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
post '/nested-array' do
|
51
|
+
end
|
52
|
+
|
53
|
+
params do
|
54
|
+
requires :items, type: Array do
|
55
|
+
requires :nested_items, type: Array do
|
56
|
+
optional :beer, :wine, :grapefruit
|
57
|
+
at_least_one_of :beer, :wine, :grapefruit, message: 'fail'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
post '/deeply-nested-array' do
|
62
|
+
end
|
63
|
+
end
|
12
64
|
end
|
13
65
|
end
|
14
|
-
|
15
|
-
|
66
|
+
|
67
|
+
def app
|
68
|
+
ValidationsSpec::AtLeastOneOfValidatorSpec::API
|
69
|
+
end
|
16
70
|
|
17
71
|
context 'when all restricted params are present' do
|
72
|
+
let(:path) { '/' }
|
18
73
|
let(:params) { { beer: true, wine: true, grapefruit: true } }
|
19
74
|
|
20
|
-
it 'does not
|
21
|
-
|
75
|
+
it 'does not return a validation error' do
|
76
|
+
validate
|
77
|
+
expect(last_response.status).to eq 201
|
22
78
|
end
|
23
79
|
|
24
80
|
context 'mixed with other params' do
|
25
|
-
let(:
|
81
|
+
let(:path) { '/mixed-params' }
|
82
|
+
let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
|
26
83
|
|
27
|
-
it 'does not
|
28
|
-
|
84
|
+
it 'does not return a validation error' do
|
85
|
+
validate
|
86
|
+
expect(last_response.status).to eq 201
|
29
87
|
end
|
30
88
|
end
|
31
89
|
end
|
32
90
|
|
33
91
|
context 'when a subset of restricted params are present' do
|
92
|
+
let(:path) { '/' }
|
34
93
|
let(:params) { { beer: true, grapefruit: true } }
|
35
94
|
|
36
|
-
it 'does not
|
37
|
-
|
95
|
+
it 'does not return a validation error' do
|
96
|
+
validate
|
97
|
+
expect(last_response.status).to eq 201
|
38
98
|
end
|
39
99
|
end
|
40
100
|
|
41
|
-
context 'when
|
42
|
-
let(:
|
101
|
+
context 'when none of the restricted params is selected' do
|
102
|
+
let(:path) { '/' }
|
103
|
+
let(:params) { { other: true } }
|
104
|
+
|
105
|
+
it 'returns a validation error' do
|
106
|
+
validate
|
107
|
+
expect(last_response.status).to eq 400
|
108
|
+
expect(JSON.parse(last_response.body)).to eq(
|
109
|
+
'beer,wine,grapefruit' => ['are missing, at least one parameter must be provided']
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when custom message is specified' do
|
114
|
+
let(:path) { '/custom-message' }
|
43
115
|
|
44
|
-
|
45
|
-
|
116
|
+
it 'returns a validation error' do
|
117
|
+
validate
|
118
|
+
expect(last_response.status).to eq 400
|
119
|
+
expect(JSON.parse(last_response.body)).to eq(
|
120
|
+
'beer,wine,grapefruit' => ['you should choose something']
|
121
|
+
)
|
122
|
+
end
|
46
123
|
end
|
47
124
|
end
|
48
125
|
|
49
|
-
context 'when
|
50
|
-
let(:
|
126
|
+
context 'when exactly one of the restricted params is selected' do
|
127
|
+
let(:path) { '/' }
|
128
|
+
let(:params) { { beer: true } }
|
51
129
|
|
52
|
-
it '
|
53
|
-
|
54
|
-
|
55
|
-
end.to raise_error(Grape::Exceptions::Validation)
|
130
|
+
it 'does not return a validation error' do
|
131
|
+
validate
|
132
|
+
expect(last_response.status).to eq 201
|
56
133
|
end
|
57
134
|
end
|
58
135
|
|
59
|
-
context 'when
|
60
|
-
let(:
|
136
|
+
context 'when restricted params are nested inside hash' do
|
137
|
+
let(:path) { '/nested-hash' }
|
138
|
+
|
139
|
+
context 'when at least one of them is present' do
|
140
|
+
let(:params) { { item: { beer: true, wine: true } } }
|
141
|
+
|
142
|
+
it 'does not return a validation error' do
|
143
|
+
validate
|
144
|
+
expect(last_response.status).to eq 201
|
145
|
+
end
|
146
|
+
end
|
61
147
|
|
62
|
-
|
63
|
-
|
148
|
+
context 'when none of them are present' do
|
149
|
+
let(:params) { { item: { other: true } } }
|
150
|
+
|
151
|
+
it 'returns a validation error with full names of the params' do
|
152
|
+
validate
|
153
|
+
expect(last_response.status).to eq 400
|
154
|
+
expect(JSON.parse(last_response.body)).to eq(
|
155
|
+
'item[beer],item[wine],item[grapefruit]' => ['fail']
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when restricted params are nested inside array' do
|
162
|
+
let(:path) { '/nested-array' }
|
163
|
+
|
164
|
+
context 'when at least one of them is present' do
|
165
|
+
let(:params) { { items: [{ beer: true, wine: true }, { grapefruit: true }] } }
|
166
|
+
|
167
|
+
it 'does not return a validation error' do
|
168
|
+
validate
|
169
|
+
expect(last_response.status).to eq 201
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when none of them are present' do
|
174
|
+
let(:params) { { items: [{ beer: true, other: true }, { other: true }] } }
|
175
|
+
|
176
|
+
it 'returns a validation error with full names of the params' do
|
177
|
+
validate
|
178
|
+
expect(last_response.status).to eq 400
|
179
|
+
expect(JSON.parse(last_response.body)).to eq(
|
180
|
+
'items[1][beer],items[1][wine],items[1][grapefruit]' => ['fail']
|
181
|
+
)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context 'when restricted params are deeply nested' do
|
187
|
+
let(:path) { '/deeply-nested-array' }
|
188
|
+
|
189
|
+
context 'when at least one of them is present' do
|
190
|
+
let(:params) { { items: [{ nested_items: [{ wine: true }] }] } }
|
191
|
+
|
192
|
+
it 'does not return a validation error' do
|
193
|
+
validate
|
194
|
+
expect(last_response.status).to eq 201
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'when none of them are present' do
|
199
|
+
let(:params) { { items: [{ nested_items: [{ other: true }] }] } }
|
200
|
+
|
201
|
+
it 'returns a validation error with full names of the params' do
|
202
|
+
validate
|
203
|
+
expect(last_response.status).to eq 400
|
204
|
+
expect(JSON.parse(last_response.body)).to eq(
|
205
|
+
'items[0][nested_items][0][beer],items[0][nested_items][0][wine],items[0][nested_items][0][grapefruit]' => ['fail']
|
206
|
+
)
|
207
|
+
end
|
64
208
|
end
|
65
209
|
end
|
66
210
|
end
|
@@ -20,10 +20,13 @@ describe Grape::Validations::CoerceValidator do
|
|
20
20
|
|
21
21
|
context 'i18n' do
|
22
22
|
after :each do
|
23
|
+
I18n.available_locales = %i[en]
|
23
24
|
I18n.locale = :en
|
25
|
+
I18n.default_locale = :en
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'i18n error on malformed input' do
|
29
|
+
I18n.available_locales = %i[en zh-CN]
|
27
30
|
I18n.load_path << File.expand_path('../zh-CN.yml', __FILE__)
|
28
31
|
I18n.reload!
|
29
32
|
I18n.locale = 'zh-CN'.to_sym
|
@@ -40,6 +43,7 @@ describe Grape::Validations::CoerceValidator do
|
|
40
43
|
end
|
41
44
|
|
42
45
|
it 'gives an english fallback error when default locale message is blank' do
|
46
|
+
I18n.available_locales = %i[en pt-BR]
|
43
47
|
I18n.locale = 'pt-BR'.to_sym
|
44
48
|
subject.params do
|
45
49
|
requires :age, type: Integer
|
@@ -397,6 +401,10 @@ describe Grape::Validations::CoerceValidator do
|
|
397
401
|
post '/upload', file: 'not a file'
|
398
402
|
expect(last_response.status).to eq(400)
|
399
403
|
expect(last_response.body).to eq('file is invalid')
|
404
|
+
|
405
|
+
post '/upload', file: { filename: 'fake file', tempfile: '/etc/passwd' }
|
406
|
+
expect(last_response.status).to eq(400)
|
407
|
+
expect(last_response.body).to eq('file is invalid')
|
400
408
|
end
|
401
409
|
|
402
410
|
it 'Nests integers' do
|
@@ -572,7 +580,7 @@ describe Grape::Validations::CoerceValidator do
|
|
572
580
|
expect(last_response.status).to eq(200)
|
573
581
|
expect(last_response.body).to eq('arrays work')
|
574
582
|
|
575
|
-
get '/', splines: [{ x: 2, ints: [] }, { x: 3, ints: [4], obj: { y: 'quack' } }]
|
583
|
+
get '/', splines: [{ x: 2, ints: [5] }, { x: 3, ints: [4], obj: { y: 'quack' } }]
|
576
584
|
expect(last_response.status).to eq(200)
|
577
585
|
expect(last_response.body).to eq('arrays work')
|
578
586
|
|
@@ -588,7 +596,7 @@ describe Grape::Validations::CoerceValidator do
|
|
588
596
|
expect(last_response.status).to eq(400)
|
589
597
|
expect(last_response.body).to eq('splines[x] does not have a valid value')
|
590
598
|
|
591
|
-
get '/', splines: [{ x: 1, ints: [] }, { x: 4, ints: [] }]
|
599
|
+
get '/', splines: [{ x: 1, ints: [5] }, { x: 4, ints: [6] }]
|
592
600
|
expect(last_response.status).to eq(400)
|
593
601
|
expect(last_response.body).to eq('splines[x] does not have a valid value')
|
594
602
|
end
|
@@ -2,73 +2,237 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Grape::Validations::ExactlyOneOfValidator do
|
4
4
|
describe '#validate!' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
subject(:validate) { post path, params }
|
6
|
+
|
7
|
+
module ValidationsSpec
|
8
|
+
module ExactlyOneOfValidatorSpec
|
9
|
+
class API < Grape::API
|
10
|
+
rescue_from Grape::Exceptions::ValidationErrors do |e|
|
11
|
+
error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
|
12
|
+
end
|
13
|
+
|
14
|
+
params do
|
15
|
+
optional :beer
|
16
|
+
optional :wine
|
17
|
+
optional :grapefruit
|
18
|
+
exactly_one_of :beer, :wine, :grapefruit
|
19
|
+
end
|
20
|
+
post do
|
21
|
+
end
|
22
|
+
|
23
|
+
params do
|
24
|
+
optional :beer
|
25
|
+
optional :wine
|
26
|
+
optional :grapefruit
|
27
|
+
optional :other
|
28
|
+
exactly_one_of :beer, :wine, :grapefruit
|
29
|
+
end
|
30
|
+
post 'mixed-params' do
|
31
|
+
end
|
32
|
+
|
33
|
+
params do
|
34
|
+
optional :beer
|
35
|
+
optional :wine
|
36
|
+
optional :grapefruit
|
37
|
+
exactly_one_of :beer, :wine, :grapefruit, message: 'you should choose one'
|
38
|
+
end
|
39
|
+
post '/custom-message' do
|
40
|
+
end
|
10
41
|
|
11
|
-
|
42
|
+
params do
|
43
|
+
requires :item, type: Hash do
|
44
|
+
optional :beer
|
45
|
+
optional :wine
|
46
|
+
optional :grapefruit
|
47
|
+
exactly_one_of :beer, :wine, :grapefruit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
post '/nested-hash' do
|
51
|
+
end
|
52
|
+
|
53
|
+
params do
|
54
|
+
optional :item, type: Hash do
|
55
|
+
optional :beer
|
56
|
+
optional :wine
|
57
|
+
optional :grapefruit
|
58
|
+
exactly_one_of :beer, :wine, :grapefruit
|
59
|
+
end
|
60
|
+
end
|
61
|
+
post '/nested-optional-hash' do
|
62
|
+
end
|
63
|
+
|
64
|
+
params do
|
65
|
+
requires :items, type: Array do
|
66
|
+
optional :beer
|
67
|
+
optional :wine
|
68
|
+
optional :grapefruit
|
69
|
+
exactly_one_of :beer, :wine, :grapefruit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
post '/nested-array' do
|
73
|
+
end
|
74
|
+
|
75
|
+
params do
|
76
|
+
requires :items, type: Array do
|
77
|
+
requires :nested_items, type: Array do
|
78
|
+
optional :beer, :wine, :grapefruit, type: Boolean
|
79
|
+
exactly_one_of :beer, :wine, :grapefruit
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
post '/deeply-nested-array' do
|
84
|
+
end
|
85
|
+
end
|
12
86
|
end
|
13
87
|
end
|
14
|
-
let(:exactly_one_of_params) { %i[beer wine grapefruit] }
|
15
|
-
let(:validator) { described_class.new(exactly_one_of_params, {}, false, scope.new) }
|
16
88
|
|
17
|
-
|
89
|
+
def app
|
90
|
+
ValidationsSpec::ExactlyOneOfValidatorSpec::API
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when all params are present' do
|
94
|
+
let(:path) { '/' }
|
18
95
|
let(:params) { { beer: true, wine: true, grapefruit: true } }
|
19
96
|
|
20
|
-
it '
|
21
|
-
|
22
|
-
|
23
|
-
|
97
|
+
it 'returns a validation error' do
|
98
|
+
validate
|
99
|
+
expect(last_response.status).to eq 400
|
100
|
+
expect(JSON.parse(last_response.body)).to eq(
|
101
|
+
'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
|
102
|
+
)
|
24
103
|
end
|
25
104
|
|
26
105
|
context 'mixed with other params' do
|
27
|
-
let(:
|
106
|
+
let(:path) { '/mixed-params' }
|
107
|
+
let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
|
28
108
|
|
29
|
-
it '
|
30
|
-
|
31
|
-
|
32
|
-
|
109
|
+
it 'returns a validation error' do
|
110
|
+
validate
|
111
|
+
expect(last_response.status).to eq 400
|
112
|
+
expect(JSON.parse(last_response.body)).to eq(
|
113
|
+
'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
|
114
|
+
)
|
33
115
|
end
|
34
116
|
end
|
35
117
|
end
|
36
118
|
|
37
|
-
context 'when a subset of
|
119
|
+
context 'when a subset of params are present' do
|
120
|
+
let(:path) { '/' }
|
38
121
|
let(:params) { { beer: true, grapefruit: true } }
|
39
122
|
|
40
|
-
it '
|
41
|
-
|
42
|
-
|
43
|
-
|
123
|
+
it 'returns a validation error' do
|
124
|
+
validate
|
125
|
+
expect(last_response.status).to eq 400
|
126
|
+
expect(JSON.parse(last_response.body)).to eq(
|
127
|
+
'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
|
128
|
+
)
|
44
129
|
end
|
45
130
|
end
|
46
131
|
|
47
|
-
context 'when
|
48
|
-
let(:
|
132
|
+
context 'when custom message is specified' do
|
133
|
+
let(:path) { '/custom-message' }
|
134
|
+
let(:params) { { beer: true, wine: true } }
|
49
135
|
|
50
|
-
it '
|
51
|
-
|
52
|
-
|
53
|
-
|
136
|
+
it 'returns a validation error' do
|
137
|
+
validate
|
138
|
+
expect(last_response.status).to eq 400
|
139
|
+
expect(JSON.parse(last_response.body)).to eq(
|
140
|
+
'beer,wine,grapefruit' => ['you should choose one']
|
141
|
+
)
|
54
142
|
end
|
55
143
|
end
|
56
144
|
|
57
|
-
context 'when
|
145
|
+
context 'when exacly one param is present' do
|
146
|
+
let(:path) { '/' }
|
147
|
+
let(:params) { { beer: true, somethingelse: true } }
|
148
|
+
|
149
|
+
it 'does not return a validation error' do
|
150
|
+
validate
|
151
|
+
expect(last_response.status).to eq 201
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when none of the params are present' do
|
156
|
+
let(:path) { '/' }
|
58
157
|
let(:params) { { somethingelse: true } }
|
59
158
|
|
60
|
-
it '
|
61
|
-
|
62
|
-
|
63
|
-
|
159
|
+
it 'returns a validation error' do
|
160
|
+
validate
|
161
|
+
expect(last_response.status).to eq 400
|
162
|
+
expect(JSON.parse(last_response.body)).to eq(
|
163
|
+
'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
|
164
|
+
)
|
64
165
|
end
|
65
166
|
end
|
66
167
|
|
67
|
-
context 'when
|
68
|
-
let(:
|
168
|
+
context 'when params are nested inside required hash' do
|
169
|
+
let(:path) { '/nested-hash' }
|
170
|
+
let(:params) { { item: { beer: true, wine: true } } }
|
171
|
+
|
172
|
+
it 'returns a validation error with full names of the params' do
|
173
|
+
validate
|
174
|
+
expect(last_response.status).to eq 400
|
175
|
+
expect(JSON.parse(last_response.body)).to eq(
|
176
|
+
'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
|
177
|
+
)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'when params are nested inside optional hash' do
|
182
|
+
let(:path) { '/nested-optional-hash' }
|
183
|
+
|
184
|
+
context 'when params are passed' do
|
185
|
+
let(:params) { { item: { beer: true, wine: true } } }
|
186
|
+
|
187
|
+
it 'returns a validation error with full names of the params' do
|
188
|
+
validate
|
189
|
+
expect(last_response.status).to eq 400
|
190
|
+
expect(JSON.parse(last_response.body)).to eq(
|
191
|
+
'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
|
192
|
+
)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'when params are empty' do
|
197
|
+
let(:params) { { other: true } }
|
198
|
+
|
199
|
+
it 'does not return a validation error' do
|
200
|
+
validate
|
201
|
+
expect(last_response.status).to eq 201
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when params are nested inside array' do
|
207
|
+
let(:path) { '/nested-array' }
|
208
|
+
let(:params) { { items: [{ beer: true, wine: true }, { wine: true, grapefruit: true }] } }
|
209
|
+
|
210
|
+
it 'returns a validation error with full names of the params' do
|
211
|
+
validate
|
212
|
+
expect(last_response.status).to eq 400
|
213
|
+
expect(JSON.parse(last_response.body)).to eq(
|
214
|
+
'items[0][beer],items[0][wine],items[0][grapefruit]' => [
|
215
|
+
'are missing, exactly one parameter must be provided'
|
216
|
+
],
|
217
|
+
'items[1][beer],items[1][wine],items[1][grapefruit]' => [
|
218
|
+
'are missing, exactly one parameter must be provided'
|
219
|
+
]
|
220
|
+
)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when params are deeply nested' do
|
225
|
+
let(:path) { '/deeply-nested-array' }
|
226
|
+
let(:params) { { items: [{ nested_items: [{ beer: true, wine: true }] }] } }
|
69
227
|
|
70
|
-
it '
|
71
|
-
|
228
|
+
it 'returns a validation error with full names of the params' do
|
229
|
+
validate
|
230
|
+
expect(last_response.status).to eq 400
|
231
|
+
expect(JSON.parse(last_response.body)).to eq(
|
232
|
+
'items[0][nested_items][0][beer],items[0][nested_items][0][wine],items[0][nested_items][0][grapefruit]' => [
|
233
|
+
'are missing, exactly one parameter must be provided'
|
234
|
+
]
|
235
|
+
)
|
72
236
|
end
|
73
237
|
end
|
74
238
|
end
|