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
data/spec/grape/request_spec.rb
CHANGED
@@ -62,6 +62,30 @@ module Grape
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
describe 'when the param_builder is set to Hashie' do
|
66
|
+
before do
|
67
|
+
Grape.configure do |config|
|
68
|
+
config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
after do
|
73
|
+
Grape.config.reset
|
74
|
+
end
|
75
|
+
|
76
|
+
subject(:request_params) { Grape::Request.new(env, opts).params }
|
77
|
+
|
78
|
+
context 'when the API does not include a specific param builder' do
|
79
|
+
let(:opts) { {} }
|
80
|
+
it { is_expected.to be_a(Hashie::Mash) }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when the API includes a specific param builder' do
|
84
|
+
let(:opts) { { build_params_with: Grape::Extensions::Hash::ParamBuilder } }
|
85
|
+
it { is_expected.to be_a(Hash) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
65
89
|
describe '#headers' do
|
66
90
|
let(:options) do
|
67
91
|
default_options.merge(request_headers)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Validations::MultipleAttributesIterator do
|
4
|
+
describe '#each' do
|
5
|
+
subject(:iterator) { described_class.new(validator, scope, params) }
|
6
|
+
let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
|
7
|
+
let(:validator) { double(attrs: %i[first second third]) }
|
8
|
+
|
9
|
+
context 'when params is a hash' do
|
10
|
+
let(:params) do
|
11
|
+
{ first: 'string', second: 'string' }
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'yields the whole params hash without the list of attrs' do
|
15
|
+
expect { |b| iterator.each(&b) }.to yield_with_args(params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when params is an array' do
|
20
|
+
let(:params) do
|
21
|
+
[{ first: 'string1', second: 'string1' }, { first: 'string2', second: 'string2' }]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'yields each element of the array without the list of attrs' do
|
25
|
+
expect { |b| iterator.each(&b) }.to yield_successive_args(params[0], params[1])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -121,14 +121,14 @@ describe Grape::Validations::ParamsScope do
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
context 'param
|
124
|
+
context 'param renaming' do
|
125
125
|
it do
|
126
126
|
subject.params do
|
127
127
|
requires :foo, as: :bar
|
128
128
|
optional :super, as: :hiper
|
129
129
|
end
|
130
|
-
subject.get('/
|
131
|
-
get '/
|
130
|
+
subject.get('/renaming') { "#{declared(params)['bar']}-#{declared(params)['hiper']}" }
|
131
|
+
get '/renaming', foo: 'any', super: 'any2'
|
132
132
|
|
133
133
|
expect(last_response.status).to eq(200)
|
134
134
|
expect(last_response.body).to eq('any-any2')
|
@@ -138,8 +138,8 @@ describe Grape::Validations::ParamsScope do
|
|
138
138
|
subject.params do
|
139
139
|
requires :foo, as: :bar, type: String, coerce_with: ->(c) { c.strip }
|
140
140
|
end
|
141
|
-
subject.get('/
|
142
|
-
get '/
|
141
|
+
subject.get('/renaming-coerced') { "#{params['bar']}-#{params['foo']}" }
|
142
|
+
get '/renaming-coerced', foo: ' there we go '
|
143
143
|
|
144
144
|
expect(last_response.status).to eq(200)
|
145
145
|
expect(last_response.body).to eq('there we go-')
|
@@ -149,12 +149,35 @@ describe Grape::Validations::ParamsScope do
|
|
149
149
|
subject.params do
|
150
150
|
requires :foo, as: :bar, allow_blank: false
|
151
151
|
end
|
152
|
-
subject.get('/
|
153
|
-
get '/
|
152
|
+
subject.get('/renaming-not-blank') {}
|
153
|
+
get '/renaming-not-blank', foo: ''
|
154
154
|
|
155
155
|
expect(last_response.status).to eq(400)
|
156
156
|
expect(last_response.body).to eq('foo is empty')
|
157
157
|
end
|
158
|
+
|
159
|
+
it do
|
160
|
+
subject.params do
|
161
|
+
requires :foo, as: :bar, allow_blank: false
|
162
|
+
end
|
163
|
+
subject.get('/renaming-not-blank-with-value') {}
|
164
|
+
get '/renaming-not-blank-with-value', foo: 'any'
|
165
|
+
|
166
|
+
expect(last_response.status).to eq(200)
|
167
|
+
end
|
168
|
+
|
169
|
+
it do
|
170
|
+
subject.params do
|
171
|
+
requires :foo, as: :baz, type: Hash do
|
172
|
+
requires :bar, as: :qux
|
173
|
+
end
|
174
|
+
end
|
175
|
+
subject.get('/nested-renaming') { declared(params).to_json }
|
176
|
+
get '/nested-renaming', foo: { bar: 'any' }
|
177
|
+
|
178
|
+
expect(last_response.status).to eq(200)
|
179
|
+
expect(last_response.body).to eq('{"baz":{"qux":"any"}}')
|
180
|
+
end
|
158
181
|
end
|
159
182
|
|
160
183
|
context 'array without coerce type explicitly given' do
|
@@ -479,7 +502,50 @@ describe Grape::Validations::ParamsScope do
|
|
479
502
|
end.to_not raise_error
|
480
503
|
end
|
481
504
|
|
482
|
-
it '
|
505
|
+
it 'does not raise an error if when using nested given' do
|
506
|
+
expect do
|
507
|
+
subject.params do
|
508
|
+
optional :a, type: Hash do
|
509
|
+
requires :b
|
510
|
+
end
|
511
|
+
given :a do
|
512
|
+
requires :c
|
513
|
+
given :c do
|
514
|
+
requires :d
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end.to_not raise_error
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'allows nested dependent parameters' do
|
522
|
+
subject.params do
|
523
|
+
optional :a
|
524
|
+
given a: ->(val) { val == 'a' } do
|
525
|
+
optional :b
|
526
|
+
given b: ->(val) { val == 'b' } do
|
527
|
+
optional :c
|
528
|
+
given c: ->(val) { val == 'c' } do
|
529
|
+
requires :d
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
subject.get('/') { declared(params).to_json }
|
535
|
+
|
536
|
+
get '/'
|
537
|
+
expect(last_response.status).to eq 200
|
538
|
+
|
539
|
+
get '/', a: 'a', b: 'b', c: 'c'
|
540
|
+
expect(last_response.status).to eq 400
|
541
|
+
expect(last_response.body).to eq 'd is missing'
|
542
|
+
|
543
|
+
get '/', a: 'a', b: 'b', c: 'c', d: 'd'
|
544
|
+
expect(last_response.status).to eq 200
|
545
|
+
expect(last_response.body).to eq({ a: 'a', b: 'b', c: 'c', d: 'd' }.to_json)
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'allows renaming of dependent parameters' do
|
483
549
|
subject.params do
|
484
550
|
optional :a
|
485
551
|
given :a do
|
@@ -497,6 +563,34 @@ describe Grape::Validations::ParamsScope do
|
|
497
563
|
expect(body.keys).to_not include('b')
|
498
564
|
end
|
499
565
|
|
566
|
+
it 'allows renaming of dependent on parameter' do
|
567
|
+
subject.params do
|
568
|
+
optional :a, as: :b
|
569
|
+
given b: ->(val) { val == 'x' } do
|
570
|
+
requires :c
|
571
|
+
end
|
572
|
+
end
|
573
|
+
subject.get('/') { declared(params) }
|
574
|
+
|
575
|
+
get '/', a: 'x'
|
576
|
+
expect(last_response.status).to eq 400
|
577
|
+
expect(last_response.body).to eq 'c is missing'
|
578
|
+
|
579
|
+
get '/', a: 'y'
|
580
|
+
expect(last_response.status).to eq 200
|
581
|
+
end
|
582
|
+
|
583
|
+
it 'raises an error if the dependent parameter is not the renamed one' do
|
584
|
+
expect do
|
585
|
+
subject.params do
|
586
|
+
optional :a, as: :b
|
587
|
+
given :a do
|
588
|
+
requires :c
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end.to raise_error(Grape::Exceptions::UnknownParameter)
|
592
|
+
end
|
593
|
+
|
500
594
|
it 'does not validate nested requires when given is false' do
|
501
595
|
subject.params do
|
502
596
|
requires :a, type: String, allow_blank: false, values: %w[x y z]
|
@@ -592,7 +686,53 @@ describe Grape::Validations::ParamsScope do
|
|
592
686
|
end
|
593
687
|
end
|
594
688
|
|
689
|
+
context 'default value in given block' do
|
690
|
+
before do
|
691
|
+
subject.params do
|
692
|
+
optional :a, values: %w[a b]
|
693
|
+
given a: ->(val) { val == 'a' } do
|
694
|
+
optional :b, default: 'default'
|
695
|
+
end
|
696
|
+
end
|
697
|
+
subject.get('/') { params.to_json }
|
698
|
+
end
|
699
|
+
|
700
|
+
context 'when dependency meets' do
|
701
|
+
it 'sets default value for dependent parameter' do
|
702
|
+
get '/', a: 'a'
|
703
|
+
expect(last_response.body).to eq({ a: 'a', b: 'default' }.to_json)
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
context 'when dependency does not meet' do
|
708
|
+
it 'does not set default value for dependent parameter' do
|
709
|
+
get '/', a: 'b'
|
710
|
+
expect(last_response.body).to eq({ a: 'b' }.to_json)
|
711
|
+
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
595
715
|
context 'when validations are dependent on a parameter within an array param' do
|
716
|
+
before do
|
717
|
+
subject.params do
|
718
|
+
requires :foos, type: Array do
|
719
|
+
optional :foo
|
720
|
+
given :foo do
|
721
|
+
requires :bar
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
subject.get('/test') { 'ok' }
|
726
|
+
end
|
727
|
+
|
728
|
+
it 'should pass none Hash params' do
|
729
|
+
get '/test', foos: ['']
|
730
|
+
expect(last_response.status).to eq(200)
|
731
|
+
expect(last_response.body).to eq('ok')
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
context 'when validations are dependent on a parameter within an array param within #declared(params).to_json' do
|
596
736
|
before do
|
597
737
|
subject.params do
|
598
738
|
requires :foos, type: Array do
|
@@ -948,4 +1088,40 @@ describe Grape::Validations::ParamsScope do
|
|
948
1088
|
end
|
949
1089
|
end
|
950
1090
|
end
|
1091
|
+
|
1092
|
+
context 'with exactly_one_of validation for optional parameters within an Hash param' do
|
1093
|
+
before do
|
1094
|
+
subject.params do
|
1095
|
+
optional :memo, type: Hash do
|
1096
|
+
optional :text, type: String
|
1097
|
+
optional :custom_body, type: Hash, coerce_with: JSON
|
1098
|
+
exactly_one_of :text, :custom_body
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
subject.get('test')
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
context 'when correct data is provided' do
|
1105
|
+
it 'returns a successful response' do
|
1106
|
+
get 'test', memo: {}
|
1107
|
+
expect(last_response.status).to eq(200)
|
1108
|
+
|
1109
|
+
get 'test', memo: { text: 'HOGEHOGE' }
|
1110
|
+
expect(last_response.status).to eq(200)
|
1111
|
+
|
1112
|
+
get 'test', memo: { custom_body: '{ "xxx": "yyy" }' }
|
1113
|
+
expect(last_response.status).to eq(200)
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
context 'when invalid data is provided' do
|
1118
|
+
it 'returns a failure response' do
|
1119
|
+
get 'test', memo: { text: 'HOGEHOGE', custom_body: '{ "xxx": "yyy" }' }
|
1120
|
+
expect(last_response.status).to eq(400)
|
1121
|
+
|
1122
|
+
get 'test', memo: '{ "custom_body": "HOGE" }'
|
1123
|
+
expect(last_response.status).to eq(400)
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
end
|
951
1127
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Validations::SingleAttributeIterator do
|
4
|
+
describe '#each' do
|
5
|
+
subject(:iterator) { described_class.new(validator, scope, params) }
|
6
|
+
let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
|
7
|
+
let(:validator) { double(attrs: %i[first second third]) }
|
8
|
+
|
9
|
+
context 'when params is a hash' do
|
10
|
+
let(:params) do
|
11
|
+
{ first: 'string', second: 'string' }
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'yields params and every single attribute from the list' do
|
15
|
+
expect { |b| iterator.each(&b) }
|
16
|
+
.to yield_successive_args([params, :first], [params, :second], [params, :third])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when params is an array' do
|
21
|
+
let(:params) do
|
22
|
+
[{ first: 'string1', second: 'string1' }, { first: 'string2', second: 'string2' }]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'yields every single attribute from the list for each of the array elements' do
|
26
|
+
expect { |b| iterator.each(&b) }.to yield_successive_args(
|
27
|
+
[params[0], :first], [params[0], :second], [params[0], :third],
|
28
|
+
[params[1], :first], [params[1], :second], [params[1], :third]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -2,58 +2,166 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Grape::Validations::AllOrNoneOfValidator do
|
4
4
|
describe '#validate!' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
subject(:validate) { post path, params }
|
6
|
+
|
7
|
+
module ValidationsSpec
|
8
|
+
module AllOrNoneOfValidatorSpec
|
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, type: Boolean
|
16
|
+
all_or_none_of :beer, :wine
|
17
|
+
end
|
18
|
+
post do
|
19
|
+
end
|
20
|
+
|
21
|
+
params do
|
22
|
+
optional :beer, :wine, :other, type: Boolean
|
23
|
+
all_or_none_of :beer, :wine
|
24
|
+
end
|
25
|
+
post 'mixed-params' do
|
26
|
+
end
|
27
|
+
|
28
|
+
params do
|
29
|
+
optional :beer, :wine, type: Boolean
|
30
|
+
all_or_none_of :beer, :wine, message: 'choose all or none'
|
31
|
+
end
|
32
|
+
post '/custom-message' do
|
33
|
+
end
|
34
|
+
|
35
|
+
params do
|
36
|
+
requires :item, type: Hash do
|
37
|
+
optional :beer, :wine, type: Boolean
|
38
|
+
all_or_none_of :beer, :wine
|
39
|
+
end
|
40
|
+
end
|
41
|
+
post '/nested-hash' do
|
42
|
+
end
|
43
|
+
|
44
|
+
params do
|
45
|
+
requires :items, type: Array do
|
46
|
+
optional :beer, :wine, type: Boolean
|
47
|
+
all_or_none_of :beer, :wine
|
48
|
+
end
|
49
|
+
end
|
50
|
+
post '/nested-array' do
|
51
|
+
end
|
10
52
|
|
11
|
-
|
53
|
+
params do
|
54
|
+
requires :items, type: Array do
|
55
|
+
requires :nested_items, type: Array do
|
56
|
+
optional :beer, :wine, type: Boolean
|
57
|
+
all_or_none_of :beer, :wine
|
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::AllOrNoneOfValidatorSpec::API
|
69
|
+
end
|
16
70
|
|
17
71
|
context 'when all restricted params are present' do
|
18
|
-
let(:
|
72
|
+
let(:path) { '/' }
|
73
|
+
let(:params) { { beer: true, wine: 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, 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
|
-
context 'when
|
91
|
+
context 'when a subset of restricted params are present' do
|
92
|
+
let(:path) { '/' }
|
93
|
+
let(:params) { { beer: true } }
|
94
|
+
|
95
|
+
it 'returns a validation error' do
|
96
|
+
validate
|
97
|
+
expect(last_response.status).to eq 400
|
98
|
+
expect(JSON.parse(last_response.body)).to eq(
|
99
|
+
'beer,wine' => ['provide all or none of parameters']
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when custom message is specified' do
|
105
|
+
let(:path) { '/custom-message' }
|
106
|
+
let(:params) { { beer: true } }
|
107
|
+
|
108
|
+
it 'returns a validation error' do
|
109
|
+
validate
|
110
|
+
expect(last_response.status).to eq 400
|
111
|
+
expect(JSON.parse(last_response.body)).to eq(
|
112
|
+
'beer,wine' => ['choose all or none']
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when no restricted params are present' do
|
118
|
+
let(:path) { '/' }
|
34
119
|
let(:params) { { somethingelse: true } }
|
35
120
|
|
36
|
-
it 'does not
|
37
|
-
|
121
|
+
it 'does not return a validation error' do
|
122
|
+
validate
|
123
|
+
expect(last_response.status).to eq 201
|
38
124
|
end
|
39
125
|
end
|
40
126
|
|
41
|
-
context 'when
|
42
|
-
let(:
|
127
|
+
context 'when restricted params are nested inside required hash' do
|
128
|
+
let(:path) { '/nested-hash' }
|
129
|
+
let(:params) { { item: { beer: true } } }
|
43
130
|
|
44
|
-
it '
|
45
|
-
|
46
|
-
|
47
|
-
|
131
|
+
it 'returns a validation error with full names of the params' do
|
132
|
+
validate
|
133
|
+
expect(last_response.status).to eq 400
|
134
|
+
expect(JSON.parse(last_response.body)).to eq(
|
135
|
+
'item[beer],item[wine]' => ['provide all or none of parameters']
|
136
|
+
)
|
48
137
|
end
|
49
|
-
|
50
|
-
let(:mixed_params) { params.merge!(other: true, andanother: true) }
|
138
|
+
end
|
51
139
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
140
|
+
context 'when mutually exclusive params are nested inside array' do
|
141
|
+
let(:path) { '/nested-array' }
|
142
|
+
let(:params) { { items: [{ beer: true, wine: true }, { wine: true }] } }
|
143
|
+
|
144
|
+
it 'returns a validation error with full names of the params' do
|
145
|
+
validate
|
146
|
+
expect(last_response.status).to eq 400
|
147
|
+
expect(JSON.parse(last_response.body)).to eq(
|
148
|
+
'items[1][beer],items[1][wine]' => ['provide all or none of parameters']
|
149
|
+
)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'when mutually exclusive params are deeply nested' do
|
154
|
+
let(:path) { '/deeply-nested-array' }
|
155
|
+
let(:params) { { items: [{ nested_items: [{ beer: true }] }] } }
|
156
|
+
|
157
|
+
it 'returns a validation error with full names of the params' do
|
158
|
+
validate
|
159
|
+
expect(last_response.status).to eq 400
|
160
|
+
expect(JSON.parse(last_response.body)).to eq(
|
161
|
+
'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
|
162
|
+
'provide all or none of parameters'
|
163
|
+
]
|
164
|
+
)
|
57
165
|
end
|
58
166
|
end
|
59
167
|
end
|