Joi array Object validation based on root key value - javascript

I have a complex scenario that I want to validate using Joi
here sample Joi Object Schema
const itemSchema = Joi.object({
product_id: Joi.string().required(),
quantity: Joi.number().required().min(0)
});
let objSchema = {
items: Joi.array().items(itemSchema).required().min(1),
item_return_flag: Joi.string().optional().valid(true, false)
};
depending opon item_return_flag key value true or false, I want to change the items.quantity min value requirement. When true, quantity will be 0 , otherwise it will be 1.
Is there anyway, to control the definition of validation of the object in an array, based on the root object in Joi

The sample code that will switch the schema based one the parent key item_return_flag. Schema of the array need to switch based using Joi.altertnatives()
let itemArr = Joi.object({
product_id: Joi.string().required(),
quantity: Joi.number().required().min(0)
});
let itemArr2 = Joi.object({
product_id: Joi.string().required(),
quantity: Joi.number().required().min(1)
});
let itemSchema = Joi.alternatives()
.when('item_return_flag', { is: true, then: Joi.array().items(itemArr).required().min(1), otherwise: Joi.array().items(itemArr2).required().min(1)}) ;
let objSchema = {
items: itemSchema,
item_return_flag: Joi.string().optional().valid(true, false)
};

It looks to me like you could, following the API docs, do something like this:
let objSchema = {
items: Joi.array().items(Joi.object({
product_id: Joi.string().required(),
quantity: Joi.alternatives().when('item_return_flag', {
is: true, then: Joi.number().required().min(0),
otherwise: Joi.number().required().min(1)
})
})).required().min(1),
item_return_flag: Joi.string().optional().valid(true, false)
};
I'm not 100% sure that's the exact correct structure, but it's close. The Joi.alternatives() is provided for just such use cases.

Related

Set field max to equals another field (array) min value

I'm currently using joi to valite my data and I want to adchieve the next:
const schema = Joi.object({
arr: Joi.array().min(1).required(),
qty: Joi.number().min(1).max(/* arr.min */)
});
Is there a way to do so? If so, how? I've been trying with:
const schema = Joi.object({
arr: Joi.array().min(1).required(),
qty: Joi.number().min(1).max(Joi.ref('arr'))
});
But it doesn't seem to work.
Thanks in advance!

Complex validation using Joi library

I have this json:
let purchaseSubscription = {
'metadata': {
'eventName': 'PurchaseSubscription',
'type': 'setup' // setup, repurchase or recurring
},
'data': {
'subscriptionId': '447481',
'subscriptionTrialId': '23542'
}
};
If the metadata.type has value setup
then data.subscriptionTrialId should be validated for existence and to be a number.
If the metadata.type has other values, the data.subscriptionTrialId can be ignored.
This is what I currently have:
const Joi = require('joi');
const validTypes = ['setup', 'repurchase', 'recurring'];
exports.schema = Joi.object().keys({
metadata: Joi.object({
eventName: Joi.string().required(),
type: Joi.string().valid(validTypes).required()
}).required(),
data: Joi.object({
subscriptionId: Joi.number().integer().min(1).max(2147483647).required(),
subscriptionTrialId: Joi.when(
'metadata.type', { is: 'setup', then: Joi.required() })
}).required()
}).options({ 'allowUnknown': true });
But I am not getting desired results. The data.subscriptionTrialId is always validated, no matter what I have under metadata.type
I tried reading documentation, but can't make it to work :(
You can use the otherwise key in the JOI schema.
Somewhere in your code before declaring exports.schema:
const trialIdRequired = Joi.object({
subscriptionId: Joi.number().integer().min(1).max(2147483647).required(),
subscriptionTrialId: Joi.required()
}).required()
const trialIdNotRequired = Joi.object({
subscriptionId: Joi.number().integer().min(1).max(2147483647).required(),
subscriptionTrialId: Joi.any()
})
And then add a when clause to the data field
data: Joi.when(
'metadata.type',
{
is: 'setup',
then: trialIdRequired,
otherwise: trialIdNotRequired
})

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

Array of objects with different schemas - JOI

I currently have the following schema on my app:
Joi.object().keys({
users: Joi.array().items(mySchema)
})
So I can get an array of users and validate them.
But now I need different schemas for each object.
Is there a way I can do something like:
Joi.object().keys({
users: [
Joi.object().keys(mySchemaForUserOne),
Joi.object().keys(mySchemaForUserTwo),
// ...
]
})
Here you go ~
Joi.object().keys({
users: Joi.array().items(
Joi.alternatives()
.conditional('.type', {
switch: [{
is: 'mySchemaForUserOne',
then: Joi.object({ ... }),
}, {
is: 'mySchemaForUserTwo',
then: Joi.object({ ... }),
}],
})
)
})
You can check documents here =>
Joi conditional API
You can use array.items by listing all allowed types. If a given type is .required() then there must be a matching item in the array: joi API reference
users: Joi.array().items(Joi.object().keys(mySchemaForUserOne).required(),
Joi.object().keys(mySchemaForUserTwo).required(),
// ...)

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')

Categories

Resources