-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 coerce_nil when Array of Type #2040
Conversation
433374b
to
44e0498
Compare
Should it equals NilClass ? |
@@ -76,7 +76,7 @@ def coerce_value(val) | |||
def coerce_nil(val) | |||
# define default values for structures, the dry-types lib which is used | |||
# for coercion doesn't accept nil as a value, so it would fail | |||
return [] if type == Array | |||
return [] if type == Array || type.is_a?(Array) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you love ruby?
2.6.6 :001 > Array.is_a?(Array)
=> false
This obviously makes sense because a single type is not an array of types, but still funny.
params[:arry] | ||
end | ||
|
||
get '/array', arry: nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a default value anywhere here. You're passing a nil
, and the API expects an array of integers. Feels like it should fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add
default: []
and it still fails on master. I found that since arry
is an actual params, the default validator doesn't do anything. Actually it returns immediatly here
Anyway, the default is kind of tricky for structures.
Array
, Set
and Hash
will return a default value even if not explicitly written in the param declaration
So these specs are working
[[Array, 'Array'], [Set, 'Set'], [Hash, 'ActiveSupport::HashWithIndifferentAccess']].each do |type, expected_class|
context 'structures default value' do
it 'Empty array' do
subject.params do
requires :arry, type: type
end
subject.get '/array' do
params[:arry].class
end
get '/array', arry: nil
expect(last_response.status).to eq(200)
expect(last_response.body).to eq(expected_class)
end
end
end
Therefore, I was expecting that an array of types with nil
like Array[Integer]
as param value would behave like a plain Array
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, please do add those specs and let's make sure to document this behavior?
|
44e0498
to
aba1f3c
Compare
@@ -752,19 +776,6 @@ def self.parsed?(value) | |||
expect(last_response.body).to eq('String') | |||
end | |||
|
|||
it 'respects nil values' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer working? Aren't we breaking something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be flip-flopping on what we want, let's step back. For this example alone, when I say :a
can be either an array of File
or a String
, and I pass nil
, don't I want a nil
and not []
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this example alone, when I say :a can be either an array of File or a String, and I pass nil, don't I want a nil and not []?
That was the behavior of Grape prior to v1.3.0. v1.3.x broke our tests with that, which is why I filed #2018.
README.md
Outdated
@@ -1070,6 +1071,26 @@ params do | |||
end | |||
``` | |||
|
|||
### Structures Implicit Default Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this it feels like it's wrong. Yes, it works this way. But if I send a nil
, why am I getting a non-nil?
Here's what I think we want:
My question is, why would we not want to do what I am suggesting? |
@dblock That sounds like the right approach to me. I don't see any issues with that at the moment. |
@dblock sound good to me too. For the default part (2), will need to adjust the context 'integer default value when nil' do
it 'respects the default value' do
subject.params do
optional :int, type: Integer, default: 1
end
subject.get '/default_value' do
params[:int].class
end
get '/default_value', int: nil
expect(last_response.status).to eq(200)
expect(last_response.body).to eq("Integer")
end
end but this one works context 'integer default value when nil' do
it 'respects the default value' do
subject.params do
optional :int, type: Integer, default: 1
end
subject.get '/default_value' do
params[:int].class
end
get '/default_value'
expect(last_response.status).to eq(200)
expect(last_response.body).to eq("Integer")
end
end It's just a matter the presence or not of the param. So should we adjust the default validator behavior ? |
Yes, I believe so. A |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good! Looks like we're not breaking any unrelated existing behavior, and I feel good about the thoroughness of tests.
9b03676
to
be04f92
Compare
LGTM, needs README and UPGRADING and I think it's good to go. Thank you! Would appreciate another pair of eyes, maybe from @dnesteryuk, @stanhu. |
Thanks, this is looking good to me too. I ran this branch against our test suite. As expected, I found a few tests that failed because we had |
Let's also bump version to 1.4. |
@dblock I found a real simple case that doesn't work which is
|
We'll have to discuss about the current spec failures @dblock |
@@ -574,7 +574,7 @@ def validate_param!(attr_name, params) | |||
# NOTE: with body parameters in json or XML or similar this | |||
# should actually fail with: children[parents][name] is missing. | |||
expect(last_response.status).to eq(400) | |||
expect(last_response.body).to eq('children[1][parents] is missing') | |||
expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, it is something I would like to avoid. If a parent node is missing, why do we need to check children? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put it in "nice to have". Generally, you could see it both ways. If the info is not there, you see an error, then you add a parent, then it tells you that you're still missing children. Get frustrated, repeat.
In general, the idea and PR look right to me, there is only one concern about validating children in a structure when a parent node is missing. expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing') But, I know that it is super hard to fix, validations are flat, so validation failure for parents doesn't interrupt validation for children. We might leave it as it is in this PR. |
What about this test that returns 200 instead of 400 ? params do
requires :names, type: { value: Array[String], message: 'can\'t be nil' }, regexp: { value: /^[a-z]+$/, message: 'format is invalid' }
end
get 'regexp_with_array' do
end
it 'refuses nil instead of array' do
get '/regexp_with_array', names: nil
expect(last_response.status).to eq(400)
expect(last_response.body).to eq('{"error":"names can\'t be nil"}')
end There are 3 similar specs that returns 200 now instead of 400 1) Grape::Validations::RegexpValidator custom validation message regexp with array refuses nil instead of array
Failure/Error: expect(last_response.status).to eq(400)
expected: 400
got: 200
(compared using ==)
# ./spec/grape/validations/validators/regexp_spec.rb:103:in `block (4 levels) in <top (required)>'
2) Grape::Validations::RegexpValidator regexp with array rejects nil instead of array
Failure/Error: expect(last_response.status).to eq(400)
expected: 400
got: 200
(compared using ==)
# ./spec/grape/validations/validators/regexp_spec.rb:159:in `block (3 levels) in <top (required)>'
3) Grape::Validations::ValuesValidator nil value for a parameter rejects for an optional param with a list of values
Failure/Error: expect(last_response.status).to eq 400
expected: 400
got: 200
(compared using ==)
# ./spec/grape/validations/validators/values_spec.rb:324:in `block (3 levels) in <top (required)>' |
In these specs we are saying that the value has to be an |
Add CHANGELOG.md Add documentation in README.md Add specs for future proof return nil instead of InvalidValue Remove Structures Implicit Default Value section Not returning when param's value is nil Remove default value for structures Add specs for nil values and default values Add non-empty value spec when params is nil Typo change Rubocop Array[Integer] case doesn't work with default rejects Fix Array[Integer] Fix specs where nil is now a valid value Replaced non-empty-json by non-empty-string
72541d9
to
3bcdc29
Compare
So a fixed the remaining specs based on your last comments. I guess we're up to CHANGELOG, README, and UPGRADING.md :) |
Awesome work @ericproulx. Yes please on README/UPGRADING. |
By chance, do you guys have any time estimate on when a new Grape version will be released with this fix in it? It looks like the change is mostly done besides a couple doc updates. |
I’ll work the doc this weekend
… On Apr 24, 2020, at 22:27, Zachary Wileman ***@***.***> wrote:
By chance, do you guys have any time estimate on when a new Grape version will be released with this fix in it? It looks like the change is mostly done besides a couple doc updates.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#2040 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ACAHJI543DOQATCT5AIHX4DROHY4PANCNFSM4MKJFCAA>.
|
Unfortunately, I didnt do it. If someone wants to write the DOC that would be great. I'm very busy at work right now probably like you. |
This is a tremendous job @ericproulx. Thank you. |
Thank you :)
… On May 3, 2020, at 19:33, Daniel Doubrovkine (dB.) @dblockdotorg ***@***.***> wrote:
This is a tremendous job @ericproulx <https://github.com/ericproulx>. Thank you.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#2040 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ACAHJI3O5F2CGFFC3YFKBP3RPWTEZANCNFSM4MKJFCAA>.
|
Does anyone know when a new version of Grape is going to be released that includes this fix? |
@ericproulx just FYI this is causing us a bit of grief - not sure if it's an error you've seen too #1717 (comment) |
Sorry about that, I'll investigate. First thought : default is applied when param is nil or when param is missing. So missing and nil are equivalent but should it ? @dblock |
That sounds right. https://github.com/ruby-grape/grape#parameter-validation-and-coercion doesn't specifically clarify this I assumed |
This is the correct behavior. I particularly find the |
Since #2019, I found a regression (our tests suite fails with 1.3.2) when params type is an
Array
ofType
and the api is called with anil
value. It should return the default value which is[]
.