dry-validation: Rule fired for optional attribute which is missing
Description
Rule is getting fired for a required attribute which is nested within an optional attribute and when the whole optional hash is missing (which should be allowed) it reports an error causing the validation to fail.
To Reproduce
Create a contract with a rule for an optional hash attribute with a required key + a rule for the required key and run with an input not including that hash. I use custom macro and I don’t know if it’s related to the bug.
So given:
class SignUpContract < Dry::Validation::Contract
params do
required(:user).hash do
optional(:id) { filled? & str? }
optional(:first_name) { filled? & str? }
optional(:last_name) { filled? & str? }
required(:email) { filled? & str? }
required(:password) { filled? & str? }
required(:accepts_terms) { filled? & bool? }
optional(:freelancer).hash do
required(:id) { filled? & str? }
end
optional(:clients).array(:hash) do
required(:id) { filled? & str? }
end
end
end
rule('user.id').validate(:uuid_format)
rule('user.email').validate(:email_format)
rule('user.password').validate(min_length: 8)
rule(user: %i[freelancer clients]) do
freelancer = values.dig(:user, :freelancer)
clients = values.dig(:user, :clients)
unless freelancer ^ (clients && clients.size == 1)
key(:user).failure(:profile_missing)
end
end
rule('user.freelancer.id').validate(:uuid_format)
rule('user.clients') do
have_valid_uuid = lambda { |client|
client[:id].nil? || client[:id].match?(Macros::UUID_REGEXP)
}
unless value.all?(have_valid_uuid)
key.failure('not a valid UUID format')
end
end
end
And params:
{
"user": {
"id": "d16dfef3-50f9-4588-b7d7-837801e403cd",
"first_name": "Blah",
"last_name": "Blah",
"email": "blah@blah.blah",
"password": "blahblahblah",
"accepts_terms": true,
"clients": [
{
"id": "d287fdff-78ac-45ed-871b-b1b5c9548399"
}
]
}
}
It produces:
#<Dry::Validation::Result{:user=>{:id=>"d16dfef3-50f9-4588-b7d7-837801e403cd", :first_name=>"Blah", :last_name=>"Blah", :email=>"blah@blah.blah", :password=>"blahblahblah", :accepts_terms=>true, :clients=>[{:id=>"d287fdff-78ac-45ed-871b-b1b5c9548399"}]}} errors={:user=>{:freelancer=>{:id=>["not a valid UUID format"]}}}>
Expected behavior
The validation rule for optional attribute should never be run when it’s missing. In the example rules related to freelancer attributes should not be run and the validation should pass
My environment
- Affects my production application: NO as I haven’t upgraded yet
- Ruby version: 2.6.3
- OS: MacOS Mojave Version 10.14.5 (Darwin Mateuszs-MacBook-Pro.local 18.6.0 Darwin Kernel Version 18.6.0: Thu Apr 25 23:16:27 PDT 2019; root:xnu-4903.261.4~2/RELEASE_X86_64 x86_64)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 17 (10 by maintainers)
Commits related to this issue
- Add Values#key? and Evaluator#value? This is helpful when dealing with optional keys. We can't just assume that we do not want to execute a rule when a value was missing. Good example: a password mu... — committed to dry-rb/dry-validation by deleted user 5 years ago
- Add Values#key? and Evaluator#value? This is helpful when dealing with optional keys. We can't just assume that we do not want to execute a rule when a value was missing. Good example: a password mu... — committed to dry-rb/dry-validation by deleted user 5 years ago
:nil?looks ambiguous. It may mean key or value.:value?may be more suitable:I know where it’s coming from. The spectre of ActiveModel validation haunts us all.
@musaffa I reported two feature requests:
It’s tentatively scheduled to be part of 1.7.0 release. I marked both as
help wantedfor the time being though. I’ll give this a shot during xmas break unless somebody beats me to it.Maybe
rule?instead ofrule(...).if(:key?)as a shorthand?for the record, this thing in PM is usually called “guard”, just to save some time on coming with proper naming which is, you know, hard…