Reputation: 285
I have a field in a JOI schema that I would like to be optional (i.e. undefined is accepted, and null is also accepted), however if a value for it is supplied, it must be a positive integer. How might I go about achieving this?
Here is what I have tried so far, with the field to validate being "capacity" however it does not seem to work, it appears the ".when" statement is just being ignored:
const divebarSchema = joi.object({
divebar: joi
.object({
title: joi.string().required(),
capacity: joi.optional().allow(null),
description: joi.string().required(),
location: joi.string().required(),
image: joi.string().optional().allow(""),
map: joi.string().optional().allow(""),
})
.required()
.when(joi.object({ capacity: joi.exist() }), {
then: joi.object({ capacity: joi.number().integer().min(0) }),
}),
});
Before the above I originally had no .when and instead the capacity rule was:
capacity: joi.number().optional().allow(null).integer().min(0),
However that also did not work, it kept throwing the error "must be a number" when submitting a null value.
Upvotes: 8
Views: 13390
Reputation: 4729
To Allow null:
capacity: Joi.string().max(255).allow(null)
To Allow empty string:
capacity: Joi.string().max(255).allow('')
To Allow both:
capacity: Joi.string().max(255).allow(null, '')
Upvotes: 0
Reputation: 11037
You need to use any.empty
rather than any.allow
to make it so that null
ends up considered as an empty value. This will mean that null
gets stripped out of the resulting value. If you don't want null
capacity to be stripped from the resulting object, you can use allow(null)
as mentioned in the other answer.
I've included a snippet of code that uses both for comparison and shows the validation rules fully applying for both.
const divebarSchemaEmpty = joi.object({
divebar: joi
.object({
title: joi.string().required(),
capacity: joi.number().empty(null).integer().min(0),
description: joi.string().required(),
location: joi.string().required(),
image: joi.string().allow(""),
map: joi.string().allow(""),
})
.required(),
});
const divebarSchemaAllow = joi.object({
divebar: joi
.object({
title: joi.string().required(),
capacity: joi.number().allow(null).integer().min(0),
description: joi.string().required(),
location: joi.string().required(),
image: joi.string().allow(""),
map: joi.string().allow(""),
})
.required(),
});
const baseObject = {
divebar: {
title: 'capacity',
description: 'test',
location: 'here',
}
};
const schemaRuns = [{
title: 'Empty',
schema: divebarSchemaEmpty
}, {
title: 'Allow',
schema: divebarSchemaAllow
}];
const runs = [{
title: 'null capacity',
data: {
capacity: null
}
},
{
title: 'missing capacity',
data: {}
},
{
title: 'undefined capacity',
data: {
capacity: undefined
}
},
{
title: 'positive capacity',
data: {
capacity: 5
}
},
{
title: 'negative capacity',
data: {
capacity: -1
},
fails: true
},
{
title: 'float capacity',
data: {
capacity: 0.25
},
fails: true
}
];
for (const {
title: baseTitle,
data: override,
fails
} of runs) {
for (const {
title: schemaTitle,
schema
} of schemaRuns) {
const title = `${schemaTitle}->${baseTitle}`;
const data = { ...baseObject,
divebar: { ...baseObject.divebar,
...override,
title
}
};
try {
const result = joi.attempt(data, schema);
if (fails) {
throw new Error(`${title} succeeded validation when expected to fail`)
}
console.log(`${title} passed with data`, result);
} catch (err) {
if (joi.isError(err)) {
if (fails) {
console.log(`${title} passed with error object`, err)
} else {
// unexpected error!
console.error(err);
}
} else {
console.error(err);
}
}
}
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/joi-browser.min.js"></script>
Upvotes: 0