Node.js Mongoose Promise getting lost - javascript

I have a Node.js API with a mongoDB. There is a route that creates a user and needs to hash the password, for this I use the bcryptjs package.
the route looks like this:
router.route('/user')
.post(function(req, res) {
if(req.body.password === req.body.passwordConfirm) {
userManager.addUser(req.body)
.then(function(response) { // waiting for the result of the mongoDB save
res.send({data:response});
});
} else {
res.send({err:'passwords do not match'});
}
})
and userManager.addUSer:
this.addUser = function(userobject) {
bcrypt.genSalt(10, function(err, salt) { // generate a salt
if(err !== null) {
console.log(err);
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) { // hash pw
if(err !== null) {
console.log(err);
else {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save().catch(function(err){ // save user in mongoDB
console.log(err);
});
}
});
}
});
};
I get an error saying: "Cannot read property 'then' of undefined", which tells me that I am not receiving a promise from addUser. I looked and bcryptjs sadly does not use promises, however, mongoose does.
(adding this:
var mongoose = require('mongoose').Promise = Promise;
didn't help)
I tried wrapping the function in a promise with reject and resolve, but that gives this error: "TypeError: Promise resolver undefined is not a function".
How do I get the promise that the save() function of mongoose returns back to the .then() in the post route? I tried adding return in front of the two bcrypt function but that didn't work either..
Any suggestions are welcome!

Your addUser function nevers returns the promise to its caller. You're doing a return from the bcrypt.hash callback function, but that has nothing to do with addUser's return value.
It looks like addUser has to use some non-Promise-enabled APIs, so you're stuck with doing new Promise, something like this (see the *** comments):
this.addUser = function(userobject) {
return new Promise(function(resolve, reject) { // ***
bcrypt.genSalt(10, function(err, salt) { // generate a salt
if(err !== null) {
reject(err); // ***
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) { // hash pw
if(err !== null) {
reject(err); // ***
else {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
resolve(user.save()); // *** save user in mongoDB
}
});
}
});
});
};
Also note that I don't have addUser just swallowing errors; instead, they're propagated to the caller. The caller should handle them (even if "handling" is just logging).

You do not return a Promise form your this.addUser, you have to convert your callback based bcrypt to Promises. You can convert the whole bcrypt API to support Promise based functions using e.g. promisifyAll of the bluebird library, or do it manually using new Promise like this way:
this.addUser = function(userobject) {
return new Promise((resolve, reject) => {
bcrypt.genSalt(10, (err, salt) => {
if (err) {
reject(err);
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) {
if (err) {
reject(err)
} else {
resolve(hash)
}
})
}
});
})
.then(hash => {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save() // save user in mongoDB
})
.catch(function(err) {
console.log(err);
});
}
Or that way:
this.addUser = function(userobject) {
return new Promise((resolve, reject) => {
bcrypt.genSalt(10, (err, salt) => {
if (err) {
reject(err);
} else {
resolve(salt);
}
});
})
.then(salt => {
return new Promise((resolve, reject) => {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) {
if (err) {
reject(err)
} else {
resolve(hash)
}
})
})
})
.then(hash => {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save() // save user in mongoDB
})
.catch(function(err) {
console.log(err);
});
}

After doing some more digging in the change logs of bcryptjs I found out that they added promises but did not update the documentation.. The genSalt en hash methods will return a promise if the callbacks are omitted. This would translate to:
this.addUser = function(userobject) {
return bcrypt.genSalt(10).then((salt) => {
return bcrypt.hash(userobject.password, salt).then((hash) => {
userobject.password_hash = hash;
var user = new User(userobject);
return user.save();
});
});
};

Related

Method returns undefined the first time it is called

I have a method that selects distinct values from a database as shown below:
function displayCategories(res, req) {
query = `SELECT DISTINCT name FROM product_category;`;
connection.query(query, function (err, rows) {
if (err) {
console.log(err);
res.render("home");
throw err;
} else {
session = req.session;
session.categories = rows[0];
}
});
}
I then have a button with the method POST and action /categories
The displayCategories is called when the button is clicked as follows:
router.post('/categories', function (req, res) {
displayCategories(res, req);
if (session.categories === undefined) {
console.log("categories is undefined");
} else {
console.log("categories is defined");
console.log(session.categories);
}
})
I added some console logs for test purposes. The issue I am having is that the first time I click the button, it returns undefined. Each time I click it again, it prints the correct data for session.categories as shown below:
Is there a simple fix for this issue?
The code is calling a displayCategories as if it were synchronous, but it is running asynchronous code with the callback.
There are multiple possible solutions for that but one of them would be to use Promises, like the following:
const displayCategories = (res, req) => new Promise((resolve, reject) => {
// you are not declaring query in this scope, that makes it global
query = `SELECT DISTINCT name FROM product_category;`
connection.query(query, function (err, rows) {
if (err) {
console.error(err)
res.render("home")
reject(err)
} else {
session = req.session
session.categories = rows[0]
resolve()
}
})
})
And the other part with an async function
router.post('/categories', async function (req, res) {
await displayCategories(res, req);
if (session.categories === undefined) { // session is not declared
console.log("categories is undefined");
} else {
console.log("categories is defined");
console.log(session.categories); // session is not declared
}
})
But that's just to make your issue go away, if you want to improve the code even further you can just keep the responsibility of dealing with request and response with the controller action and just use the other function to get the data you want, isolation its responsibility:
const getCategories = () => new Promise((resolve, reject) => {
const query = `SELECT DISTINCT name FROM product_category;`
connection.query(query, (err, rows) => {
if (err) return reject(err)
resolve(rows)
})
})
router.post('/categories', async function (req, res) {
try {
req.session.categories = await getCategories();
if (req.session.categories === undefined) {
console.log("categories is undefined");
} else {
console.log("categories is defined", req.session.categories);
console.log();
}
} catch(e) {
console.error(e)
res.render("home")
}
})

Promises when passing a function as a parameter

I understand how promises work for the most part, but I have a lot of trouble understanding how to deal with them when I need to pass a function as a parameter:
var promise = new Promise(function(resolve, reject) {
// Do async job
ec2.describeInstances(function(err, data) {
console.log("\nIn describe instances:\n");
var list = [];
if (err) reject(err); // an error occurred
else {
var i = 0 ;
//console.log(data.Reservations);
var reservations = data.Reservations;
for (var i in reservations) {
var instances = reservations[i]['Instances'];
var j = 0;
//console.log(JSON.stringify(instances, null, 2));
for (j in instances){
var tags = instances[j]
var k = 0;
var instanceId = tags['InstanceId'];
var tag = tags['Tags'];
var l;
//console.log(tag);
for (l in tag){
//console.log(instanceId);
//console.log(tag[l]['Value']);
if (String(tag[l]['Value']) == '2018-10-15T23:45' || String(tag[l]['Key']) == 'killdate') {
console.log(tag[l]['Key'] + ' ' + tag[l]['Value']);
list.push(instanceId);
console.log(list);
//return(list);
}
}
}
}
resolve(list);
}
});
});
promise.then(function (list) {
ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
});
before I had the first part of the code as:
return new Promise(function(resolve, reject) { ... }
and that worked for the first part, but as soon as I changed it to a "var" and added the new promise in underneath, it stopped working. (edit) When I mean "stopped working" I mean, neither of the two functions run, i.e.: it ends the handler before either functions are finished and none of the return statements or console logs.
Any help would be greatly appreciated!
Thanks!
Wondering if something like this would work:
var promise = Promise.resolve(function() {
return ec2.describeInstances...
})
promise
.then(/* handle successful promise resolution */ )
.catch(/* handle promise rejection */ )
var promise = Promise.resolve();
promise
.then(function() {
return ec2.describeInstances(function(err, data) {
var list = [];
if (err) throw err; // an error occurred
// else logic
})
})
.catch(/* if needed here */)
.then(function (list) {
return ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
})
.catch(/* if needed here */)
my suggestion is to break up your logic - it will be easier to handle the result you want to achieve.
A proper way in my opinion:
promise function(a service function):
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
result = () => resolve(data);
fail = () => reject(err);
});
}
then your promise caller:
myAsyncFunction().then(dataHandler(result), // "promise worked!"
function (err) {// Error: "It broke"
console.log(err)
});
then the logic:
function dataHandler(data) { /* data logic */}
good luck
I ended up fixing it. Sorry, forgot to post back before I added in the SNS portion. I ended up learning a ton about functional programming on the way and decided to use the await function over the complicated promise syntax. Below is the code:
exports.handler = async (event, result, callback) => {
const AWS = require('aws-sdk');
const date = new Date().toISOString().substr(0, 16)
const ec2 = new AWS.EC2();
var sns = new AWS.SNS();
console.log("date is: " + date)
console.log(date.length);
const params = {
TopicArn:'arn:aws:sns:us-east-1:503782973686:test',
Message:'Success!!! ',
Subject: 'TestSNS'
}
const describeResult = await ec2.describeInstances().promise()
const terminatableInstances = await describeResult
.Reservations
.reduce((acc, reservation) => acc.concat(reservation.Instances), [])
//'2018-10-15T23:45'
.map((instance) => {
//console.log(instance)
//console.log(instance.Tags)
var date = instance.Tags
.filter(tag => tag.Key == 'killdate' && tag.Value == date) //date should be in this format on tag: 2018-10-15T23:45
.reduce((acc, curr) => curr.Value, null);
if (date != null) {
return instance.InstanceId;
}
return null;
})
.filter(id => id != null)
console.log(terminatableInstances);
const snsPush = await ec2.terminateInstances({
InstanceIds: terminatableInstances,
//DryRun: true //set this flag if you want to do a dry run without terming instances
}, (err, data) => {
if (err) {
console.log('no instances to terminate!')
}
else {
console.log('terminated instances')
}
})
console.log(snsPush)
//return(snsPush).promise()
return sns.publish(params, (err, data) => {
if (err) {
console.log(err, err.stack);
}
else {
console.log('sent');
}
}).promise();
};

JavaScript promise not firing in order

I have been trying to effectively manage how I build my promises in my Express.js app.
Right now I have the following scenario: During the signup process, a user has an optional Organization Name field. If this is filled in, I need to create the Organization object and then add the _id of it to the other information that I am applying to the user. If there is no Organization name, proceed and update the basic user info.
<-- Right now the user is being updated before the organization is being created. -->
//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;
//if organization name is passed, create object and store _id in user object.
let create_organization = new Promise(function(resolve, reject) {
if (req.body.organization_name !== "") { //check if name is sent from form
Organization.create({
name: req.body.organization_name
}, function(err, result) {
console.log(result);
if (!err) {
info['local.organization'] = result._id;
resolve()
} else {
reject()
}
})
} else {
resolve()
}
});
let update_user = new Promise(function(resolve, reject) {
User.update({
_id: req.user._id
}, info, function(err, result) {
if (!err) {
console.log("Updated User!"); < --prints before Organization is created
resolve();
} else {
reject();
}
})
});
create_organization
.then(function() {
return update_user;
})
.then(function() {
res.redirect('/dash');
})
Nothing in your code waits for the first promise to settle before proceeding with starting the subsequent work. The work is started as soon as you call User.update, which is done synchronously when you call new Promise with that code in the promise executor.
Instead, wait to do that until the previous promise resolves. I'd do it by wrapping those functions in reusable promise-enabled wrappers (createOrganization and updateUser):
// Reusable promise-enabled wrappers
function createOrganization(name) {
return new Promise(function(resolve, reject) {
Organization.create({name: name}, function(err, result) {
console.log(result);
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
function updateUser(id, info) {
return new Promise(function(resolve, reject) {
User.update({_id: id}, info, function(err, result) {
if (err) {
reject(err);
} else {
resolve();
}
})
});
}
(You may be able to use util.promisify or the promisify npm module to avoid doing that manually.)
And then:
//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;
//if organization name is passed, create object and store _id in user object.
(req.body.organization_name === "" ? Promise.resolve() : createOrganization(req.body.organization_name))
.then(function() {
return updateUser(req.user._id, info);
})
.catch(function(error) {
// handle/report error
});
(I stuck to ES5-level syntax since your code seemed to be doing so...)
See, so called 'executor function', passed into Promise constructor, is invoked immediately. That's why you essentially have a race condition here between two remote procedure calls. To solve this, make the update responsible for promise creation instead:
function updateUser(userId, userInfo) {
return new Promise(function(resolve, reject) {
User.update({_id: userId}, userInfo, function(err, result) {
if (err) {
reject(err);
}
else {
resolve(result);
}
});
});
}
... and call this function in then(). By doing this, an executor function will be called only when updateUser is invoked - and that'll be after createOrganization() finished its job.

Error: TypeError: Cannot read property 'catch' of undefined when trying to register user for website node.js

I seem to have messed up one of my promises (I think) in a javascript function that is supposed to register a user. I have included the post request and the actual function itself.
app.post("/register", (req, res) => {
dataServiceAuth.registerUser(req.body).then(() => {
res.render("register", {successMessage: "User created"});
}).catch((err) => {
res.render("register", {errorMessage: err, user: req.body.user});
});
});
module.exports.registerUser = function (userData) {
return new Promise(function (resolve, reject) {
if (userData.password != userData.password2) {
reject("Passwords do not match");
}
else {
let newUser = new User(userData);
newUser.save((err) => {
resolve();
}).catch((err) => {
if (err) {
if (err.code == 11000) {
reject('User Name already taken');
}
else {
reject('There was an error creating the user: ${err}');
}
}
});
}
});
};
If newUser.save can return a promise, you definitely shouldn’t be passing a callback to it, or even using the Promise constructor at all. If you really want to reject with strings, the way to implement that would be by transforming rejections from newUser.save() with .catch into new rejections by returning them, and returning the resulting promise from registerUser:
module.exports.registerUser = function (userData) {
if (userData.password != userData.password2) {
return Promise.reject("Passwords do not match");
}
let newUser = new User(userData);
return newUser.save().catch((err) => {
if (err.code == 11000) {
return Promise.reject('User Name already taken');
}
else {
return Promise.reject('There was an error creating the user: ${err}');
}
});
};

How to handle Recursive Promise

function Auth() {
this.accessTokenError = false;
}
Auth.prototype.validateToken = function (accessToken, refreshToken) {
var token;
var self = this;
return new Promise(function (resolve, reject) {
AuthCron.secret()
.then(function (secret) {
if (self.accessTokenError) {
token = refreshToken;
secret = secret.substr(0, secret.length / 2);
}
else
token = accessToken;
JWT.verify(token, secret, function (error, decoded) {
console.log(error, decoded);
if (error) {
if (!self.accessTokenError) {
self.accessTokenError = true;
// I don't know how to handle this
self.validateToken(accessToken, refreshToken)
}
else {
self.accessTokenError = false;
reject(error);
}
}
else
resolve(decoded.user);
});
})
.catch(function (err) {
reject(err)
});
})
};
I am bit confused on how to handle recursive promise. The problem here is first promise never resolves nor rejects. What is the best way to handle this situation? This function accepts two tokens if access token is expired or invalid then refresh token is validated, if refresh token is also invalid then promise should reject.
With problems like this, it's generally best to separate the creation of promises from the higher order application logic. This is done by promisifying at the lowest possible level - JWT.verify() in this case.
JWT.verifyAsync = function(token, secret) {
return new Promise((resolve, reject) => {
JWT.verify(token, secret, (error, decoded) => {
error ? reject(error) : resolve(decoded);
});
});
};
Now, you could still write the Auth.prototype.validateToken() method to perform its tries recursively, but, as there would only ever be a maximum of two levels of recursion, it's far simpler to hard-code both tries with (pseudo-code) first_try().catch(() => second_try()). As a bonus, the need for that awkward .accessTokenError boolean disappears.
Here it is in full :
Auth.prototype.validateToken = function(accessToken, refreshToken) {
return AuthCron.secret()
.then(secret => {
return JWT.verifyAsync(accessToken, secret) // first try
.catch(() => JWT.verifyAsync(refreshToken, secret.substr(0, secret.length / 2))); // second try
})
.then(decoded => decoded.user);
};

Categories