grape icon indicating copy to clipboard operation
grape copied to clipboard

Array of hashes param not being parsed correctly in a spec

Open elado opened this issue 10 years ago • 7 comments

It might be a Rack issue, but:

before do
  subject.params do
    requires :contacts, type: Array do
      optional :name
      optional :phone
      optional :email
    end
  end

  subject.before_validation do
    pp form_vars: env['rack.request.form_vars'], form_hash: env['rack.request.form_hash']
    pp before: params.to_h
  end

  subject.post '/post' do
  end
end

it 'posts an array of hashes' do
  post '/post', contacts: [
    { name: 'John', phone: '123' },
    { email: '[email protected]' },
    { name: 'Doe', email: '[email protected]' },
  ]
end

Output is:

{:form_vars=>
  "contacts[][name]=John&contacts[][phone]=123&contacts[][email]=a%40mail.com&contacts[][name]=Doe&contacts[][email]=b%40mail.com",
 :form_hash=>
  {"contacts"=>
    [{"name"=>"John", "phone"=>"123", "email"=>"[email protected]"},
     {"name"=>"Doe", "email"=>"[email protected]"}]}}
{:before=>
  {"contacts"=>
    [{"name"=>"John", "phone"=>"123", "email"=>"[email protected]"},
     {"name"=>"Doe", "email"=>"[email protected]"}]}}

Rack(?) couldn't infer where each element starts so it combined both first items.

For a workaround I can send a Hash {0=>{name: ..., email:}, 1=>{email: ...}} and before validation do:

params[:contacts] = params[:contacts].values if params[:contacts].is_a?(Hash)

Any other suggestions?

elado avatar Jun 03 '15 23:06 elado

This looks legit, but I am not sure how to fix it. Arrays of hashes is not exactly something you can express in a query string.

For something like this I would POST JSON.

dblock avatar Jun 04 '15 12:06 dblock

The client sends JSON body so I'm covered there.

Maybe, if a param is defined as a type: Array and it looks like a hash with numeric keys it should convert automatically to an array of the values? I understand why Rack does what it does, the only solution I see is not using key-less params (as in contacts[] but contacts[0]).

elado avatar Jun 04 '15 17:06 elado

I would just let this go, but it's me :)

dblock avatar Jun 04 '15 18:06 dblock

@elado I would use a midlleware to convert hashes with numeric keys to array. For example the similar is already implemented here (spec)

dm1try avatar Jun 09 '15 17:06 dm1try

@dm1try thanks!

elado avatar Jun 09 '15 17:06 elado

Having the same problem now..

My HTTP post request has to pass in form data that looks like this:

listings_attributes[0][count] = 1
listings_attributes[1][name] = "Ipsum"
listings_attributes[1][count] = 2
listings_attributes[2][name] = "Dolor"
listings_attributes[2][count] = 3

This is represented as a ruby hash with numeric keys. Is there a way to declare the params so that the presence and type validations apply in my endpoint?

Tried the following param declaration

requires :listings_attributes, type: Array do
  optional :name, type: String
  optional :count, type: Integer
end

but this results in

(Grape::Exceptions::ValidationErrors) - listings_attributes is invalid

martinverdejo avatar Feb 19 '19 06:02 martinverdejo

@martinverdejo post a repro, and see my comment

dblock avatar Feb 24 '19 20:02 dblock