Array[Array[X]]] and Array[Hash[X]]] return wrong indices in errors
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 10k
- Forks
- 1.2k
- Avg merge
- 14h 38m
- Merged PRs (30d)
- 92
Description
Basically the indices of nested arguments are not kept correctly during validation process. It happens both for Hashes and Arrays declared inside another Array.
In the case of Array[Array[X]] the inner index keeps increasing (in the error messages) suggesting there might be something wrong in the recursion logic of validators -- i.e., it acts as a global count.
Created the following spec to explain the issue (disclaimer: first spec file, not sure if I got it right).
# frozen_string_literal: true
require 'spec_helper'
describe Grape::Validations::DefaultValidator do
describe '#validate!' do
subject(:validate) { post path, params }
module ValidationsSpec
module DefaultValidatorSpec
class API < Grape::API
rescue_from Grape::Exceptions::ValidationErrors do |e|
error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
end
params do
requires :points, type: Array do
# Following optional can be replaced with requires
optional :location, type: Hash, allow_blank: false do
requires :lat, type: Float, allow_blank: false
requires :lon, type: Float, allow_blank: false
end
end
end
post '/nested-array-hash' do
end
params do
requires :points, type: Array do
# Following optional can be replaced with requires
optional :location, type: Array, allow_blank: false do
requires :id, type: Fixnum, allow_blank: false
end
end
end
post '/nested-array-array' do
end
end
end
end
def app
ValidationsSpec::DefaultValidatorSpec::API
end
context 'when params are nested inside hash which is inside an array' do
let(:path) { '/nested-array-hash' }
let(:params) {
{
points: [
{ location: nil },
{ location: { lat: nil, lon: 0.0 } },
{ location: { lat: "string", lon: 0.0 } },
{ location: { lat: 0.0, lon: "string" } },
{ location: { lat: 0.0, lon: nil } },
{ location: nil },
]
}
}
it 'does return a validation error with correct array indices' do
validate
expect(JSON.parse(last_response.body)).to eq(
'points[0][location]' => ['is empty'],
'points[1][location][lat]' => ['is empty'],
'points[2][location][lat]' => ['is invalid'],
'points[3][location][lon]' => ['is invalid'],
'points[4][location][lon]' => ['is empty'],
'points[5][location]' => ['is empty'],
)
end
end
context 'when params are nested inside an array which is inside an array' do
let(:path) { '/nested-array-array' }
let(:params) {
{
points: [
{ location: [{ id: 0 }, { id: 1 }] }, # valid entry: to shift the indicies
{ location: [{ id: nil }, { id: 0 }] },
{ location: [{ id: 0.0 }, { id: 0 }] },
{ location: [{ id: 0 }, { id: nil }] },
{ location: [{ id: 0 }, { id: 0.0 }] },
{ location: 0 }, # This line creates multiple errors
{ location: nil },
{ location: "" }
]
}
}
it 'does return a validation error with correct array indices' do
validate
expect(JSON.parse(last_response.body)).to eq(
"points[1][location][0][id]" => ["is empty"],
"points[2][location][0][id]" => ["is invalid"],
"points[3][location][1][id]" => ["is empty"],
"points[4][location][1][id]" => ["is invalid"],
"points[5][location]" => ["is invalid"],
"points[6][location]" => ["is empty"],
"points[7][location]" => ["is empty"]
)
end
end
end
end
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Grape::Validations::DefaultValidator#validate! and reproduce the issue with the supplied nested-array hash and nested-array array specs. Trace how validation errors build their keys during recursion, then verify that the expected points[index] paths are preserved for every failing parameter and that the regression specs pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100