Joi array validation ignoring nested keys required - javascript

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

How to use Joi alternatives conditional for schema validation?

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

Mongoose schema with nested object

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

How to validate nested object whose keys should match with outer objects another key whose value is array using Joi?

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.

Validating unique key pairs in a nested object with Joi and nodeJS

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)

spread operator form submission in express.js

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!

Categories