I have following validation schema, and sample data.
var schema = Joi.alternatives().try(
Joi.object().keys({
searchTerm: Joi.string().trim().min(3).label('Search Term').options({ language: { any: { empty: 'should not be empty' } } }),
location: Joi.string().allow(''),
searchType: Joi.string().valid('people')
}),
Joi.object().keys({
searchTerm: Joi.string().allow(''),
location: Joi.string().trim().min(3).label('Location').options({ language: { any: { empty: 'should not be empty' } } }),
searchType: Joi.string().valid('people')
})
);
Sample data is:
{searchTerm: "", searchType: "people", location: ""}
Should not pass and show a message Please enter either search term or location. Make sure it contains 3 characters at least
{searchTerm: "as", searchType: "people", location: ""}
Should not pass and show a message Search term must contain 3 characters at least
{searchTerm: "test", searchType: "people", location: ""} // Should pass
My validation schema shows both message in failure situations
You can simplify your joi schema to this
const schema = Joi.object().keys({
searchTerm: Joi.string().trim().min(3),
location: Joi.string().allow(''),
searchType: Joi.string().valid('people'),
}).or('searchTerm', 'location').error(new Error('Please enter either search term or location. Make sure it contains 3 characters at least'));
However this will not meet your second condition(Search term must contain 3 characters at least) as there can be only one error message.
Related
A user will be sending contact information as an array of objects like the following, with the phone number being optional and the email being required:
{
"contact": [{
"type": "phone",
"value": "555-555-5555"
}, {
"type": "email",
"value": "test#test.com"
}]
}
I would like to ensure there is an email object inside the array. I tried a Joi validation like this:
contact: Joi.array().items(Joi.object().keys({
type: Joi.string().valid('phone', 'email'),
value: Joi.string()
.when('contact.type', { is: 'phone', then: Joi.string() })
.when('contact.type', { is: 'email', then: Joi.string().email().required() })
}))
.when('contact.type', { is: 'phone', then: Joi.array().min(2).required() }),
But I get the following error:
Error: Item cannot come after itself: contact
It seems it doesn't like me giving it a length in this manner but I can't figure out any other way to do it. Any help would be appreciated. Thanks.
This schema combining .when, .has, and .unique, should work:
Joi.object({
contact: Joi.array().items(
Joi.object().keys({
type: Joi.string().valid('phone', 'email').required(),
value: Joi.string().when('type', { is: 'email', then: Joi.required() })
}).required(),
)
.has(Joi.object({ type: 'email', value: Joi.exist() }))
.unique('type').min(1).max(2)
})
Let's see the rules:
the object with the type 'email' must exist;
That's why I've added
.has(Joi.object({ type: 'email', value: Joi.exist() }))
This means that the array must have at least one of these elements.
We don't want duplicates, right?
.unique('type').min(1).max(2)
The array will have either 1 element, or 2, with different types.
When the type is 'email', the value should be required, and optional otherwise. That's what we are saying here:
value: Joi.string().when('type', { is: 'email', then: Joi.required() })
Thank you #soltex for your answer.
First I'm not sure we should exclude duplicates. Some people have multiple phone numbers.
Second, your answer did not quite work. Here is my updated answer based on what you wrote:
contact: Joi.array().items(Joi.object().keys({
type: Joi.string().valid('phone', 'email').required(),
value: Joi.string().required(),
}))
.has(Joi.object({ type: 'email', value: Joi.string().email() }))
Once I include the .has method then there is no reason to include the .when method. I also want the email to be a valid email not just that it exists. I changed value to required because if the user sends type phone I still want them to include the value.
Once again thanks for your guidance.
I recently migrated to new joi repo (#hapi/joi => joi)
Now I am getting error when running server
throw new AssertError([result.error.details[0].message]);
^
Error: "language" is not allowed
I searched google and SO , but cant find solution
This is my code :
forgetUser: {
query: {
email: Joi.string().regex(/^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
.required()
.options({ language: { string: { regex: { base: 'must be valid' } } } })
.label('Email')
}
},
Please help me?
This is caused when you are passing an unknown field to a Joi schema.
for example, you have this Joi schema:
Joi.object({
name: Joi.string().required(),
email: Joi.string().required(),
});
and you pass this object to validate:
{
name: "John Doe",
email: "johndoe#gmail.com",
language: "en"
}
The validation will throw an error / failed because language is not allowed inside the schema.
To prevent this, you can pass stripUnknown options on the Joi Schema so it will strip unknown fields
Joi.object({
name: Joi.string().required(),
email: Joi.string().required(),
}).options({ stripUnknown: true });
or you can pass allowUnknown so it will ignore unknown fields
Joi.object({
name: Joi.string().required(),
email: Joi.string().required(),
}).options({ allowUnknown: true });
You can read more about validation options here
It is not clear from the question what you are trying to achieve (maybe add more details?).
If you are trying to validate email, there is already a built-in function to do so: string.email().
If you still want to do a custom regex matching, there is an built-in function for this too: string.pattern().
If you want to replace the built-in error messages to custom error messages, Joi provides that by using .messages(). Check the answer here: https://stackoverflow.com/a/58234246/1499476.
Basically, you can do something like:
forgetUser: {
query: {
email: Joi.string().pattern(/^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
.required()
.label('Email')
.messages({
'string.pattern.base': '{#label} must be valid', // --> Email must be valid.
'string.base': '{#label} should be a type of "text"',
... // other customisations
})
}
},
string.pattern() can also be used as alias string.regex() (as you are already doing).
I'm trying to validate a query string using JOI and express-validation.
I need the query param ?query[primaryOrgId]=5d2f2c836aeed10026ccba11 to be either a single string or an array of strings, and it is required.
The following schema is validating the primaryOrgId as expected when it is present, but it is not validating that it is required:
index: {
body: {},
query: {
query: {
primaryOrgId: Joi.alternatives().try(
Joi.array().items(Joi.string().regex(mongoId)),
Joi.string().regex(mongoId),
).required()
},
},
options: {
allowUnknownQuery: false,
allowUnknownBody: false,
},
},
I've also tried:
index: {
body: {},
query: {
query: {
primaryOrgId: Joi.alternatives().try(
Joi.array().items(Joi.string().regex(mongoId).required()),
Joi.string().regex(mongoId).required(),
)
},
},
options: {
allowUnknownQuery: false,
allowUnknownBody: false,
},
},
}
How can I ensure that primaryOrgId is present in the query string?
I am not 100% sure about your requirements, but here is a Joi ("#hapi/joi": "^17.1.1") schema with a few changes:
const schema = Joi.alternatives().try(
Joi.array().min(1).items(Joi.string().trim()), // length have to be at least 1
Joi.string().trim()
).required(); // required added
// String
console.log(schema.validate(undefined)); // error: [Error [ValidationError]: "value" is required]
console.log(schema.validate('')); // error: [Error [ValidationError]: "value" is not allowed to be empty]
console.log(schema.validate(' ')); // error: [Error [ValidationError]: "value" is not allowed to be empty]
console.log(schema.validate('foo')); // value: 'foo'
// Array
console.log(schema.validate([])); // error: [Error [ValidationError]: "value" must contain at least 1 items]
console.log(schema.validate([' '])); // error: [Error [ValidationError]: "[0]" is not allowed to be empty]
console.log(schema.validate(['foo'])); // value: [ 'foo' ]
console.log(schema.validate(['foo', 'bar'])); // value: [ 'foo', 'bar' ]
Let me know if this works well for you. Otherwise I will update my answer.
I am trying to insert array of objects inside array of objects in my mongoDB schema. This is how i want my schema to appear.
const CourseSchema = mongoose.Schema({
categoryname: {
type: String,
required: "Course Category",
min: 3,
max: 100
},
coursename: {
type: String,
required: "Course Name",
min: 3,
max: 100
},
levels:
[
{
levelid: Number,
levelname: String,
chapter:
[
{
chapternumber: Number,
chaptername: String,
content: String //To be elaborated
}
]
}
]
});
My API which i have written looks like this:
exports.addcourse = (req, res) => {
let levels = [];
levels.push({
levelid: req.body.levelid,
levelname: req.body.levelname,
chapter: [
{
chapternumber: req.body.chapternumber,
chaptername: req.body.chaptername,
content: req.body.content
}
]
})
const newCourse = new Course({
coursename: req.body.coursename,
categoryname: req.body.categoryname,
levels: levels
});
newCourse.save(function (error) {
if (error) res.json({ message: 'could not add course because ' + error });
res.json({ newCourse: newCourse });
});
}
This works fine when i enter one level and one chapter, but gives an error when i enter multiple data.
I am giving input from postman 'x-www'form-urlencoded'.
please help.
The error i get when i add one more levelid and levelname in postman
{
"message": "could not add course because ValidationError: levels.0.levelid: Cast to Number failed for value \"[ '1', '2' ]\" at path \"levelid\", levels.0.levelname: Cast to String failed for value \"[ 'First Level', 'Second Level' ]\" at path \"levelname\""
}
The data i am trying to enter
In postman, when you are sending the same key levelid twice, it converts it to array containing both the values. Like in your case, in req.body.levelid, you will receive [1, 2]. But in your schema, levelid is expecting a number value. Also, you are reading it wrong in your API code. Instead of getting levelid and chapter seperately, you can get the levels array in request body with values levelid, levelname and chapter. Similarly, chapter can be an array of objects. req.body.levels will look like this:
[{
levelid: 1,
levelname: "level1",
chapter: [{
chapternumber: 1,
chaptername: "chapter1",
content: "chapter1-content"
}, {
chapternumber: 2,
chaptername: "chapter2",
content: "chapter2-content"
}]
}]
Try this in postman by giving input in raw
Planning to use AJV
for validating user inputs. AJV needs data model JSON Schema to validate user inputs. So, we need to derive JSON Schema from Sequelize model. Is there a way to get JSON schema from Sequelize model programatically?
A late answer, but I ended up creating sequelize-to-json-schema to solve this for our needs.
It offers more customisation in terms of which attributes you include in your schema and adding virtual attributes that might be used by your create method or similar.
Example
// assuming you have a user model with the properties
// name (string) and status (enum: real, imagined)
const schemaFactory = require('sequelize-to-json-schema');
const factory = new SchemaFactory({
customSchema: {
user: {
name: { description: "The user's name" },
status: { description: 'Was it all just a dream?' },
},
}
hrefBase: 'http://schema.example',
});
const schemaGenerator = factory.getSchemaGenerator(User);
const schema = schemaGenerator.getSchema();
// Results in
schema = {
{
title: 'User',
'$id': 'http://schema.example/user.json',
type: 'object',
'$schema': 'http://json-schema.org/draft-06/schema#',
properties: {
name: {
'$id': '/properties/fullname',
type: 'string',
examples: [],
title: 'Name',
description: "The user's name",
},
status: {
'$id': '/properties/status',
type: 'string',
examples: ['REAL', 'IMAGINED'],
enum: ['REAL', 'IMAGINED'],
title: 'Status',
description: 'Was it all just a dream?'
}
}
}
}
Note: sequelize-to-json-schema generates draft-06 schemas, to use that with AJV, their README says you'll need to do:
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));