SAILS JS 1.0 model callback lifecycle is not called - node.js

well, I'm trying to create a user with Sails 1.0.0-42, the problem is that the callback lifecycle of the user model is not called. I have tried in many ways and nothing is called. Thanks in advance.
this is the code of Action2 "signup":
module.exports = {
friendlyName: 'Create User',
description: 'user create action2',
inputs: {
name: {
description: 'user create',
type: 'string',
required: true
},
email: {
description: 'user create',
type: 'string',
required: true
},
password: {
description: 'user create',
type: 'string',
required: true
},
},
exits: {
notFound: {
description: 'ERRO create user.',
responseType: 'notFound'
}
},
fn: async function (inputs, exits) {
const {name, email, password} = inputs;
var user = await User.create({name, email, password}).fetch();
if(!user) return exits.notFound();
return exits.success(user);
}
};
this is the user model code
var bcrypt = require('bcrypt');
module.export = {
attributes: {
name: {
type: 'string',
required: true,
},
email: {
type: 'string',
required: true,
},
password: {
type: 'string',
minLength: 6,
required: true,
columnName: 'hashed_password'
},
},
// Lifecycle Callbacks
beforeCreate: function (values, cb) {
// Hash password
bcrypt.hash(values.password, 10, function(err, hash) {
if(err) return cb(err);
values.password = hash;
cb();
});
}
};
the user is created, but the password is not encrypted, and in the tests the beforeCreate is not called.

Your model file declares:
module.export
But it needs to be:
module.exports
That s makes a big difference!
The reason it works at all with your current code is that, by default, the built-in sails-disk database is schemaless, so even though your User.js file wasn't exporting anything, it still lets you create records with whatever fields you want.

Related

How can I get a `select: false` Schema objects value from the context of this in a schema.methods method?

tl;dr: If this isn't possible and User.findOne({username: this.username}).select('token') is the only way, then please let me know. Otherwise, I would like to know if there's another way to select select:false objects.
I have an email verification token that I only want to pull from my UserSchema object when an email verification endpoint is visited. (Other than that, I don't even necessarily want the object to exist in the database, but I don't know of a way to make a temporary value.) Inside of the context of this within a mongoose method, how can I select this value?
I also want to do the same thing for the password
Example:
const userSchema = new Schema({
username: {
//unique: true,
required: true,
type: String
},
password: {
required: true,
type: String,
select: false
},
email: {
required: true,
type: String,
unique: true
},
phone: {
required: true,
type: String
},
name: {
first: {
required: true,
type: String
},
last: {
required: true,
type: String
},
full: {
required: true,
type: String,
default: function() {
return `${this.name.first} ${this.name.last}`
}
},
},
email_verified: {
required: true,
type: Boolean,
default: false
},
token: {
type: String,
select: false
},
active: {
required: true,
type: Boolean,
default: true
},
created_date: {
required: true,
type: Date,
default: Date.now
},
updated_date: {
required: true,
type: Date,
default: Date.now,
}
},
{
timestamps: {
createdAt: 'created_date',
updatedAt: 'updated_date'
}
})
Methods in same file:
/* Code in question */
userSchema.methods.verify = async function(submittedToken, cb) {
try {
let user = this
/* I've tried this.select() and this.get() to no avail, seems like those only work with user.findOne() which I don't mind using, but would like to avoid if I can use this instead */
const token = user.token //undefined because of select:false
if(token != submittedToken) return cb({'Invalid token'})
return cb(null, user)
} catch (err) {
return cb(err, false);
}
}
userSchema.methods.authenticate = function(submittedPassword, cb) {
bcrypt.compare(submittedPassword, this.password, function(err, isMatch) {
if (err) return cb(err);
return cb(null, isMatch)
});
};
module.exports = mongoose.model('User', userSchema)
On a side note, do you guys recommend leaving the userId out of the email verification endpoint? It makes it easier to select the user, but not sure if it will cause security issues.
Total noob with Mongoose and not even sure if I'm approaching this professionally or securely, so all pointers welcome. Thanks guys.

mongoose .save() doesn't work on a specific collection

so I'm trying to create a party with creator field with id of a user, and at the same time adding a party id to users parties using mongoose sessions. Here's the code of a request:
const createParty = async (req, res, next) => {
const {title, description, address, creator} = req.body;
const createdParty = new Party({
title,
description,
image: 'https://media-cdn.tripadvisor.com/media/photo-s/14/03/b3/4e/tlv.jpg',
address,
creator,
savedBy: []
});
let user;
try {
user = await User.findById(creator);
} catch (err) {
let error = new HttpError('Fetching user failed', 500);
return next(error);
}
if (!user) {
return next(new HttpError('Could not find user for providen id', 404));
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await createdParty.save({ session: sess });
user.parties.push(createdParty);
console.log(user);
await user.save({ session: sess });
await sess.commitTransaction();
} catch (err) {
let error = new HttpError('Creating party failed', 500);
return next(error);
}
res.status(201).json({party: createdParty});
};
And my user and parties schemas:
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true, minlength: 6 },
image: { type: String, required: true },
parties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }],
savedParties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }]
});
const partySchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
image: { type: String, required: true },
address: { type: String, required: true },
creator: { type: mongoose.Types.ObjectId, required: true, ref: 'User' },
savedBy: [{ type: mongoose.Types.ObjectId, required: true, ref: 'User' }]
});
The problem is I can't save a user with new party id, only this line fails:
await user.save({ session: sess });. Tried to move this line to a separate try/catch, tried to add user.markModified('parties'); didn't help. Please help those who may know the solution.🙏🏻
UPDATE ON THE PROBLEM
So I did some testing and found out that if I delete everything from the database, and I'll create a user I will be able to add parties, and it'll work as it should. But if I'll create another user and afterward will try to add a party to one of the users it won't work.
when you session it won't create the collection if it doesn't exist and you need to do it manually in the data

I have been trying to solve the duplicate key problem in nodeJS with Mongoose, but nothing works

I'm trying to build a user model, but I want to make sure that username and email are unique. When I created the first user everything was ok, but when I try to create the second user with the same information, I got the some error that I can handle in when I will save, but the duplicate key wasn't there to handle it.
This is my schema file code:
const UserSchema = new Schema({
// this username with SchemaType of string
username: {
type: String,
lowercase: true,
required: [true, "username is required"],
unique: true,
trim: true,
minlength: [4, "try to user longer name"],
maxlength: [60, "your name is way too long"],
},
// virtual name
name: {
// name have two properties
// first is first and refer to first-name
// second is last and refer to last-name
first: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
},
last: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
}
},
password: {
type: String,
required: [true, "password is required"]
},
email: {
type: String,
required: [true, "email is required"],
unique: true
},
active: {
type: Boolean,
default: true
},
admin: {
type: Boolean,
default: false
},
meta: {
update: {
type: Date,
default: Date.now()
},
timestamp: {
type: Date,
default: Date.now()
}
}
});
UserSchema.virtual("fullname").get(function () {
// return the concatenation of first and last
return this.name.first + " " + this.name.last;
});
// Create User Model
const User = mongoose.model("User", UserSchema);
module.exports = User;
And this is my router code where I tried to handle it:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
console.log(err);
});
// save User in model
newUser.save()
// return response with info
return response.status(201).json(user);
})
I think the explanation here is quite a simple one. You are specifying the unique attribute in your schema for multiple fields, so mongo will not allow you to create multiple entries with the same information. This is quite obvious.
Also, I noticed a bit of irregularity in your code. The save method you are calling returns a promise, which means the event loop will not block your code and the response will be returned immediately. For this, you either need to handle your response inside the then block or use async await throughout your code.
I would suggest the following changes:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
if(err) {
response.status(403).json({ message: 'Your custom error message' });
}
newUser.save().then(res => {
return response.status(201).json(user);
}).catch(e => {
return response.status(500).json({ message: 'Your custom error message' });
})
});
})

Mongoose unable to get _id from Schema

I 've a UserSchema that looks like:
export var UserSchema: Schema = new mongoose.Schema({
createdAt: Date,
email: {
type: String,
required: true,
trim: true,
unique: false,
},
firstName: {
type: String,
required: false,
trim: true
},
lastName: {
type: String,
required: false,
trim: true
},
password: {
type: String,
trim: true,
minlength: 6
},
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
And I 've a instance method like:
UserSchema.methods.printThis = () => {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
The method printThis is being called from
router.post('/signup', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
var user = new User(body);
console.log("created user as: ", user);
user.printThis();
});
Below is the output:
created user as: { email: 'prsabodh.r#gmail.com',
password: '123456',
_id: 59be50683606a91647b7a738,
tokens: [] }
========>>> PRINTING USER NOW
{}
You can see that the user is getting created properly. However, when I call printThis method on User - I'm not able to print the same user back and an empty {} is printed. How to fix this?
You shouldn't use arrow functions (=>) if the calling function is explicitly setting a context (which is what Mongoose does):
UserSchema.methods.printThis = function() {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
More info on arrow functions and their handling of this here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Arrow_functions
To get the _id value from the instance method can use _conditions that should work
UserSchema.methods.printThis = function(password) {
var user = this;
console.log(user._conditions['_id']);
};

How use a findOne query method in a custom instance method

I'm trying to do the following:
// Task Model
module.exports = {
schema: true,
attributes: {
directProy: {
type: 'string',
required: true
},
user: {
type: 'string',
required: true
},
checkUser: {
type: 'boolean',
defaultsTo: false
},
proy: {
type: 'string',
required: true
},
pieza: {
type: 'string',
required: true
},
hours: {
type: 'string',
required: true
},
obs: {
type: 'text',
defaultsTo: "lorem ipsum"
},
check: {
type: 'boolean',
defaultsTo: false
},
userName: function() {
User.findOne(this.user).done(function(err, user){
if(err){ return err;}
return user.name;
});
}
}
};
In the method "userName" I'm trying to get the name of a user with the ID it stored in the "user" attribute.
but when I run the "username" method, brings me back "undefined", I think this has to be a problem of asynchronous type
Would greatly appreciate the help they can give me since I have no idea how to associate values ​​between models and this is very useful
try passing a callback.
userName: function(cb) {
User.findOne(this.user).done(function(err, user){
cb(err, user.name);
});
}
Then when you are calling it, make sure to pass a callback.
model.userName(function(err, username) {
console.log(username);
});
your should use .exec instead of .done since it will not be avalible in sails#0.10
http://beta.sailsjs.org/#/documentation/reference/Models

Resources