Skip to content
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

Fix NoMethodError with none Hash params #1868

Merged
merged 2 commits into from
Mar 13, 2019

Conversation

ksss
Copy link
Contributor

@ksss ksss commented Mar 11, 2019

params do
  requires :foos, type: Array do
    optional :bar
    given :bar do
      requires :baz
    end
  end
end
get '/test' { 'ok' }
client.get('/test', foos: ['abc'])
#=> NoMethodError (undefined method `with_indifferent_access' for "abc":String)

ksss added a commit to ksss/grape that referenced this pull request Mar 11, 2019
@ksss ksss force-pushed the fix-with_indifferent_access branch from 9f3fea8 to 72da4e6 Compare March 11, 2019 04:52
@dblock
Copy link
Member

dblock commented Mar 11, 2019

I don't think the fix is correct. In the example case the type of items in foos is expected to be a Hash, implied by an optional :bar. Passing a String should yield an error. I agree the current error is not great and I'd want Grape to do better.


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

@dm1try dm1try merged commit f824521 into ruby-grape:master Mar 13, 2019
@ksss ksss deleted the fix-with_indifferent_access branch March 14, 2019 01:26
basjanssen pushed a commit to basjanssen/grape that referenced this pull request Feb 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants