joi
joi copied to clipboard
unknown(false) is not work when configuration "allowUnknown" is true
Support plan
- is this issue currently blocking your project? (yes/no): yes
- is this issue affecting a production system? (yes/no): yes
Context
- node version: 14.20.0
- module version: 17.6.0
- environment (e.g. node, browser, native): node
- used with (e.g. hapi application, another framework, standalone, ...): node
- any other relevant information:
How can we help?
Our project use version 14 before, we want to allow all fields of all object except for some fields we don't expect For example, we want to allow field "e" of data.d only, and allow other unknown fields for data.b and even data, we use below code:
const joi = require('joi');
const schema = joi.object().keys({
a: joi.string(),
b: joi.object().keys({
c: joi.string()
}),
d: joi.object().keys({
e: joi.string()
}).unknown(false)
});
const value = {
a: '123',
b: {
c: '123',
f: 123
},
d: {
e: '12',
g: 123
},
h: 123
};
const result = schema.validate(value, {
abortEarly: false,
allowUnknown: true
});
console.log(result?.error?.message);
// child "d" fails because ["g" is not allowed]
After we use version 17, the data pass the validation, which we do not expect.
Do you have any suggestions?
It looks like a bug. In the meantime, you can replace .unknown(false)
by .options({ allowUnknown: false })
, just beware that it's inherited by children schemas.
Thanks Marsup. By the way, I look up the source code and found where is this error caused. It is in /lib/base.js.
$_setFlag(name, value, options = {}) {
Assert(name[0] === '_' || !this._inRuleset(), 'Cannot set flag inside a ruleset');
const flag = this._definition.flags[name] || {};
if (DeepEqual(value, flag.default)) {
value = undefined;
}
if (DeepEqual(value, this._flags[name])) {
return this;
}
}
It is so weird to set the value to undefined when value same as the default value of flag. But once I delete this if statement, many tests failed. Please help have a check.
Hi folks. Could I work on that issue?
It's not the easiest one but sure.
Thanks @Marsup .
Hi, @LuEason I tested this code you sent using Joi 17.6.1 and it looks like it works as you expect.
It doesn't have any errors. When I print the result I get the following:
{
value: { a: '123', b: { c: '123', f: 123 }, d: { e: '12', g: 123 }, h: 123 }
}
Maybe it was already fixed @Marsup .
@geeksilva97 that's precisely the problem, it should fail on object d because g is not allowed.
@marsup
It looks like a bug
This is expected:
from the docs
allowUnknown
- whentrue
, allows object to contain unknown keys which are ignored. Defaults to false.
allowUknown
will override any .unknown(false)
settings you have
const schema = joi.object().keys({
foo: joi.object().keys({
bar: joi.string()
}).unknown(false)
})
console.log(
schema.validate({
foo: {
bar: "bar",
baaz: "baaz"
}
})
) // "foo.baaz" is not allowed
console.log(
schema.validate({
foo: {
bar: "bar",
baaz: "baaz"
}
}, {allowUnknown: true})
) // valid
@LuEason
you can turn on this feature by not passing allowUnknown: true
and instead add granular uknown(false)
entries to the your desired keys
Local should always override global, it is imo a bug.
fair enough, i now see this is what happens in the yup lib as well
const schema = yup.object().shape({
foo: yup.object({
bar: yup.string()
}).noUnknown(true)
})
console.log(
schema.validate({
foo: {
bar: "bar",
baaz: "baaz"
}
}, {stripUnknown: false})
) // foo field has unspecified keys: baaz