Skip to content

Commit 8395a41

Browse files
ericproulxdblock
authored andcommitted
Fix #1636: optional nested array validation (#1724)
1 parent 63a03e5 commit 8395a41

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
* [#1710](https://github.com/ruby-grape/grape/pull/1710): Fix wrong transformation of empty Array in declared params - [@pablonahuelgomez](https://github.com/pablonahuelgomez).
1313
* [#1722](https://github.com/ruby-grape/grape/pull/1722): Fix catch-all hiding multiple versions of an endpoint after the first definition - [@zherr](https://github.com/zherr).
14+
* [#1724](https://github.com/ruby-grape/grape/pull/1724): Optional nested array validation - [@ericproulx](https://github.com/ericproulx).
1415
* Your contribution here.
1516

1617
### 1.0.1 (9/8/2017)

lib/grape/validations/params_scope.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def initialize(opts, &block)
3939
# @return [Boolean] whether or not this entire scope needs to be
4040
# validated
4141
def should_validate?(parameters)
42-
return false if @optional && (params(parameters).blank? ||
43-
any_element_blank?(parameters))
42+
return false if @optional && (params(parameters).blank? || all_element_blank?(parameters))
4443

4544
return true if parent.nil?
4645
parent.should_validate?(parameters)
@@ -441,8 +440,8 @@ def options_key?(type, key, validations)
441440
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
442441
end
443442

444-
def any_element_blank?(parameters)
445-
params(parameters).respond_to?(:any?) && params(parameters).any?(&:blank?)
443+
def all_element_blank?(parameters)
444+
params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
446445
end
447446
end
448447
end

lib/grape/validations/validators/base.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def validate!(params)
3838
attributes = AttributesIterator.new(self, @scope, params)
3939
array_errors = []
4040
attributes.each do |resource_params, attr_name|
41+
next if !@scope.required? && resource_params.empty?
4142
next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
4243
next unless @scope.meets_dependency?(resource_params, params)
4344

spec/grape/validations/validators/default_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ class API < Grape::API
7575
get '/nested_optional_array' do
7676
{ root: params[:root] }
7777
end
78+
79+
params do
80+
requires :root, type: Hash do
81+
optional :some_things, type: Array do
82+
requires :foo
83+
optional :options, type: Array do
84+
optional :name, type: String
85+
optional :value, type: String
86+
end
87+
end
88+
end
89+
end
90+
get '/another_nested_optional_array' do
91+
{ root: params[:root] }
92+
end
7893
end
7994
end
8095
end
@@ -99,6 +114,31 @@ def app
99114
expect(last_response.body).to eq({ root: params }.to_json)
100115
end
101116

117+
it 'does not allows faulty optional arrays' do
118+
params = { some_things:
119+
[
120+
{ foo: 'one', options: [{ name: 'wat', value: 'nope' }] },
121+
{ foo: 'two', options: [{ name: 'wat' }] },
122+
{ foo: 'three' }
123+
] }
124+
error = { error: 'root[some_things][1][options][0][value] is missing' }
125+
get '/nested_optional_array', root: params
126+
expect(last_response.status).to eq(400)
127+
expect(last_response.body).to eq(error.to_json)
128+
end
129+
130+
it 'allows optional arrays with optional params' do
131+
params = { some_things:
132+
[
133+
{ foo: 'one', options: [{ value: 'nope' }] },
134+
{ foo: 'two', options: [{ name: 'wat' }] },
135+
{ foo: 'three' }
136+
] }
137+
get '/another_nested_optional_array', root: params
138+
expect(last_response.status).to eq(200)
139+
expect(last_response.body).to eq({ root: params }.to_json)
140+
end
141+
102142
it 'set default value for optional param' do
103143
get('/')
104144
expect(last_response.status).to eq(200)

0 commit comments

Comments
 (0)