Reputation: 933
EDIT: This question was asked earlier, but I didn't do a good job of asking it. I've rewritten the question. Thanks in advance for your help!
I'm in the process of writing a simple messaging server for a school project. Among its other functionalities, the server allows the user to update the information stored in their account. When the user does update their account, an authentication token is generated for them. Here's the schema that defines all of that. Note, header
and body
are parts of the user input:
UserSchema = new Schema({
_id: {type: ObjectId, select: false},
username: {type: String, required: true, index: {unique: true} },
password: {type: String, required: true, select: false},
email: {type: String},
token: {type: String, select: false}
}, {
autoIndex: false
});
UserSchema.pre("save", function(next) {
// Create a new token for the user
var self = this;
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) {
next(err);
} else {
crypto.randomBytes(256, function(err, bytes) {
if (err) {
next(err);
} else {
bytes = bytes.toString("hex");
bcrypt.hash((new Date() + bytes), salt, function(err, tokenHash) {
if (err) {
next(err);
} else {
self.token = tokenHash;
next();
}
});
}
});
}
});
});
UserSchema.pre("save", function(next) {
// Hash the password before saving
var self = this;
if (!self.isModified("password")) {
next();
} else {
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) {
next(err);
} else {
bcrypt.hash(self.password, salt, function(err, passwordHash) {
if (err) {
next(err);
} else {
self.password = passwordHash;
next();
}
});
}
});
}
});
I'm running into an issue when updating a particular user. Because I want to use the Model middleware, the way I'm updating a user is by using Model#findOne()
followed by Model#save()
. Here's the code I have to do that:
// Make sure user provided all necessary information.
if (!header.token) {
return callback(new errors.MissingHeaderDataError("Missing 'token' parameter in the header."));
} else {
// Update the user account based on what's in the envelope's body.
User.findOne({"token": header.token}, "+token +password", function (err, user) {
if (err) {
return callback(err);
} else {
// Get a list of all parameters the user wants to change.
var paramsToChange = Object.keys(body);
// Now update the parameters
paramsToChange.forEach(function(param) {
user[param] = body[param];
});
console.log("Updated user:");
console.dir(user);
user.save(function(err, user) {
if (err) {
return callback(err);
} else {
console.log("Returned user:");
console.dir(user);
User.find({}, "+token +password", function(err, foundUser) {
if (err) {
throw err;
} else {
console.log(JSON.stringify(foundUser));
}
});
callback(null, new SuccessEnvelope(user));
}
});
}
});
}
When I run my tests and come to the last bit of code (after save()
is returned), I get this output:
Updated user:
{ token: '$2a$10$5VWWqjJ52aGbS4xc6NDKjuGPv8brX7pRmwiKyYjP8VHoTKCtYZiTu',
username: 'jim_bob',
password: '$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZxF3SwkwadC6D1JsIC9xAUhTbCC',
email: '[email protected]',
__v: 0 }
Returned user:
{ token: '$2a$10$fRwED..7fFFhN46Vn.ZJW..xYql5t5P39LHddjFS4kl/pmhwfT.tO',
username: 'jim_bob',
password: '$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZxF3SwkwadC6D1JsIC9xAUhTbCC',
email: '[email protected]',
__v: 0 }
[{"token":"$2a$10$5VWWqjJ52aGbS4xc6NDKjuGPv8brX7pRmwiKyYjP8VHoTKCtYZiTu","username":"joe_bob","password":"$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZ
xF3SwkwadC6D1JsIC9xAUhTbCC","email":"[email protected]","__v":0}]
As you can see, the document is not properly saved to the database, as the previous data is still there. My question is: why? Why is the user not being updated when calling save? I think I'm doing everything properly, but obviously I'm not. Any help with this would be great since I'm going mad!
Upvotes: 4
Views: 7545
Reputation: 933
Apparently, in order to save a document to the database, it needs an _id
. Kinda silly that Mongoose doesn't give an error when it doesn't find a document. Alas...
I updated my code to reflect the change:
// Make sure user provided all necessary information.
if (!header.token) {
return callback(new errors.MissingHeaderDataError("Missing 'token' parameter in the header."));
} else {
// Update the user account based on what's in the envelope's body.
User.findOne({"token": header.token}, "+_id +token +password", function (err, user) {
if (err) {
return callback(err);
} else {
console.log("Found user:");
console.dir(user);
// Get a list of all parameters the user wants to change.
var paramsToChange = Object.keys(body);
// Now update the parameters
paramsToChange.forEach(function(param) {
user[param] = body[param];
});
console.log("Updated user:");
console.dir(user);
user.save(function(err, user, numberTouched) {
if (err) {
return callback(err);
} else {
console.log("Returned user:");
console.dir(user);
console.log(numberTouched);
User.find({}, "+token +password", function(err, foundUser) {
if (err) {
throw err;
} else {
console.dir(foundUser);
}
});
callback(null, new SuccessEnvelope(user));
}
});
}
});
}
Upvotes: 3