Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#### Fixes

* Your contribution here.
* [#2129](https://github.com/ruby-grape/grape/pull/2129): Fix validation error when Required Array nested inside an optional array, for Multiparam validators - [@dwhenry](https://github.com/dwhenry).
* [#2128](https://github.com/ruby-grape/grape/pull/2128): Fix validation error when Required Array nested inside an optional array - [@dwhenry](https://github.com/dwhenry).
* [#2127](https://github.com/ruby-grape/grape/pull/2127): Fix a performance issue with dependent params - [@dnesteryuk](https://github.com/dnesteryuk).
* [#2126](https://github.com/ruby-grape/grape/pull/2126): Fix warnings about redefined attribute accessors in `AttributeTranslator` - [@samsonjs](https://github.com/samsonjs).
Expand Down
8 changes: 8 additions & 0 deletions lib/grape/validations/attributes_iterator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ def do_each(params_to_process, parent_indicies = [], &block)
def yield_attributes(_resource_params, _attrs)
raise NotImplementedError
end

# This is a special case so that we can ignore tree's where option
# values are missing lower down. Unfortunately we can remove this
# are the parameter parsing stage as they are required to ensure
# the correct indexing is maintained
def skip?(val)
val == Grape::DSL::Parameters::EmptyOptionalValue
end
end
end
end
2 changes: 1 addition & 1 deletion lib/grape/validations/multiple_attributes_iterator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class MultipleAttributesIterator < AttributesIterator
private

def yield_attributes(resource_params, _attrs)
yield resource_params
yield resource_params, skip?(resource_params)
end
end
end
Expand Down
9 changes: 0 additions & 9 deletions lib/grape/validations/single_attribute_iterator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@ def yield_attributes(val, attrs)
end
end


# This is a special case so that we can ignore tree's where option
# values are missing lower down. Unfortunately we can remove this
# are the parameter parsing stage as they are required to ensure
# the correct indexing is maintained
def skip?(val)
val == Grape::DSL::Parameters::EmptyOptionalValue
end

# Primitives like Integers and Booleans don't respond to +empty?+.
# It could be possible to use +blank?+ instead, but
#
Expand Down
3 changes: 2 additions & 1 deletion lib/grape/validations/validators/multiple_params_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ def validate!(params)
attributes = MultipleAttributesIterator.new(self, @scope, params)
array_errors = []

attributes.each do |resource_params|
attributes.each do |resource_params, skip_value|
next if skip_value
begin
validate_params!(resource_params)
rescue Grape::Exceptions::Validation => e
Expand Down
16 changes: 13 additions & 3 deletions spec/grape/validations/multiple_attributes_iterator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
{ first: 'string', second: 'string' }
end

it 'yields the whole params hash without the list of attrs' do
expect { |b| iterator.each(&b) }.to yield_with_args(params)
it 'yields the whole params hash and the skipped flag without the list of attrs' do
expect { |b| iterator.each(&b) }.to yield_with_args(params, false)
end
end

Expand All @@ -24,7 +24,17 @@
end

it 'yields each element of the array without the list of attrs' do
expect { |b| iterator.each(&b) }.to yield_successive_args(params[0], params[1])
expect { |b| iterator.each(&b) }.to yield_successive_args([params[0], false], [params[1], false])
end
end

context 'when params is empty optional placeholder' do
let(:params) do
[Grape::DSL::Parameters::EmptyOptionalValue, { first: 'string2', second: 'string2' }]
end

it 'yields each element of the array without the list of attrs' do
expect { |b| iterator.each(&b) }.to yield_successive_args([Grape::DSL::Parameters::EmptyOptionalValue, true], [params[1], false])
end
end
end
Expand Down
88 changes: 86 additions & 2 deletions spec/grape/validations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ def validate_param!(attr_name, params)
end

it "with valid data" do
data_without_errors = {
data = {
top: [
{ top_id: 1, middle_1: [
{middle_1_id: 11}, {middle_1_id: 12, middle_2: [
Expand All @@ -1037,7 +1037,7 @@ def validate_param!(attr_name, params)
]
}

get '/multi_level', data_without_errors
get '/multi_level', data
expect(last_response.body).to eq("multi_level works!")
expect(last_response.status).to eq(200)
end
Expand Down Expand Up @@ -1067,6 +1067,90 @@ def validate_param!(attr_name, params)
end
end
end

it "exactly_one_of" do
subject.params do
requires :orders, type: Array do
requires :id, type: Integer
optional :drugs, type: Hash do
optional :batch_no, type: String
optional :batch_id, type: String
exactly_one_of :batch_no, :batch_id
end
end
end

subject.get '/exactly_one_of' do
'exactly_one_of works!'
end

data = {
orders: [
{ id: 77, drugs: {batch_no: "A1234567"}},
{ id: 70 }
]
}

get '/exactly_one_of', data
expect(last_response.body).to eq("exactly_one_of works!")
expect(last_response.status).to eq(200)
end

it "at_least_one_of" do
subject.params do
requires :orders, type: Array do
requires :id, type: Integer
optional :drugs, type: Hash do
optional :batch_no, type: String
optional :batch_id, type: String
at_least_one_of :batch_no, :batch_id
end
end
end

subject.get '/at_least_one_of' do
'at_least_one_of works!'
end

data = {
orders: [
{ id: 77, drugs: {batch_no: "A1234567"}},
{ id: 70 }
]
}

get '/at_least_one_of', data
expect(last_response.body).to eq("at_least_one_of works!")
expect(last_response.status).to eq(200)
end

it "all_or_none_of" do
subject.params do
requires :orders, type: Array do
requires :id, type: Integer
optional :drugs, type: Hash do
optional :batch_no, type: String
optional :batch_id, type: String
all_or_none_of :batch_no, :batch_id
end
end
end

subject.get '/all_or_none_of' do
'all_or_none_of works!'
end

data = {
orders: [
{ id: 77, drugs: {batch_no: "A1234567", batch_id: "12"}},
{ id: 70 }
]
}

get '/all_or_none_of', data
expect(last_response.body).to eq("all_or_none_of works!")
expect(last_response.status).to eq(200)
end
end

context 'multiple validation errors' do
Expand Down