Joi - Required based on value on other key - node.js

I want to validate an input which only has two fields(namely text and image). Both text and image are string and one of them must always be present. When one of the fields is absent then the other one can not be an empty string.
this is the Validation I have defined.
text: Joi.string()
.when('image',
{
is: Joi.string(),
then: Joi.string().allow(''),
otherwise: Joi.string().required(),
}
),
image: Joi.string().allow(null),
when I use the below input, the validation allows the data to pass. I dont how to change the validation to prohibit the below input.
post: {
text: ''
}

Use exist() instead of string() when validating:
when('image', {
is: Joi.exist(),
then: Joi.string().allow(''),
otherwise: Joi.string().required(),
})

body:Joi.object({
notifiyto:Joi.string().valid('selectedCustomer','selectedDrivers','allCustomers','allDrivers').required(),
notifiyIds:Joi.when('notifiyto',{
is: Joi.exist().valid('selectedCustomer', 'selectedDrivers'),
then: Joi.required(),
}),
message:Joi.string()
})

Related

Enums for JOI scheme validation

I have a JOI validation scheme which some values which I need in some other scheme's and other places too, so I placed all those values in a seperated file and import them in the scheme.
Scheme:
const ENUMS = require(path.join(__dirname, 'enums'))
const Product = Joi.object({
name: Joi.string(),
category: Joi.string().valid(ENUMS.validProductCategory),
description: Joi.string().allow(ENUMS.empty)
})
Enums.js
module.exports.productCategory = ['JEWEL', 'WATCH', 'EARRING']
module.exports.empty = '', null
The problem is that Joi.valid() and Joi.allow() does not accept arrays as values (see https://joi.dev/api/?v=17.5.0#anyallowvalues). The values in the function needs to passed as .valid('a', 'b')
I don't know how the let that values return from the enums file. I have tried some ways (like the module.exports.empty) and with the spreading (...) options.
I don't get it to work either, so I'm wondering how I can return the string in the right format to reuse it?
if you have an enum e.g
enum ValidStringsEnum {
DESCRIPTION_ONE = "description one",
DESCRIPTION_TWO = "description two",
}
you could do:
description: Joi.string().valid(...Object.values(ValidStringsEnum))

Sequelize setter error 'invalid input syntax for integer: "[object Object]"'

I am trying to associate a list of contacts to a customer using the associations setter method, however, it always throws the error 'invalid input syntax for integer: "[object Object]"'.
The relevant query mentioned in the error is: UPDATE "contactperson" SET "refCustomerId"=$1,"updatedAt"=$2 WHERE "id" IN ('[object Object]')
This is how I use the setter:
db.customer.findByPk(customerID, {
include: [{
model: db.address,
as: 'address',
},{
model: db.contactoption,
as: 'contactOptions',
}, {
model: db.contactperson,
as: 'contactPersons',
}]
}).then(customer => {
customer.setContactPersons([ { firstName: 'tester', lastName: 'tester', description: 'lorem ipsum' } ]);
});
This is the association:
Customer.hasMany(models.contactperson, {
foreignKey: 'refCustomerId',
as: 'contactPersons'
});
Any idea what I'm doing wrong?
I managed to resolve this issue using the following code:
db.contactperson.bulkCreate([ { firstName: 'tester', lastName: 'tester', description: 'lorem ipsum' } ]).then(newContactPersons => {
customer.setContactPersons(newContactPersons);
});
It's a more complicated approach than intended, but it get's the job done.
You used set<ModelName>s that just updates a link field of given records. If you need to create contactperson record you need to use createContactPerson instead of setContactPersons (NOTE: you cannot create several records at once).
customer.createContactPerson({
firstName: 'tester',
lastName: 'tester',
description: 'lorem ipsum'
});
compare to:
const contactPerson = db.contactperson.findById(1);
if (contactPerson) {
customer.addContactPersons([contactPerson.id]);
}
set<ModelName>s - replaces old linked records with the new existing ones
add<ModelName>s - adds exisiting records in addition to old ones
create<ModelName> - create a new non-existing record in addition to old ones
See hasMany special methods
Exactly what Anatoly posted.
I had method declared on TypeScript like:
declare addPost: HasManyCreateAssociationMixin<PostClass, 'userId'>;
When i changed to:
declare createPost: HasManyCreateAssociationMixin<PostClass, 'userId'>;
Everything works so remember - how you describe name of method its very necesary.

Use validation error values on custom Joi/celebrate messages

I'm creating something using a nodejs/typescript stack for the server, and I'm trying to define custom generic error messages instead of per-field messages. Something like this:
routes.post(
'/points',
upload.single('image'),
celebrate({
body: Joi.object().keys({
name: Joi.string().required(),
email: Joi.string().required().email(),
whatsapp: Joi.number().required(),
latitude: Joi.number().not(0).required(),
longitude: Joi.number().not(0).required(),
city: Joi.string().required(),
uf: Joi.string().required().max(2),
items: Joi.string().required()
}),
}, {
abortEarly: false,
messages: {
'string.empty':'{context.label} cant be empty!'
}
}),
pointsController.create
);
As you can see, I'm trying to use a variable/value inside the custom message. I got that 'key' based on the error entry that comes out of celebrate/joi error, which is like this:
{
message: ' cant be empty!',
path: [ 'items' ],
type: 'string.empty',
context: { label: 'items', value: '', key: 'items' }
}
If there a way to do something like that?
The message is not 'parsing' the {context.label} as I though it would. I mean, this is a shot in the dark since I couldn't find anywhere if something like this is suported at all.
You can use {#label} to achieve what you want to.
Try:
.messages({
'string.empty': '{#label} cant be empty!',
'any.required': '{#label} is a required field for this operation'
})
and so on for all other types.
Other values are also accessible similarly. For ex, if you want to generalise the error message for string min/max:
.messages({
'string.min': '{#label} should have a minimum length of {#limit}'
})
Here, limit (min) was set when you created the schema for the string.

How to add extra properties to error returned from express-validator

When I use the express-validator and validate fields, I always get the error response with value, msg, param and location properties. What if I want to add extra properties in there like say code to communicate it to the client.
For ex:
When I do
check('name').not().isEmpty().withMessage('Name must have more than 5 characters')
I get
{ value: undefined, msg: 'Name must have more than 5 characters', param 'name', location: 'body'}
// What I am looking to achieve is this
{ value: undefined, msg: 'Name must have more than 5 characters', code: 'NAME_MANDATORY' param 'name', location: 'body'}
How do I achive this with express-validator?
you can try to give the withMessage a function like this
withMessage((value, { req, location, path }) => {
return { value, location, path, otherkey:'some message' };
})

How to validate different data type using same key in joi validation

This is my Scenario. I want validate same key but different datatypes passing in joi validation. How to achieve this:
joi validate
static validateSearchedProduct(request_query) {
const joiSearchedProductSchema = Joi.object({
product_category: Joi.objectId()
})
return Joi.validate(request_query, joiSearchedProductSchema);
}
function
validateSearchedProduct({product_category:"5d44258bcb9b611da1f658c8"})
validateSearchedProduct({product_category:["5d44258bcb9b611da1f658c8"]})
So, you want product_category to be either string or array of strings. Then, you could use Joi.alternatives and do something like that:
static validateSearchedProduct(request_query) {
const joiSearchedProductSchema = Joi.object({
product_category: Joi.alternatives().try(
Joi.string(),
Joi.array().items(Joi.string())
)
})
return Joi.validate(request_query, joiSearchedProductSchema);
}
And Joi.string() can be replaced with whatever that is needed (e.g. Joi.objectId).

Resources