I'm trying to test some logic inside a callback without connecting to a database. To do this, I need to stub the model's save method. I do, however, still need to inspect model values that change as part of the logic.
The logic:
// check if user exists with facebook or google account
User.findOne({ $or: [{'facebook.email': email}, {'google.email': email}] }, function(err, user) {
if (err) return cb(err);
else {
// create new user
if (!user) {
user = new User();
}
// add the local account
user.local.email = email; // <--
user.local.password = User.generateHash(password);
// create a new token for the local account
user.local.token = User.createAccessToken(email);
// save
user.save(function(err, user) {
if (err) cb(err);
return cb(false, user); // <-- HOW DO I TEST that user.local.email == email inside this callback?
});
}
});
My test looks like this:
it('should create new user if email not in database', function(done) {
var email = 'test#test.com';
sinon.stub(User, 'findOne').yields(false, null);
sinon.stub(User.prototype, 'save') // <-- how to stub this correctly?
userService.localSignup(email, '123123', function(err, user) {
assert.instanceOf(user, User);
assert.equal(user.local.email, email);
assert.isNotNull(user.local.password);
return done();
});
});
Maybe these nested callbacks are a bad pattern?
Related
I have an application that is connected to this route. This route is to update the user personal detail, I don't get any errors in the process but for some reason what ever I put the input, the value on the mongodb is changed to null.
app.post('/updateUserDetails', verifyToken, function(req, res){
jwt.verify(req.token, 'secretkey', (err, authData) => {
if(err) {
res.sendStatus(403);
} else {
var userID = authData._id,
newFirstName = req.firstName;
// lastName = req.lastName,
// age = req.age,
// gender = req.gender,
// phoneNumber = req.body.phoneNumber;
console.log(err);
user.update({_id: userID}, {firstName: newFirstName}, function(err, updatedUser){
if(err){
console.log("error updating user firstName");
res.json({msg:"error updating user firstName"});
}else{
console.log("user firstName has been updated");
res.json({msg:"user firstName has been updated", firstName: newFirstName});
}
},function(err){
console.error(err);
});
}
});
});
Console.log your req see exactly what you are sending, you can also use the debugger to understand what is being sent and received at any point.
console req.firstname and check what will print. try req.body.firstName, your route is post call
I am currently trying to add some authentication to my node API.
Right now I'm using PassportJS (pretty new to this so sorry for my incompetents).
I am trying to add a local strategy and check if the users password is legit when loggin in:
// Local Strategy
passport.use(
new LocalStrategy(async (username, password, done) => {
try {
// Find user by username
const user = await User.findOne({ username })
// No user found
if (!user) {
return done(null, false)
}
console.log('user', user) // Getting output
// Check if password correct
const isMatch = await user.isValidPassword(password)
// Handle if password is not correct
if (!isMatch) {
return done(null, false)
}
// Return user
done(null, user)
} catch (err) {
done(err, false)
}
})
)
Something I've noticed is when using await on const isMatch = await user.isValidPassword(password) Postman is saying: Error: ReferenceError: user is not defined. And when I remove await it works fine, but I can type in the wrong password but I still can login. And I can see my user object when I console.log it.
{
"username": "martinnord3",
"password": "this_is_the_wrong_password"
}
Here's the isValidPassword function:
UserSchema.methods.isValidPassword = async function(newPassword) {
try {
return await bcrypt.compare(newPassword, user.password)
} catch (err) {
throw new Error(err)
}
}
I guess there's something obvious I'm missing, but I can't manage to solve this.
Thanks for taking your time to read this!
Well this is a bit awkward, but I guess it's my duty to answer my own dumb question... My function isValidPassword has this: ...user.password and I don't specify what user is in that function.. It expects this.
I am trying to get a user to sign up, I have the HTML form working etc. I just need to handle the sign up itself.
The user is successfully created BUT I'm not sure how to keep the user logged in or access the current user logged in as a Parse.User object.
app.post("/sign-up", function (req, res) {
var userObject = new Parse.User();
userObject.set("username", username);
userObject.set("password", password);
userObject.set("email", email);
userObject.set("supportEmail", email);
userObject.signUp(null, {success: function(user) {
//success
res.redirect('/admin');
},
error: function(user, error) {
//show error
console.log(error.message);
res.render('sign-up', {success:false, errorMessage:error.message});
}
});
});
Not sure what to do in order to keep them logged in and to acess the Parse.User object for the current user.
you can save in global variable in your application. also you can export user object to use in other files. There is another way to store in database or one other way is to app.locals.user = userObject
var userObject = new Parse.User();
app.post("/sign-up", function (req, res) {
userObject.set("username", username);
userObject.set("password", password);
userObject.set("email", email);
userObject.set("supportEmail", email);
app.locals.user = userObject;
userObject.signUp(null, {success: function(user) {
//success
res.redirect('/admin');
},
error: function(user, error) {
//show error
console.log(error.message);
res.render('sign-up', {success:false, errorMessage:error.message});
}
});
module.exports.userObject = userObjetct;
The signup promise resolves in an authentication object along with the session token.
Then you can use it and call Parse.User.become to retrieve the user class.
Parse.User.become("session-token-here").then(function (user) {
// The current user is now set to user.
}, function (error) {
// The token could not be validated.
});
Source: http://parseplatform.github.io/docs/js/guide/#setting-the-current-user
postRegistrationHandler: function (account, req, res, next) {
console.log('postRegistrationHandler activated');
account.getCustomData(function(err, data) {
if (err) {
console.log(err.toString, "error string");
return next(err);
} else {
data.mongo_id = userCreationCtrl(account);
data.save();
next();
}
});
},
This function almost works properly, but the line:
data.save();
runs before the previous line finishes which means that the data I want to save isn't present at the appropriate time.
data.mongo_id = userCreationCtrl(account);
This line calls a function that creates a mongoDB document with information in the account object and then returns the _id (which is what I am trying to save.
I thought maybe using a .then() would help but that seems to be unavailable here for some reason. If anyone sees something I'm missing, that would be quite helpful. Thank you!
Here is the userCreationCtrl file as requested:
var UserSchema = require('./../models/UserModel.js');
var createNewUser = function (account, res, next){
// We will return mongoId after it is created by submitting a newUser
var mongoId = "";
// Save StormpathID (last 22 characters of account.href property)
var newStormpathId = account.href.slice(account.href.length - 22);
console.log('stormpath ID:', newStormpathId, 'just registered!');
console.log(account);
// Create new user from model by recycling info from the Stormpath registration form and include the stormpathId as well.
var newUser = new UserSchema({
stormpathId: newStormpathId,
firstName: account.givenName,
lastName: account.surname,
email: account.email,
street: account.street,
city: account.city,
zip: account.zip
});
// This saves the user we just created in MongoDB
newUser.save(function(err, result){
console.log(result);
if (err) {
console.error(err);
}
else {
console.log("User created in MongoDB, attempting to return mongoDB _id to stormpath customData");
// Keep track of the new user's mongo _id so we can return it to the previous function and save it as Stormpath custom data.
mongoId = result._id;
console.log(mongoId, "mongoid");
return result._id;
}
});
};
module.exports = createNewUser;
You have userCreationCtrl expecting 3 arguments, account, res, and next. next is the callback that should be called after the user is created so instead of return result._id you should call next like so:
// inside of createNewUser()
newUser.save(function(err, result){
console.log(result);
if (err) {
console.error(err);
}
else {
console.log("User created in MongoDB, attempting to return mongoDB _id to stormpath customData");
// Keep track of the new user's mongo _id so we can return it to the previous function and save it as Stormpath custom data.
mongoId = result._id;
console.log(mongoId, "mongoid");
// IMPORTANT change to make it all work...
// get rid of return result._id because its not doing anything
// pass the value to your callback function instead of returning the value
next(null, result._id);
}
});
then calling code in postRegistrationHandler should look like this:
account.getCustomData(function(err, data) {
if (err) {
console.log(err.toString, "error string");
return next(err);
} else {
// pass in a callback as the 3rd parameter that will be called by newUser.save() when its finished
userCreationCtrl(account, null, function(err, resultId) {
data.save();
next();
});
}
});
Hi I am currently new to nodejs and mongodb what I want to do is make a function to update my win,lose,draw record from my userschema.
My Schema:
UserSchema = new mongoose.Schema({
username:'string',
password:'string',
email:'string',
//Change Made
win:{ type: Number, default: 0 },
lose:{ type: Number, default: 0 },
draw:{ type: Number, default: 0 }
});
My Function for updating:
//Update scores
app.post("/user/updateScores", function (req, res) {
var user = new User({
username:req.body.username,
win:req.body.win,
lose:req.body.lose,
draw:req.body.draw
});
Users.findOne({ username : req.params.username }, function(error, user) {
if (error || !user) {
res.send({ error: error });
} else {
user.update(function (err, user) {
if (err) res.json(err)
req.session.loggedIn = true;
res.redirect('/user/' + user.username);
});
}
});
});
The problem is when I try updating, when I try updating via my html file. It does not update anything and just stays the same (the values win,lose,draw the default value is 0 so when I logout and login again the values of the win,lose,draw record is still zero). I thoroughly checked if the problem was the html and javascript functions that I have made but this is not the case so I think that the problem is the update function I have made. Any of you guys have an idea where I went wrong? Thanks!
Assuming your post is being called correctly from the client, you'll need to be careful about variable and parameter names, as the scope right now is that you're saving an exact duplicate of the user object that was just fetched via findOne.
You had user declared as a variable of the post callback, and then again within the findOne. The inner variable user will take precedence.
app.post("/user/updateScores", function (req, res) {
var username = req.body.username;
Users.findOne({ username : username }, function(error, user) {
if (error || !user) {
res.send({ error: error });
} else {
// update the user object found using findOne
user.win = req.body.win;
user.lose = req.body.lose;
user.draw = req.body.draw;
// now update it in MongoDB
user.update(function (err, user) {
if (err) res.json(err) {
req.session.loggedIn = true;
}
res.redirect('/user/' + user.username);
});
}
});
});