Making one input as required based on the input of another key - javascript

I am using JOI for schema validation. In the following schema, I want input_file to be of type required when type is jobType.MBR, otherwise file_name must remain of type required
const jobObjectSchema = {
type: Joi.string().valid(jobType.MBR, jobType.MP4).required(),
file_name: Joi.string().required(),
input_file: Joi.string()
};
How can I do this?

Use Joi any().when.
const jobObjectSchema = {
type: Joi.string().valid(jobType.MBR, jobType.MP4).required(),
file_name: Joi.any().when('type', {
is: jobType.MBR,
then: Joi.string().optional(),
otherwise: Joi.string().required()
}),
input_file: Joi.any().when('type', {
is: jobType.MBR,
then: Joi.string().required(),
otherwise: Joi.string().optional()
})
};

Related

TypeScript Yup schema validation and Type Inference

I currently have the following data type:
type TPlan =
| {
type: "pro";
content: { signedAt: string; expiresOn: string };
}
| {
type: "default" | "regular";
content: { signedAt: string };
};
And taking into account the previous data type, I defined a schema for each structure of the object:
const schemaA = object({
type: mixed<"pro">().oneOf(["pro"]).defined(),
content: object({
signedAt: string().defined(),
expiresOn: string().defined(),
}),
});
const schemaB = object({
type: mixed<"default" | "regular">().oneOf(["default", "regular"]).defined(),
content: object({
signedAt: string().defined(),
}),
});
When I do the individual inference of the schemas, the data types are correct, as follows:
type TSchemaA = InferType<typeof schemaA>;
type TSchemaB = InferType<typeof schemaB>;
However, when I go to define the validation schema, where I use the .oneOf() method, TypeScript says that the data types are incorrect. I did it this way:
export const validationSchema = mixed<TSchemaA | TSchemaB>()
.oneOf([schemaA, schemaB])
.defined();
And my question is the following, how can I create a validation schema using Yup taking into account the initial type (TPlan)? Because in addition to needing the validation schemas to validate the JSON objects, I will also need the inference of the schemas to be correct as well.
export const validationSchema = mixed<TSchemaA | TSchemaB>()
.oneOf([schemaA, schemaB])
.defined();
This will not do what you want.
It will create a yup schema that could be used to validate TSchemaA or TSchemaB. But you do not want to validate schema, you want to validate data.
Here is my proposal :
const schemaPro = yup.object({
type: yup.mixed<"pro">().oneOf(["pro"]).required(),
content: yup
.object({
signedAt: yup.string().required(),
expiresOn: yup.string().required(),
})
.required(),
});
const schemaDefaultOrRegular = yup.object({
type: yup.mixed<"default" | "regular">().oneOf(["default", "regular"]).required(),
content: yup.object({
signedAt: yup.string().required(),
}),
});
then this helper that will validate against multiple schema :
const validateOneOf = async (schemas: yup.AnySchema[], value: unknown): Promise<TPlan> => {
for (const schema of schemas) {
try {
return await schema.validate(value, { stripUnknown: true });
} catch (e) {
// I know you can create a yup error, that concat all error for the loop.
// then you should be able to fire it at the end.
}
}
throw new yup.ValidationError("No schema validated");
};
I have casted output as TPlan, that should be ok for your case.
then call it :
try {
const plan: TPlan = await validateOneOf([schemaPro, schemaDefaultOrRegular], {
type: "default",
content: { signedAt: undefined, expiresOn: "456" },
});
} catch (e) {
console.log("ERROR " + e);
}

How to validate with .when statement in yup with parent object

I have this yup which is works:
const exceptionSchema = yup.object().shape({
exceptionRule: yup.object().shape({
exceptionCondition: yup.object().shape({
exceptionCount: yup.string().required('Exception_Count_Required')
}),
selectedExceptions: yup
.array()
.ensure()
.min(1, 'Validate_ExceptionsAssigned')
})
});
But I have to validate this subobject selectedExceptions when some other property is equals to 1:
and this doesnt work:
const exceptionSchema = yup.object().shape({
exceptionRule: yup.object().shape({
exceptionCondition: yup.object().shape({
exceptionCount: yup.string().required('Exception_Count_Required')
}),
selectedExceptions: yup
.array()
.ensure()
// .min(1, 'Validate_ExceptionsAssigned')
.when('type', {
is: 1,
then: yup
.array()
.ensure()
.min(1, 'Validate_ExceptionsAssigned')
})
})
});
this is mine model, and I think because the type proeprty is not within this object exceptionRule then yup validation does not take into consideration this .when() clause.
Model:
const newRule = () => ({
type: '',
exceptionRule: {
exceptionCondition: {
exceptionCount: '1',
condition: 1,
frequency: 1
},
selectedExceptions: [],
}
});
How to get this type from above, outside my inner object?
How can I debug this when('type') to be sure what value does it have?

How to make custom error message using Joi?

How to make a custom message using joi? i saw many answered question related on this but i dont know why it didnt work on my end, the error message always appeared is "Student" does not contain 1 required value(s) what i want is "Student" This field is required.
export const VALIDATION_SCHEMA = {
students: Joi.array()
.label('Student Name(s)')
.items(
Joi.object({
name: Joi.string(),
value: Joi.string()
}).required().messages('"Student" This field is required.')
),
}
You can return a custom error object using Error constructor like this:
var schema = Joi.object().keys({
firstName: Joi.string().min(4).max(8).required().error(new
Error('error message here for first name')),
lastName: Joi.string().min(5).max(1).required().error(new
Error('error message here for last name'))
});
Joi.validate(req.body, schema, function(err, value) {
if (err) {
console.log(err.message)
return catched(err.message);
}
});
The easiest way in my opinion would be this.
const Joi = require("#hapi/joi");
export const categorySchema = Joi.object({
mobile: Joi.string().trim().regex(/^[6-9]\d{9}$/).required().messages({
"string.base": `"" should be a type of string`,
"string.empty": `"" must contain value`,
"string.pattern.base": `"" must be 10 digit number`,
"any.required": `"" is a required field`
}),
password: Joi.string().trim().required().messages({
"string.base": `"" should be a type of 'text'`,
"string.pattern.base": `"" must be 10 digit number`,
"any.required": `"" is a required field`
}),
}).required();

Add error in existing errors while validating object through hapi/joi

const schema = Joi.object().keys({
Id: Joi.number().required(),
CustomerName: Joi.string()
.trim()
.required()
.when('$isInValidCustomer', {
is: true,
then: //Add some error in existing error block,
}),
BankName: Joi.string().trim(),
});
const custDetail = {
Id: 2,
CustomerName: 'xyz'
BankName: ''
};
const schemaOptions = {
abortEarly: false,
context: {
isInValidCustomer: true,
},
};
const valError = schema.validate(custDetail, schemaOptions);
So, now when I validate 'custDetail' object I want following 2 errors:
- CustomerName error because 'isInValidCustomer' is true
- BankName is required
I am not able to append error for CustomerName in existing error object. If I use '.error()' then just get single error corresponding to 'CustomerName' else just getting error for BankName.
Any help is really appreciated.
This can be achieved using custom function.
const schema = Joi.object().keys({
Id: Joi.number().required(),
CustomerName: Joi.string()
.trim()
.required()
.when('$isInValidCustomer', {
is: true,
then: Joi.any().custom(() => {
throw new Error('Invalid Customer');
}),
}),
BankName: Joi.string().trim(),
});

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

Categories

Resources