Skip to content

Fix NoMethodError with none Hash params #1868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 13, 2019
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#### Fixes

* Your contribution here.
* [#1868](https://github.com/ruby-grape/grape/pull/1868): Fix NoMethodError with none hash params - [@ksss](https://github.com/ksss).

### 1.2.3 (2019/01/16)

Expand Down
1 change: 1 addition & 0 deletions lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def meets_dependency?(params, request_params)

return true unless @dependent_on
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
return false unless params.respond_to?(:with_indifferent_access)
params = params.with_indifferent_access

@dependent_on.each do |dependency|
Expand Down
20 changes: 20 additions & 0 deletions spec/grape/validations/params_scope_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,26 @@ def initialize(value)
end

context 'when validations are dependent on a parameter within an array param' do
before do
subject.params do
requires :foos, type: Array do
optional :foo
given :foo do
requires :bar
end
end
end
subject.get('/test') { 'ok' }
end

it 'should pass none Hash params' do
get '/test', foos: ['']
expect(last_response.status).to eq(200)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be a 400. The first item in foos does not match the expected input of my API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed this behavior.

params do
  optional :foo
  given :foo do
    requires :bar
  end
end
get('/test') { 'ok' }
client.get '/test', 'hello'
last_response.status #=> 200
last_response.body #=> "ok"

Is this also not intentional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. So we have this spec today? I feel that's wrong. Maybe @namusyaka or @dm1try have an opinion?

Copy link
Member

@dm1try dm1try Mar 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dblock I do not see any contradiction in the examples above, in the latter case foo is not provided, so it's not validated. in the original case foos is provided, so it's validated.

according to the right behavior, if we assume that the implicit requirement for type: Array with block is "array of objects"(Array[Object]) then you are right, we should return a validation error(the string is not an "object", provided type is wrong).
in this PR, a type requirement is "array of something"(Array[Any]) and this feels wrong, as for me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I think I agree. So what @dm1try is saying is that the spec above that @ksss is suggesting is correct by design, but that the spec in this PR is not because see above. So I'll take back my "i feel that's wrong".

@ksss does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dm1try Thank you for reviewing.
@dblock That makes sense.
I expect that scope in this PR is no errors occur on validation step.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dm1try Just to confirm, you're saying this PR is good to go as is? Merge if yes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dblock I think it's fine, at least it's better than NoMethodError.

though, in the future this spec should pass:

  context 'group values' do
    before do
      subject.params do
        requires :value, type: Hash do
        end
      end
      subject.get('/hash') { }

      subject.params do
        requires :value, type: Array do
        end
      end
      subject.get('/array_of_hashes') { }
    end

    it 'should be consistent' do
      get '/hash', value: ""
      expect(last_response.status).to eq(400)
      expect(last_response.body).to eq("value is invalid")

      get '/array_of_hashes', value: [""]
      expect(last_response.status).to eq(400)
      expect(last_response.body).to eq("value is invalid")
    end
  end

I've tested the idea to set implicit Array[Hash] type here

        if(block && (validations[:type].nil? || validations[:type] == Array))
          validations[:type] = Array[Hash]
        end

and while it works the case above, it fails for the cases where we rely on Virtus coerce logic for the hash

irb -Ilib -rgrape
irb(main):001:0> Virtus::Attribute.build(Array[Hash]).coerce({just: "hash"})
=> [{:just=>nil, "hash"=>nil}]
irb(main):002:0> Virtus::Attribute.build(Array).coerce({just: "hash"})
=> [[:just, "hash"]]

so this needs more investigation

expect(last_response.body).to eq('ok')
end
end

context 'when validations are dependent on a parameter within an array param within #declared(params).to_json' do
before do
subject.params do
requires :foos, type: Array do
Expand Down