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

string().min(0) is failing to validate a zero-length string #2687

Closed
gh-andre opened this issue Oct 10, 2021 · 4 comments
Closed

string().min(0) is failing to validate a zero-length string #2687

gh-andre opened this issue Oct 10, 2021 · 4 comments
Assignees
Labels
feature New functionality or improvement
Milestone

Comments

@gh-andre
Copy link

Support plan

  • is this issue currently blocking your project? (yes/no): no
  • is this issue affecting a production system? (yes/no): no

Context

  • node version: 12.15.0
  • module version with issue: 17.4.2
  • last module version without issue: n/a
  • environment (e.g. node, browser, native): node
  • used with (e.g. hapi application, another framework, standalone, ...): standalone
  • any other relevant information:

What are you trying to achieve or the steps to reproduce?

I'm trying to validate form submissions and API requests (separate components) in which some values may be either explicitly empty or may not be present in the submission format at all. For example, if an input has a disabled attribute, it will not be submitted at all, while the one with an empty value will be submitted as such. In other words,

<input name="abc" value="" disabled>
<input name="xyz" value="">

, will yield this query, where abc is not present:

/?xyz=

Same for API clients - some fields may be missing and some may be explicitly empty strings.

I would like to be able to distinguish missing fields from empty fields using optinal() and min(0), just as described in the docs. That is, given this validation object:

Joi.object({
    abc: Joi.string().optional().min(0).max(5),
    xyz: Joi.string().allow("").max(5),
    presence: "required"
});

, following objects should be valid. Note that xyz is just for contrast and abc is the example.

{xyz: ""}
{xyz: "", abc: ""}
{xyz: "", abc: "123"}

min(5) applied in the string context is the string length, so it stands to reason that min(0) would allow empty strings. The docs don't call out that min(0) isn't allowed to be used for strings, which wouldn't make much sense, anyway.

Also, note that allow("") isn't the same as min(0), even though it's commonly advised as a workaround, which is what it is. Yes, I can compare against an empty string, which is the only option now, but comparing two strings and comparing their lengths are two different logical operations and if I could set up validation rules with min(0), I would prefer to do just that to align with min(5) in this case.

I see quite often confusion between empty string arguments in form/API submissions and non-existing arguments, as is evident from the discussion in issue #482. I'm happy that Joi distinguishes the two with optional/required, similar to how Mongo DB uses the $exists operator, but string().min(0) is not following this logic and forces one having to compare values instead of comparing just lengths.

Here's a small example with both validation cases having correctly formatted objects being validated, but while the first one correctly allows abc to be omitted, the second one fails to apply min(0) against a zero-length string and incorrectly reports abc as an invalid empty string.

let Joi = require("joi");

let validateOptions = {
    abortEarly: false,
    allowUnknown: false,
    presence: "required"
};

//
// Expected:
//
//  * `abc` may be missing or present as a 0-5 character string
//  * `xyz` must be present, may be equal to "" of have length
//    up to 5 characters
//
let validationSchema = Joi.object({
    abc: Joi.string().optional().min(0).max(5),
    xyz: Joi.string().allow("").max(5)
});

//
// Correctly allows field `abc` to be missing
// Correctly allows field `xyz` to be present and empty
//
let validated = validationSchema.validate({xyz: ""}, validateOptions);

if(validated.error)
    console.error("FAIL: %s", validated.error.message);
else
    console.log("PASS: %s", JSON.stringify(validated.value));

//
// Correctly allows `xyz` to be empty
// Fails to validate `abc` as a present zero-length string
//
validated = validationSchema.validate({xyz: "", abc: ""}, validateOptions);

if(validated.error)
    console.error("FAIL: %s", validated.error.message);
else
    console.log("PASS: %s", JSON.stringify(validated.value));

What was the result you got?

string().min(0) fails to accept a zero length string.

What result did you expect?

I expect string().min(0) to work the same way as string().min(5) does in enforcing the string length being 5 characters and allow a zero-length string to be considered valid..

@hueniverse
Copy link
Contributor

Empty strings are special in joi. You can find a long history on this. But I see your point since you are explicitly asking it to allow empty strings.

@hueniverse hueniverse self-assigned this Dec 2, 2021
@hueniverse hueniverse added the feature New functionality or improvement label Dec 2, 2021
@hueniverse hueniverse added this to the 17.5.0 milestone Dec 2, 2021
@gh-andre
Copy link
Author

gh-andre commented Dec 2, 2021

@hueniverse Ok. Thanks for giving it a thought.

@hueniverse
Copy link
Contributor

@gh-andre It's done... I didn't just close it.

@gh-andre
Copy link
Author

gh-andre commented Dec 2, 2021

@hueniverse My apologies. I missed the commit in the closing email. Thanks a lot for this change. Joi is awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New functionality or improvement
Projects
None yet
Development

No branches or pull requests

2 participants