Mongoose model `this` is undefined in its own definition - javascript

I am trying to define a conditional required field for a mongoose model in the following way:
const userSchema = new mongoose.Schema({
email: {
type: String,
},
emailVerified: {
type: Boolean,
required: true
},
hash: {
type: String,
required: function() {
if(!this.emailVerified) return true
return false
}
},
oauth: {
type: Boolean,
required: true,
},
password: {
type: String,
required: function() {
if(this.oauth) return false
return true
}
}
})
export default mongoose.models.Users || mongoose.model('Users', userSchema)
My intention is that I only want the hash field to be required when emailVerified === false, and the password field to be required only when oauth === true.
The thing is that when I try to add a document in the Users collection I get the following error:
Error: Users validation failed: hash: Cannot read property 'emailVerified' of undefined, password: Cannot read property 'oauth' of undefined
By looking at the documentation I understand that I should be able to reference the model in its own definition.

Related

Node.js + Mongoose: How to use a virtual property to associate an ObjectID with the property?

I'm trying to access a MongoDB database using Node.js and Mongoose.
I created a virtual property in Schema called username. See the code that follows.
const mongoose = require("mongoose");
const User = require("../models/user");
const datatypes = ['temperature', 'humidity'];
const nodeSchema = new mongoose.Schema(
{
MACAddress: {
type: String,
required: true,
trim: true,
uppercase: true,
match: /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/,
},
alias: {
type: String,
trim: true,
},
coordinates: {
type: String,
required: false,
match: /^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/,
},
address: {
type: String,
required: false,
},
userID: {
type: mongoose.Types.ObjectId,
},
nodeType: {
type: String,
enum: ['router', 'node'],
default: 'node',
},
dataTypes: {
type: [String],
enum: datatypes,
required: true,
}
},
{
timestamps: true,
}
);
The virtual property is used to set the userID property. See the code that follows.
// virtual field
nodeSchema.virtual("username").set(async function (username) {
this.userID = await this.getUserID(username);
});
// methods
nodeSchema.methods = {
getUserID: function (username) {
if (!username) return null;
return User.find({username: username}).then(userDoc => userDoc[0]._id);
},
};
To add a new document to the database, I am using the following code.
const newNode = new Node(newNodeData);
newNode.save().then( (node) => {
console.log(node.userID)
}
)
The problem is this... Calling the User.find function returns a promise. Even using await (see previous code), newNode.save() saves the document in the database without the userID property.
If I change the code to the following snippet, which doesn't use promise, the userID property is saved in the database with no problem. However, this is not what I want.
// virtual field
nodeSchema.virtual("username").set(async function (username) {
let ObjectId = mongoose.Types.ObjectId;
this.userID = new ObjectId("6245e896afe465a25047302e");
});
How can I force newNode.save() to wait for the promise result before saving the document to the database?

findByIdAndUpdate not failing even when body contains params not in schema

Taken from This MERN tutorial...
I have a mongoose schema with 4 fields:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Todo = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: false
},
comments: {
type: String,
required: false
},
done: {
type: Boolean,
required: true
},
});
module.exports = mongoose.model('Todo', Todo);
I'm calling this update route:
todoRoutes.route('/update/:id').post(function(req, res) {
Todo.findByIdAndUpdate(req.params.id, req.body, function(err, todo) {
if (err)
res.status(400).send('Updating item failed: ' + err);
else
todo.save().then(todo => {
res.json('Item updated!');
}).catch(err => {
res.status(400).send("Update not possible: " + err);
});
});
});
with the following body:
{
"name": "bla"
}
and gets an "ok" status, and the document is updated as I wanted. However, running the same update with an extra field, also gets an "ok" status, though it should have failed IMO:
{
"name": "bla",
"unwanted_field": true
}
The field is not shown when I GET the DB, but it still returns without any error. Why?
Why update does not require the "required" fields, and accepts any updates?
enter link description here has a strict option, that can be used like this:
let Todo = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: false
},
comments: {
type: String,
required: false
},
done: {
type: Boolean,
required: true
},
},
{
strict: true // This is true by default. Changing this to "throw" will throw an error, as shown in the image attached.
}
);
I add here an image of an error I got using postman, trying to add bad data after adding the strict: "throw" option:

Why is my var being returned as undefined when adding .methods to it?

I'm attempting to add bycrypt to an authentication table but I'm getting an error that authTable is undefined when attempting to add the generateHash and validPassword methods. Is there something i'm not understanding about the authTables.methods portion?
The code works when I comment out the generateHash and validPassword portion which leads me to believe that .methods is the hang up.
var bcrypt = require("bcryptjs");
module.exports = function (sequelize, DataTypes) {
var authTable = sequelize.define("authTable", {
username: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [1, 30]
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [6, 20]
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isEmail: true,
len: [1]
}
}
});
authTable.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
};
authTable.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
return authTable;
}
I would expect this to go to the table within the database with an encrypted password.
The errors i'm receiving are:
TypeError: Cannot set property 'generateHash' of undefined.
TypeError: Cannot set property 'validPassword' of undefined
I'm getting an error that authTable is undefined
No, you don't. You get an error that authTable.methods is undefined.
define returns an instance of Model, and as you can see in the documentation of Model, it does not have a property named methods, ergo, authTable.methods will evaluate to undefined.

Check for not required property existing in mongoose model variable

So, I have this mongoose scheemas structure:
const execStatusScheema = new mongoose.Schema(...)
const notifyScheema = new mongoose.Schema({
...
sms: {
type: Boolean,
required: true
},
smsStatus: {
type: execStatusScheema
},
telegram: {
type: Boolean,
required: true
},
telegramStatus: {
type: execStatusScheema
},
voice: {
type: Boolean,
required: true
},
voiceStatus: {
type: execStatusScheema
}
})
const Notify = mongoose.model('Notify', notifyScheema)
module.exports.Notify = Notify
as you can see in Notify scheema smsStatus, voiceStatus and telegramStatus are not required. If sms is false, the smsStatus property is not assiged in my code, and for voice and telegram the same behavior. I want to check in Notify some of these propertys. I do the follow:
const uncomplitedNotifies = await Notify.find(...).select('smsStatus voiceStatus telegramStatus sms voice telegram')
uncomplitedNotifies.forEach(async notify => {
console.log(notify)
if ('telegramStatus' in notify) {
console.log('123')
}
...
Result is:
{ _id: 5ba07aaa1f9dbf732d6cbcdb,
priority: 5,
sms: true,
telegram: false,
voice: false,
smsStatus:
{ status: 'run',
_id: 5ba07aaa1f9dbf732d6cbcdc,
errMessage: 'Authentication failed',
statusDate: '2018-9-18 12:10:19' } }
123
Ok, I find one, and its ok, but my if statment is true even I have no this property in my object. I guess it check in scheema object, where its declared, but I want to check in my 'real' object I got by query.
I also try this checking case, but the same result:
if (typeof something === "undefined") {
alert("something is undefined");
}
How can I check this object correctly ?
The in operator checks the object's own properties and its prototype chain. The unset properties are in the prototype chain of your object, but not on the object's own properties:
const hasTelegramStatus = 'telegramStatus' in document; // true
const hasTelegramStatus = document.hasOwnProperty('telegramStatus') // false
One option is to convert the query into an object by doing document.toObject(), which will remove the prototype chain and only return the own properties.

Mongoose JS path default generating id

Take a look at the following schema:
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
index: true
},
activationId: {
type: mongoose.Schema.ObjectId
}
});
userSchema.path('activationId').default(function() {
return new mongoose.Schema.ObjectId.fromString('actvt');
});
It is about the "activationId", I want to generate an ID (unique code, objectID prefered since that is already built-in Mongoose) once a user gets created, so if I have the following code:
var newUser = new User({
fullName: 'Rick Richards'
});
newUser.save(function(error, data){
console.log(data.activationId); // Should give the activationId
});
But this solution gives me the following error:
TypeError: undefined is not a function
You can do this by defining a default property for the activationId field that's the ObjectId constructor function:
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
index: true
},
activationId: {
type: mongoose.Schema.ObjectId
default: mongoose.Types.ObjectId
}
});

Categories

Resources