Mongoose seems to satisfy wrong promise (node js) - javascript

I am trying to refactor some inherited code. In every endpoint was the same validation code. I want to pull it out into it's own method. I am new to promises, but I think that is what I want to use. The issues is prom seems to be resolved at the User.findOne call and exits with an undefined prom.promise.
cheers
bob
function validateUser(req) {
var prom = q.defer();
var token = getToken(req.headers);
if (token) {
console.log("have a token")
var decoded = jwt.decode(token, config.secret);
console.log("now going to look for the user")
//Problem exit is on next line
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
prom.reject(err);
if (!user) {
console.log("no user found")
prom.reject("Authentication failed. User not found.")
} else {
console.log("user found returning true")
prom.resolve(true);
}
})
} else {
console.log("no token found")
prom.reject("No token provided.")
}
return prom.promise;
}

why you are using promises when mongoose itself returns it.
function validateUser(req, callback) {
var token = getToken(req.headers);
if (token) {
var decoded = jwt.decode(token, config.secret);
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
callback(err);
if (!user) {
callback("Authentication failed. User not found.")
} else {
console.log("user found returning true")
callback(null, {status:true, userData:user});
}
})
} else {
callback("No token provided.")
}
}
In above code,
if token is not found callback is returned with an error in the first attempt. if token is found then it is decoded in a row and if matched in DB if the result is an error then the callback is called with err parameter else if no user is found or empty match then a custom message is sent in callback error part. But in final is returned as success with status and userData.

Related

I am getting TypeError is not a function in nodeJS

I have a login route but whenever it's giving me a typeError not a function. I have checked the code too many times but still can't get why it's giving me this error:
Here's the code:
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).send("Please provide an email and password");
}
const user = await User.find({ email });
if (!user) return res.status(401).send("User not found");
const isMatch = await user.checkHashedPassword(password);
if (!isMatch) return res.status(401).send("Invalid credentials");
sendTokenResponse(user, 200, res);
} catch (ex) {
console.log(ex);
}
});
The error I get is that user.checkHashedPassword is not a function.
Here's the checkHashedPassword method in userSchema:
userSchema.methods.checkHashedPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
Here's the complete error that I get:
TypeError: user.checkHashedPassword is not a function
at D:\pythonprogs\todoapp\routes\users.js:46:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
I have checked the spellings and everything even changed the function name to see if it works but don't know why it's giving this error. Please help
problem is you are using find() method instead of findOne().
find() returns array of collections not object. try this:
const isMatch = await user[0].checkHashedPassword(password)

Unable to pass custom express-validator check

The purpose of the following code is to check whether an email already exists in MongoDB, using express-validator:
app.post('/registerPage',[check('email').custom((email) => {
// connect to database
let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://localhost';
MongoClient.connect(url, function(err, client) {
if (err) throw err;
let db = client.db('Mydatabase');
// search database
return db.collection('users').findOne({
email: email
}).then(user => {
if (user) {
console.log(user); // here console shows correct record in database
return Promise.reject('E-mail already in use');
}
// otherwise, it returns null
});
})
}).withMessage('Error Message Example')], (req, res) => {
// Handle the request
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
});
When email already exists, console shows Promise.reject('E-mail already in use');.
The problem is when email does not exist, although it doesn't show Promise.reject, the code cannot process any further, validationResult(req) is not empty, so it still prints out the error message 'Error Message Example'. But there isn't any problem with non-custom validators which can successfully pass the checks.
I tried to add an else statement where !user, it doesn't work.
The question is how to pass the custom validation check, or why the array validationResult(req) is not empty even it should be? How do I make sure validationResult is empty after all checks were passed.
The issue is you are returning the promise in the callback of MongoClient.connect and not the validator function. Try using Promise wrapper like:
app.post('/registerPage',[check('email').custom((email) => {
return new Promise((resolve, reject) => {
// connect to database
let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://localhost';
MongoClient.connect(url, function(err, client) {
if (err) throw err;
let db = client.db('Mydatabase');
// search database
return db.collection('users').findOne({
email: email
}).then(user => {
if (user) {
console.log(user); // here console shows correct record in database
return reject('E-mail already in use');
}
return resolve();
});
})
});
}).withMessage('Error Message Example')], (req, res) => {
// Handle the request
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
});
Hope this helps!

Wait for AWS SNS publish callback to return a value to calling method

I am attempting to send a text message when a user requests to reset their password. I would like to wait for the message to be sent to alert the user if it was successful or not. I am currently attempting to do it as follows:
async function sendResetPasswordTextMessage(req, res) {
let result = {};
let phoneNumber = req.body.phoneNumber;
if (phoneNumber === undefined) {
return sendInvalidParametersMessage(res);
}
phoneNumber = phoneNumber.toString();
const userProfile = await models.UserProfile.findOne({
where: {
phoneNumber: phoneNumber
}
});
************************** RELEVANT CODE TO ISSUE *************************
if (userProfile) {
const message = "Your username is:\n" + userProfile.username;
const sent = await AWSSNSClient.sendMessage(message, phoneNumber);
if (!sent) {
result.error = setTitleAndMessage("Error", "An error occurred");
} else {
result.success = setTitleAndMessage("Success", "Message sent");
}
}
return res.send(result);
***************************************************************************
}
In my other class AWSSNSClient, I have the following sendMessage function:
function sendMessage(message, phoneNumber) {
const params = {
Message: message,
MessageStructure: "string",
PhoneNumber: "+1" + phoneNumber
};
let sent = false;
sns.publish(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
sent = true;
}
});
return sent;
}
I cannot figure out how to make sendMessage wait for sns.publish to return before it returns itself. I have tried making it an async method and adding await on sns.publish, but the function still returns before sent gets set to true.
I know that the messages are sending without error because I am receiving them and no console logs are printed.
Stumbled on this one via Google trying to figure this out myself today - short answer that I am now using:
You can now do this with Async/Await — and Call the AWS service (SNS for example) with a .promise() extension to tell aws-sdk to use the promise-ified version of that service function (SNS) instead of the call back based version.
The only caveat here is the containing function must ALSO be async to utilize the await syntax.
For example:
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("SNS Push Failed:");
console.log(err.stack);
return;
}
console.log('SNS push suceeded: ' + data);
return data;
}).promise();
The important part is the .promise() on the end there. Full docs on using aws-sdk in an async / promise based manner can be found here: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
In order to run another aws-sdk task you would similarly add await and the .promise() extension to that function (assuming that is available).
For anyone who runs into this thread and is actually looking to simply push multiple aws-sdk promises to an array and wait for that WHOLE array to finish (without regard to which promise executes first) I ended up with something like this:
let snsPromises = [] // declare array to hold promises
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("Search Push Failed:");
console.log(err.stack);
return;
}
console.log('Search push suceeded: ' + data);
return data;
}).promise();
snsPromises.push(snsResult)
await Promise.all(snsPromises)
Hope that helps someone that randomly stumbles on this via google like I did!
stackdave will that actually wait?
Necevil "Search push suceeded will get logged twice" because you're mixing calling operations by passing a callback and using promises. You should only use one method of getting the result
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn}).promise()
will do the trick
You can simply use callbacks for that. Modify your sendMessge like this
function sendMessage(message, phoneNumber, cb) {
const params = {
Message: message,
MessageStructure: "string",
PhoneNumber: "+1" + phoneNumber
};
sns.publish(params, cb);
}
then on your main file you can supply callback like this
if (userProfile) {
const message = "Your username is:\n" + userProfile.username;
AWSSNSClient.sendMessage(message, phoneNumber, (err, data) => {
if (err) {
result.error = setTitleAndMessage("Error", "An error occurred");
}
else {
result.success = setTitleAndMessage("Success", "Message sent");
}
res.send(result);
});
}
Here the right updated API, August 2018, Necevil answer send the sms twice.
// using config.env
AWS.config.region = 'eu-west-1';
AWS.config.update({
accessKeyId: process.env.AMAZON_SMS_ID,
secretAccessKey: process.env.AMAZON_SMS_TOKEN,
});
// parameters
let params = {
Message: contentSMS, // here your sms
PhoneNumber: mobile, // here the cellphone
};
const snsResult = await sns.publish(params, async (err, data) => {
if (err) {
console.log("ERROR", err.stack);
}
console.log('SNS ok: ' , JSON.stringify (data));
});
If you're having issues with duplicate SNS messages being sent, I fixed this issue by utilizing examples from AWS:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'REGION'});
// Create publish parameters
var params = {
Message: 'MESSAGE_TEXT', /* required */
TopicArn: 'TOPIC_ARN'
};
// Create promise and SNS service object
var publishTextPromise = new AWS.SNS({apiVersion: '2010-03-31'}).publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("Message ${params.Message} send sent to the topic ${params.TopicArn}");
console.log("MessageID is " + data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});
By utilizing a traditional .then() I was able to squash the duplicate message bug mentioned in comments above.
You can create a async function what use the promise method
async function sendMessage(message, phoneNumber){
const params = {
Message: message,
PhoneNumber: phoneNumber
};
return new Promise((resolve, reject) => {
SNS.publish(params, (err, data) => {
if (err) {
console.log("Search Push Failed:");
console.log(err.stack);
return reject(err);
} else {
console.log('Search push suceeded:' + phoneNumber);
return resolve(data);
}
})
});
}
and then you can call
var s= await sendMessage(message,phoneNumber);

async watefall doesn't call the functions

So i am actually woking on a simple program with node.Js and i have an issue using async.waterfall :
I created a function in my user model that connect the user by accessing the database, here is the code :
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
callback(err,null);
return;
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}
Using node-inspector i figured out that the main issue(I think) is that when it enters the waterfall function it doesn't execute the callback function of findOne it literally skips this and directly jump to the getPassword function (which isn't executed too).
so if someone could help me figuring out what's the problem that would be nice since i'm on it for around two days now.
Thank you
EDIT:
After adding the different missing cases of tests(which was why the callback didn't worked) I have this connection function:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
console.log('login: ',res.login);
console.log('erreur: ',err);
if (err){
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('login received :',userReceived.login);
var Ulogin = userReceived.login;
var Upassword = userReceived.password;
// function that compare the received password with the encrypted
//one
bcrypt.compare(password, Upassword, function(err, isMatch) {
if (err) {
console.log(err);
callback(err,null);
return;
}
else if (isMatch) {
console.log('Match', isMatch);
callback(null,isMatch);
}
else {
console.log('the password dont match', isMatch);
callback('pwd error',null);
}
});
},
], function(err){
if (err) {
console.error('unexpected error while connecting', err);
return false;
}
console.log('connected successfully');
return true;
});
}
And in my main file server.js i'm doing currently doing :
var connect = users.connection(login,password);
//the goal is to use the connect variable to know if the connection
//failed or not but it's 'undefined'
if(connect){
res.send('youyou connecté');
}
else {
res.send('youyou problem');
}
this absolutely don't work so i tried to use Q library but I have an error saying
"TypeError: Cannot read property 'apply' of undefined at Promise.apply"
here is the code using Q:
app.post('/signup', function (req, res) {
var login = req.body.login;
var password = req.body.password;
Q.fcall(users.connection(login,password))
.then(function (connect) {
if(connect){
res.send('connected');
}
else {
res.send('problem');
}
})
.catch(function (error) {
throw error;
})
.done();
});
but i am a little bit astonished i thought that by using async.waterfall() i told the function to wait until it received all the callbacks return so i don't understand why the connect variable is 'undefined'?
What I don't understand is - what was the flow exactly? did 'usersModel.findOne' get called?
What I see that is missing here in the getLogin function is a callback in the case that both the 'if' statement return false. in this case you'll get stuck in the first function and you won't advance to 'getPassword' function.
If this still doesn't work, please try executing the following code and report what was printed:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
console.log('getLogin - error has occured');
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
console.log('getLogin - result seems OK');
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('getPassword');
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}

Sails.js. Check if user exists

Hello guys am new to Sails.js ( using MySQL )
Am trying to find if a user already exists before registration.
Here this is the code:
register:function(req, res, next){
var params = req.params.all();
User.find({
or : [
{ usrnm:params.usrname },
{ eml:params.eml }
]
})
.exec(function (err, user){
if (err) {
return res.negotiate(err);
}
if (user) {
res.status(400);
return res.json('User already exists!');
}
});
User.create(params, function(err, user){
if(err){
return next(err);
}
res.status(201);
res.json(user);
});
}
The problem is:
The response is always "User already exists!" with status code - 400
If user exists with the given username or/and email, the above message is displayed regardless and then something is getting logged in the console ( which I dont understand ) and user is not created as in my MySQL those two fields are unique.
**If user does not exists ** the user gets created behind but it still displays the above message.
I want to display the message only if user exists (ie if given credentials matches) else respond with 201
register:function(req, res, next){
var params = req.params.all();
User.find({
or : [
{ usrnm:params.usrname },
{ eml:params.eml }
]
})
.exec(function (err, users){
if (err) {
return res.negotiate(err);
}
if (users.length) {
res.status(400);
return res.json('User already exists!');
} else {
User.create(params, function(err, user){
if(err){
return next(err);
} else {
res.status(201);
res.json(user);
}
});
}
});
}
You should call the create user method if a user with those parameters do not already exist, and so should put it inside the callback.
The User.find() function returns an array, so you need to check its length to see if there are any matching objects.
Okay guys I figured out a solution, i will put it here in case if it helps someone
if (user) { // will be true even if user = []
res.status(400);
return res.json('User already exists!');
}
In case when user is not found in the DB , user is = [ ] , this means [ ] != false
, hence the message within the scope is getting displayed.

Categories

Resources