Joi nested when validation - javascript

I am trying to validate a nested object conditionally based upon a value in the parent.
const schema = Joi.object({
a: Joi.string(),
b: Joi.object({
c: Joi.when(Joi.ref('..a'), { is: 'foo', then: Joi.number().valid(1), otherwise: Joi.number().valid(2) }),
}),
});
const obj = {
a: 'foo',
b: {
c: 2,
},
};
In this example, I want to get an error that c must be 1, but the validation passes. I've tried with and without references, but I clearly must be misunderstanding something fundamental about how Joi works. Any help?

you need one more . in your Joi.ref() call. .. will go up to the parent tree, then another dot to signify the property. So for your case it would go to the parent .. then get the a property parent.a
Using the Joi playground this worked for me:
Joi.object({
a: Joi.string(),
b: Joi.object({
c: Joi.when(Joi.ref('...a'), {
is: 'foo',
then: Joi.number().valid(1),
otherwise: Joi.number().valid(2)
})
})
})

If you don't need Joi.ref, this would still work with ... referencing the parent's sibling, like about14sheep pointed out in their answer. I ended up doing something like this:
Joi.object({
a: Joi.string(),
b: Joi.object({
c: Joi.when('...a', {
is: 'foo',
then: Joi.number().valid(1),
otherwise: Joi.number().valid(2),
}),
}),
});

Related

How to update a schema object, after creation, inside a Joi object?

Wasn't sure the best way to phrase the question, but here is an example of what I am trying to do.
const schema = Joi.Object().keys({
array: Joi.array().length(5)
});
Then after creation I am wanting to update the length method to be a different number.
this.schema.keys({ array: Joi.array().length(8) });
The above code does not work and I'm really just not sure what else to try. (This isn't the only thing I have tried, just where I'm currently at) I've been looking through documentation but haven't found anything helpful for updating a schema. Maybe someone knows of a way to do it?
Any help would be much appreciated!
const Joi = require('joi');
////
// update schema itself.
////
let scheme = Joi.object().keys({
foo: Joi.array().length(5),
});
const a1 = scheme.validate({ foo: [1,2,3,4,5] });
console.log(a1.error); // undefined. valid.
scheme = scheme.keys({
foo: Joi.array().length(8),
});
const a2 = scheme.validate({ foo: [1,2,3,4,5] });
console.log(a2.error); // array.length error occurred.
const a3 = scheme.validate({ foo: [1,2,3,4,5,6,7,8] });
console.log(a3.error); // undefined. vaild.
////
// using base scheme.
////
const base = Joi.object().keys({
foo: Joi.array(),
});
const base_a = base.keys({
foo: Joi.array().length(5),
});
console.log('Using base a1', (base_a.validate({ foo: [1,2,3,4,5]})).error);
const base_b = base.keys({
foo: Joi.array().length(8),
});
console.log('Using base a2', (base_b.validate({ foo: [1,2,3,4,5]})).error);
console.log('Using base a3', (base_b.validate({ foo: [1,2,3,4,5,6,7,8]})).error);
////
// Not good but works.
////
const obj = Joi.object({
foo: Joi.array().length(5),
});
console.log('Obj a1', (obj.validate({ foo: [1,2,3,4,5]})).error);
const merged = Object.assign(obj, Joi.object({
foo: Joi.array().length(8),
}));
console.log('Obj a2', (merged.validate({ foo: [1,2,3,4,5]})).error);
console.log('Obj a3', (merged.validate({ foo: [1,2,3,4,5,6,7,8]})).error);

How to validate object with two max condition on a value in Joi?

I want to validate this object using Joi with conditions:
b should be greater than 2.
b can not greater than a.
b can not greater than 600
// valid object
var object = {
a: 5,
b: 3
}
// invalid object because b > a
var object = {
a: 5,
b: 6
}
I treid to build Joi schema
var schema = Joi.object({
a: Joi.number().integer(),
b: Joi.number().integer().min(2).max(Joi.ref('a')).max(600)
})
This schema is ignoring b<=a condition so invalid object is also seen as valid. What should be correct schema to apply all the conditions?
Using less() which specifies that the value must be less than limit or a reference.
var schema = Joi.object({
a: Joi.number().integer(),
b: Joi.number().integer().less(Joi.ref('a')).min(2).max(600)
})
stackblitz
var schema = Joi.object({
a: Joi.number().integer(),
b: Joi.number().integer().min(2).max(Joi.ref('a')).less(601)
})

How to force an attribute to be present only if another one is true?

I'm trying to validate a simple object using Joi. The schema i defined is the following:
const singleReq = Joi.object({
subscribing: Joi.boolean(),
duration: Joi.number().integer().positive(),
});
I would like duration to be present (not null) only when subscribing is true. I'm trying to do it with asserts but i can't figure out how.
You could try something like below:
const singleReq = Joi.object({
subscribing: Joi.boolean(),
duration: Joi.number()
.when('subscribing', { is: true, then: Joi.integer().positive().required() })
});
If you want change of type, you could also try to use alternatives like the below example:
const schema = {
a: Joi.alternatives().when('b', { is: 5, then: Joi.string(), otherwise: Joi.number() }),
b: Joi.any()
};
references

Joi nested schemas and default values

I'm trying to get Joi to enforce default values on a secondary schema referenced by another. I have two schemas like so:
const schemaA = Joi.object().keys({
title: Joi.string().default(''),
time: Joi.number().min(1).default(5000)
})
const schemaB = Joi.object().keys({
enabled: Joi.bool().default(false),
a: schemaA
})
What I want is to provide an object where a is not defined and have Joi apply the default values for it instead like this:
const input = {enabled: true}
const {value} = schemaB.validate(input)
//Expect value to equal this:
const expected = {
enabled: true,
a: {
title: '',
time: 5000
}
}
The problem is that since the key is optional it is simply not enforced. So what I want is for it to be optional yet properly filled with schemaA defaults if not present. I've been looking through the documentation, but can't seem to find any info on this though I'm probably missing something obvious. Any tips?
Update : April, 2020.
Now, you can use default() in nested objects. Here is the commit in repo with test.
var schema = Joi.object({
a: Joi.number().default(42),
b: Joi.object({
c: Joi.boolean().default(true),
d: Joi.string()
}).default()
}).default();
This should do it:
const schemaA = Joi.object().keys({
title: Joi.string().default(''),
time: Joi.number().min(1).default(5000),
});
const schemaB = Joi.object().keys({
enabled: Joi.bool().default(false),
a: schemaA.default(schemaA.validate({}).value),
});
Although it would be much better if they would implement a feature to let us pass in Joi schema objects for defaults, like so: schemaA.default(schemaA) or schemaA.default('object')

Joi concat with object nested in array

I currently have this Joi scheme:
scheme1 = Joi.object({
arr: Joi.array.items(Joi.object().keys({
value: Joi.number()
}))
})
And another one (that I can't edit/read) which looks the same with different object keys
scheme2 = Joi.object({
arr: Joi.array.items(Joi.object({
otherValue: Joi.number(),
moreValues: Joi.string()
}))
})
I now need to merge these in such a way that I get something like this:
result = Joi.object({
arr: Joi.array.items(Joi.object({
value: Joi.number(),
otherValue: Joi.number(),
moreValues: Joi.string()
}))
})
By using scheme1.concat(scheme2) I only get:
Joi.array.items(object1, object2)
How can I do this, without modifying or accessing (apart from concat) the second scheme?

Categories

Resources