Joi array required validation not working. I have an array userData which contain objects, keys dateMilli and value. I put required everywhere But if I pass an empty array of userData []. It is not throwing any error.
Joi Version is "#hapi/joi": "^16.1.8", struggling with Joi docs but nothing works.
userId is "123" and userData is []
const data = { userId, userData };
const schema = Joi.object({
userId: Joi.string().required(),
userData: Joi.array().items({
dateMilli: Joi.number().required(),
value: Joi.string().valid("YES", "NO").required()
}).required(),
});
let validate = schema.validate(data);
if (!validate || validate.hasOwnProperty("error")) {
return res.send("Invalid parameters");
}
You need to use .min with array to disallow empty arrays.
try this
const data = {"userId": "123", "userData": []};
const Joi = require("#hapi/joi");
const schema = Joi.object({
"userId": Joi.string().required(),
"userData": Joi.array().items({
"dateMilli": Joi.number().required(),
"value": Joi.string().valid("YES", "NO").required()
}).min(1).required()
});
const validate = schema.validate(data);
console.log(validate);
Related
I have a use case where a schema field is mandatory depending on the value of another field,
eg. If the schema has 2 fields, name and addr,
if the value of name field is "test" only then addr field is required.
I am using Joi for object validation,
Following is my sample code -
const Joi = require('joi');
let test = async() => {
const schema = Joi.object({
name: Joi.string().required(),
addr: Joi.alternatives().conditional('name', {is: 'test', then: Joi.string().required()})
});
const request = {
name: "test"
}
// schema options
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true // remove unknown props
};
// validate request body against schema
const validationResponse = await schema.validate(request, options);
console.log("validationResponse => ", validationResponse);
return true;
};
test();
current output -
validationResponse => { value: { name: 'test' } }
what I'm expecting is validationResponse to have error message that addr field is missing.
I tried to refer -
https://www.npmjs.com/package/joi
https://joi.dev/api/?v=17.4.2#alternativesconditionalcondition-options
Do you really need Joi.alternatives? Why don't you use Joi.when instead?
Joi.object({
name: Joi.string().required(),
addr: Joi.string().when('name', { is: 'test', then: Joi.required() })
})
I'm trying to create a document in my Schema that essentially works like a dictionary with an array of values (dates):
I want to use this to keep track of email correspondence I am sending users:
let d = {
'generic-contact' = ['11/02/2019', '11/05/2020'],
'update-profile' = ['1/01/2018']
}
I would then update this document field with something like:
let emailAddresses = ['joebloggs#yahoo.com', 'foobar#googlemail.com']
let recipients = await Profile.find({ email: { "$in" : emailAddresses } })
let emailTopic = 'newsletter03'
recipients.forEach(user => {
user.correspondenceHistory[emailTopic].push(Date.now())
user.save()
})
I want to do this so that I make sure that I don't send the same user the same email within a certain period of time.
However I can't work out how to set this up in my schema. Is this sort of structure possible?
I've tried many different structures for correspondenceHistory, but none of them have worked and I can't work out what I'm doing wrong. Here is my schema as it stands:
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
var profileSchema = new mongoose.Schema({
email: String,
firstname: String,
lastname: String,
correspondenceHistory: { type: Array } ### Here ###
}, { discriminatorKey: 'accountType', retainKeyOrder: true, timestamps: true });
I have object which I want to validate.
// valid object because all values of keys are present in details object
var object = {
details: {
key1: 'stringValue1',
key2: 'stringValue2',
key3: 'stringValue3'
},
keys: ['key1', 'key2', 'key3']
}
// invalid object as key5 is not present in details
var object = {
details: {
key4: 'stringValue4'
},
keys: ['key4', 'key5']
}
// invalid object as key5 is not present and key8 should not exist in details
var object = {
details: {
key4: 'stringValue4',
key8: 'stringValue8',
},
keys: ['key4', 'key5']
}
All the keys present in keys should be present in details also.
I tried this using Joi.ref()
var schema = Joi.object({
details: Joi.object().keys(Object.assign({}, ...Object.entries({...Joi.ref('keys')}).map(([a,b]) => ({ [b]: Joi.string() })))),
keys: Joi.array()
})
But this is not working because Joi.ref('keys') will get resolved at validation time.
How can I validate this object using Joi?
Using object.pattern and array.length
var schema = Joi.object({
details: Joi.object().pattern(Joi.in('keys'), Joi.string()),
keys: Joi.array().length(Joi.ref('details', {
adjust: (value) => Object.keys(value).length
}))
});
stackblitz
You can validate the array(if you want) then make a dynamic schema and validate that.
const arrSchema = Joi.object({
keys: Joi.array()
});
then,
const newSchema = Joi.object({
details: Joi.object().keys(data.keys.reduce((p, k) => {
p[k] = Joi.string().required();
return p;
},{})),
keys: Joi.array()
})
This should probably do it.
You have to set allowUnknown: true in validate() option.
I have the following JSON structure:
{
key1: "value1",
key2: "value2",
transactions: [
{
receiverId: '12341',
senderId: '51634',
someOtherKey: 'value'
},
{
receiverId: '97561',
senderId: '46510',
someOtherKey: 'value'
}
]
}
I'm trying to write some Joi code to validate that each object in the transactions array is unique i.e. a combination of receiverId and senderId is only ever present once. There can be a variable number of elements in the transactions array but there will always be at least 1.
Any thoughts?
You can use array.unique
const array_uniq_schema = Joi.array().unique((a, b) => a.receiverId === b.receiverId && a.senderId === b.senderId);
So for the whole object the schema would be (assuming all properties are required):
const schema = Joi.object({
key1: Joi.string().required(),
key2: Joi.string().required(),
transactions: array_uniq_schema.required(),
});
an easy way :
const schema = Joi.object({
transactions: Joi.array()
.unique('receiverId')
.unique('senderId')
.required(),
});
This way it returns an error for each field (one error for ReceivedId and another one for senderId)
Can spread operator solve below problem? Imagine I have more fields, then I have to declare req.body.something for every single fields, that's so tedious.
app.use((res,req,next) => {
const obj = {
name: req.body.name,
age: req.body.age,
gender: req.body.gender
}
//
User.saveUser(resp => res.json(resp)) //User model
})
You can use destructuring assignment:
const obj = req.body;
const { name, age, gender } = obj;
But, still you will have to validate it, and count all of them in your scheme.
Update:
Adding some validation example.
Assuming such schema in your route:
const tv4 = require('tv4');
const schema = {
type: 'object',
properties: {
name: 'string',
age: number,
gender: {
type: 'string',
pattern: /f|m/i
}
},
required: ['name']
};
And then, in your handler you validate:
if (tv4.validate(req.body, schema) {
// continue your logic here
} else {
// return 400 status here
}
You can use lodash's pick():
_.pick(object, [paths])
Creates an object composed of the picked object properties.
Example code is:
const _ = require('lodash');
...
const obj = _.pick(req.body, ['name', 'age', 'gender']);
If gender does not exist in req.body, it would be ignored -- the result obj object won't have a gender field.
If all the req.body fields are needed, you can just assign req.body to obj:
const obj = req.body;
To validate req.body content, you can use lodash's .has():
_.has(object, path)
Checks if path is a direct property of object.
Example code would be:
_.has(req.body, ['name', 'age', 'gender']); // return true if all fields exist.
You can use destructuring assignment
const { name, age, gender } = req.body
or if you wanna use spread operation, you can use :
const obj = { ...req.body }
Hope it helps!