Custom validation of mongoose's Schema params - validation

have the next params of object:
var mongoose = require (PATH);
var Schema = mongoose.Schema;
var schema - new Schema ({
barcode:{
type:number,
required:true,
unique:true
},...});
i want to validate 'barcode', which will be no less and no more than 14 characters;
for this i write the code below:
schema.path('barcode').validate(function(barcode){
return barcode.length == 13;
}, 'sorry, the error occurred, be careful while typing, 14 characters only!");
exports.Item = mongoose.model('Item', schema);
But when i implement this schema to the concrete object, this validation does not play any role. I mean, that i can type any length of number and no any error occur!

Checkout mongoose-validator. It integrates with mongoose to support custom validation. You can use it something like this.
var validate = require('mongoose-validator').validate;
var BarcodeSchema = new Schema({
code: {
type: String,
required: true,
unique: true,
validate: validate('len', 13)
}
});

Related

JSON schema validation with perfect messages

I have divided the data entry in a REST call in 4 parts. Data can be sent to REST call via:-
headers
query params
path params
request body
So in order to validate the presence of any key in any of the above 4 parts I have created a schema in this format. So if in case I have to validate anything in query params I will add the key 'query' and then add the fields inside that, that needs to be validated
const schema = {
id: 'Users_login_post',
type: 'object',
additionalProperties: false,
properties: {
headers: {
type: 'object',
additionalProperties: false,
properties: {
Authorization: {
type: 'string',
minLength: 10,
description: 'Bearer token of the user.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length',
required: 'should have Authorization'
}
}
},
required: ['Authorization']
},
path: {
type: 'object',
additionalProperties: false,
properties: {
orgId: {
type: 'string',
minLength: 23,
maxLength: 36,
description: 'OrgId Id of the Organization.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length', // ---> B
maxLength: 'should not be more than 36 length',
required: 'should have OrgId'
}
}
},
required: ['orgId']
}
}
};
Now, in my express code, I created a request object so that I can test the validity of the JSON in this format.
router.get("/org/:orgId/abc", function(req, res){
var request = { //---> A
path: {
orgId : req.params.orgId
},
headers: {
Authorization : req.headers.Authorization
}
}
const Ajv = require('ajv');
const ajv = new Ajv({
allErrors: true,
});
let result = ajv.validate(schema, request);
console.log(ajv.errorsText());
});
And I validate the above request object (at A) against my schema using AjV.
The output what I get looks something like this:
data/headers should have required property 'Authorization', data/params/orgId should NOT be shorter than 23 characters
Now I have a list of concerns:
why the message is showing data word in the data/headers and data/params/orgId even when my variable name is request(at A)
Also why not my errormessages are used, like in case of orgId I mentioned: should be atleast of 23 length (at B) as a message, even then the message came should NOT be shorter than 23 characters.
How can I show request/headers instead of data/headers.
Also, the way I used to validate my path params, query params, header params, body param, is this the correct way, if it is not, then what can be the better way of doing the same?
Please shed some light.
Thanks in advance.
Use ajv-keywords
import Ajv from 'ajv';
import AjvKeywords from 'ajv-keywords';
// ajv-errors needed for errorMessage
import AjvErrors from 'ajv-errors';
const ajv = new Ajv.default({ allErrors: true });
AjvKeywords(ajv, "regexp");
AjvErrors(ajv);
// modification of regex by requiring Z https://www.regextester.com/97766
const ISO8601UTCRegex = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?Z$/;
const typeISO8601UTC = {
"type": "string",
"regexp": ISO8601UTCRegex.toString(),
"errorMessage": "must be string of format 1970-01-01T00:00:00Z. Got ${0}",
};
const schema = {
type: "object",
properties: {
foo: { type: "number", minimum: 0 },
timestamp: typeISO8601UTC,
},
required: ["foo", "timestamp"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { foo: 1, timestamp: "2020-01-11T20:28:00" }
if (validate(data)) {
console.log(JSON.stringify(data, null, 2));
} else {
console.log(JSON.stringify(validate.errors, null, 2));
}
https://github.com/rofrol/ajv-regexp-errormessage-example
AJV cannot know the name of the variable you passed to the validate function.
However you should be able to work out from the errors array which paths have failed (and why) and construct your messages from there.
See https://ajv.js.org/#validation-errors
To use custom error messages in your schema, you need an AJV plugin: ajv-errors.
See https://github.com/epoberezkin/ajv-errors

mongoose populate different database

I have two databases in my program. One for saving users and roles and one for the rest.
1) my model User has a reference to Roles. So I can populate roles by doing:
User.find({}).populate('local.roles').sort({ email: 1 }).exec(function (err, users) { ...
This works perfect and I can get a user and his roles.
2) When I try to do the same, but with models that have a connection to another database, I get the infamous error:
"MissingSchemaError: schema hasn't been registered for model ..."
This is the way I code my models when they use a different connection:
var mongoose = require('mongoose');
// to define another mongoose connection
var configScrwebDB = require('./../../config/scrwebDatabase.js');
var scrwebDBConnection = mongoose.createConnection(configScrwebDB.url);
var aseguradoSchema = mongoose.Schema({
nombre: { type: String },
abreviatura: { type: String },
rif: { type: String },
direccion: { type: String }
});
module.exports = scrwebDBConnection.model('Asegurado', aseguradoSchema);
3) this is what I do to 'populate' some field in my query (and the one that fails with above error):
var query = Riesgo.find({ cia: filtroObject.ciaSeleccionada });
query.populate('asegurado');
query.sort("codigo");
query.select("codigo fechaInicio estado moneda");
query.exec(function (err, riesgos) { ...
Of course this is in another 'js' file; and I do my 'require', etc., in order to import my models; etc.
As I said before, I can populate when models use 'default' mongoose connection.
Any ideas how I should correct this will be appreciated ... Am I missing some obvious thing here?
Thanks and bye ...
I had the same error. You could use:
var query = Riesgo.find({ cia: filtroObject.ciaSeleccionada });
query.populate('asegurado','fields to retrieve',User);// User need to be defined in the file as an variable that contents the User model defined.
query.sort("codigo");
query.select("codigo fechaInicio estado moneda");
Refer link

Mongoose conditional required validation

I'm using mongoose and trying to set a custom validation that tells the property shall be required (ie. not empty) if another property value is set to something. I'm using the code below:
thing: {
type: String,
validate: [
function validator(val) {
return this.type === 'other' && val === '';
}, '{PATH} is required'
]}
If I save a model with {"type":"other", "thing":""} it fails correctly.
If I save a model with {"type":"other", "thing": undefined} or {"type":"other", "thing": null} or {"type":"other"} the validate function is never executed, and "invalid" data is written to the DB.
As of mongoose 3.9.1, you can pass a function to the required parameter in the schema definition. That resolves this problem.
See also the conversation at mongoose: https://github.com/Automattic/mongoose/issues/941
For whatever reason, the Mongoose designers decided that custom validations should not be considered if the value for a field is null, making conditional required validations inconvenient. The easiest way I found to get around this was to use a highly unique default value that I consider to be "like null".
var LIKE_NULL = '13d2aeca-54e8-4d37-9127-6459331ed76d';
var conditionalRequire = {
validator: function (value) {
return this.type === 'other' && val === LIKE_NULL;
},
msg: 'Some message',
};
var Model = mongoose.Schema({
type: { type: String },
someField: { type: String, default: LIKE_NULL, validate: conditionalRequire },
});
// Under no condition should the "like null" value actually get persisted
Model.pre("save", function (next) {
if (this.someField == LIKE_NULL) this.someField = null;
next()
});
A complete hack, but it has worked for me so far.
Try adding this validation to the type attribute, then adjust your validation accordingly. E.g.:
function validator(val) {
val === 'other' && this.thing === '';
}
thing: {
type: String,
required: function()[{
return this.type === 'other';
}, 'YOUR CUSTOM ERROR MSG HERE']
}

Validate single fields of a backbone form

I would like to be able to validate single fields at a time in my forms using backbone forms and backbone validation, however I am having problems getting this to work if I put my requirements in the model validation, rather than the schema.
My model is:
class Event extends Backbone.Model
url: ->
'/events' + (if #isNew() then '' else '/' + #id)
validation:
title:
required: true
start_date:
required: true
end_date:
required: true
schema: ->
title:
type: "Text"
start_date:
type: "DateTime"
title: "Start Date"
DateEditor: "DatePicker"
end_date:
type: "DateTime"
title: "End Date"
DateEditor: "DatePicker"
The code in my View that uses these is
class Events extends Backbone.View
...
initialize: ->
#form = new Backbone.Form(model: #model).render()
Backbone.Validation.bind #form;
validateField: (field) ->
#form.on "#{field}:change", (form, fieldEditor) =>
#form.fields[field].validate()
render: ->
...
#validateField for field of #form.fields
...
However, my problem is that it only seems to validate if I move the required: true into the schema like so:
schema: ->
title:
type: "Text"
validators: ["required"]
However I'd rather not do this since, backbone.validation has a broader range of built-in validators which I would like to make use of outside this example.
I've noticed that backbone-forms states
There are 2 levels of validation: schema validators and the regular built-in Backbone model validation. Backbone Forms will run both when either form.commit() or form.validate() are called.
However I'm not sure it's running the regular validation when I validate the individual fields? The reasoning behind wanting to do this is that when a user starts creating an event I do not want to validate fields that they are yet to fill in.
Is there any way I can still validate individual fields without having to move the validation into the schema?
Thanks in advance.
======= UPDATE =======
On looking at the form editors source code for individual fields, the validate function is as so:
validate: function() {
var $el = this.$el,
error = null,
value = this.getValue(),
formValues = this.form ? this.form.getValue() : {},
validators = this.validators,
getValidator = Form.helpers.getValidator;
if (validators) {
//Run through validators until an error is found
_.every(validators, function(validator) {
error = getValidator(validator)(value, formValues);
return error ? false : true;
});
}
return error;
},
It does not appear to use the model validation, so I wonder how I can incorporate this?

Node.JS / Mongoose / Express -> Object has no method "findAll"

I'm trying to pass a method from my model.js to my route.js.. And my route doesn't find any method ! I searched a solution and tested a lot of codes, without success.
I'm a beginner in Node so sorry if it's a stupid error.
This is a part of my code :
Route.js
var mongoose = require('mongoose');
var membersModel = new require('../models/MemberModel');
// Member list page
exports.list = function(req, res){
membersModel.findAll(function(err, docs){
res.render('list.jade', { title: 'My Registration App - Member list', member: docs });
});
};
MemberModel.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
// Open DB connection
var MemberSchema = new Schema({
id : ObjectId,
title : { type: String, required: true, enum: ['Mr', 'Mrs', 'Mme', 'Miss'] },
lastname : { type: String, required: true, uppercase: true, trim: true},
firstname : { type: String, required: true},
mail : { type: String, trim: true, index: { unique: true, sparse: true } },
date : Date
});
// ...
MemberSchema.method.findAll = function (callback) {
Members.find(function (err, member) {
callback(null, member)
});
};
var conn = mongoose.createConnection('mongodb://localhost/members');
var MyModel = conn.model('Members', MemberSchema);
var instanceMember = new MyModel;
module.exports = instanceMember;
Thanks for the time passed helping me.
If you want other informations, tell me !
I think you have a few problems here:
Your schema's instance methods should be defined on MemberSchema.methods (not .method).
A method like findAll that returns instances should be defined as a static method of the schema (on MemberSchema.statics) instead of an instance method.
You should be exporting MyModel, not a new MyModel instance of it as you are now. module.exports = MyModel;
route.js should not be using new in its require as you want the MyModel class to be available to the file, not an instance of it.
for the find function the syntax usually is
<modelname>.find({conditions},function(err, results){
//use the results
});
and if u don't have any specific conditions and want to find all, you can just give empty object in the conditions {}
<modelname>.find({},function(err, results){
//use the results
});
and for the schema, an example
const articleSchema = {
title: String,
content: String
};
// to create a new model named Article
const Article = mongoose.model("article",articleSchema);

Resources