Meteor asynchronous call within Accounts.createUser - javascript

Before registering a new User I need to delete a previous one with that email and query an API to fill more information into User as a product requirement.
Unfortunately I can not achieve it, this is the error I get from the server: Exception while invoking method 'createUser' Error: insert requires an argument.
What I did so far is this:
Client:
Accounts.createUser({ email, password, profile: { something } }, (err) => {
if (err) {
console.error(err2.reason);
}
history.replace('/account');
});
Server:
Accounts.onCreateUser((options, user) => {
Meteor.users.remove({ email: options.email }, () => {
try {
const res = request.postSync(authenticate, {
method: 'POST',
json: true,
body: {
email: options.email,
password: options.profile.password
}
});
if (res.response.statusCode < 300) {
const newUser = user;
newUser.profile = {};
newUser.profile.user_id = res.body.response.user_id;
newUser.profile.token = res.body.response.token;
return newUser;
}
throw new Meteor.Error(res.response.body.error.message);
} catch (err) {
throw new Meteor.Error(err.message);
}
});
});
What I'm doing wrong? Thanks

From the Accounts.onCreateUser documentation:
The function should return the user document (either the one passed in or a newly-created object) with whatever modifications are desired. The returned document is inserted directly into the Meteor.users collection.
You function returns nothing though, it just calls Meteor.users.remove() with some function as second argument. Don't forget that DB calls are synchronous in Meteor, so it should be like this:
Accounts.onCreateUser((options, user) => {
Meteor.users.remove({ email: options.email });
// do something else
return user;
});

Related

Node.js: unable to return value from other function inside same file

So I have this MEAN-stack app where, on the back-end, I have two ways to create a new user: through google authentication and through username.
Now, I am working on the code to create a new user through username, but I can't return the value from another function in my node.js file (in this case createNewUser()). When I look at my database the new user is created, but the value returned from createNewUser() is undefined.
I first thought it was a problem due to asynchronicity, so I tried a setTimeout(). The function however still returns undefined. Does someone see why?
This is the route in my node.js backend: the two console.logs return undefined
router.post('/signup', function(req, res){
var result = createNewUser(req.body);
console.log('result 1', result);
setTimeout(function(){
console.log('result2', result);
if (!result.success){
res.status(500).json({
message: "unable to create User",
obj: result.err
})
} else {
res.status(200).json({
message: "successfully created new user",
obj: result.obj
})
}
}, 6000);
});
This is the createNewUser() function in that same file, the console.log returns the user, so the user is actually created, the function just doesn't return the value.:
function createNewUser(userData, socialRegistry){
var user;
if (socialRegistry){
user = new User({
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.body.email,
googleID: userData.body.provider.ID
});
} else {
user = new User({
firstName: userData.firstName,
lastName: userData.lastName,
username: userData.username,
password: bcrypt.hashSync(userData.password, 10),
email: userData.email,
registerDate: new Date()
});
}
user.save(function(err, user) {
if (err) {
return {
success: false,
err: err
}
}
if (socialRegistry){
return user
} else {
console.log(user);
var test = {
success: true,
obj: user
}
return test
}
});
}
Although I agree that this is a basic Async issue, the link prescribed above answers it in a very general way, and is frankly TLDR for this issue here.
Since your processes, like save, are asynchronous, you can't just return something back. Instead, pass a closure / function to the code, and call that function when things have completed. Most of Node is predicated on this kind of usage, for reasons that are TLDR to list here.
createNewUser(req.body,null,function(result){
if (!result.success){
res.status(500).json({
message: "unable to create User",
obj: result.err
})
} else {
res.status(200).json({
message: "successfully created new user",
obj: result.obj
})
}
});
function createNewUser(userData, socialRegistry, onComplete){
....
user.save(function(err, user) {
if (err) {
onComplete({
success: false,
err: err
})
}
if (socialRegistry){
onComplete(user)
} else {
onComplete({
success: true,
obj: user
})
}
});
}
There are some answers that are great for posterity, this may not be one of them, but there is power in simplicity.
Oh, and to answer one of the questions you post in your post, the reason why 'result' never gets set to anything is that your function has already completed and has an implicit return after the async call to user.save().

Promise either never get called, or is rejected (Parse JS SDK)

I am trying to write a function that add or edit some fields on a User object.
The problem come when I try to save the user, if I use user.save, the Promise is rejected with error 206 UserCannotBeAlteredWithoutSessionError.
However, if I get the session id (and documentation about that is scarce), the promise never get resolve, nor rejected. The app seems to just jump to the callback.
My function:
function update(user, callback) {
let query = new Parse.Query(Parse.User);
query.equalTo("username", user.email);
query.find().then(
(users) => {
if(users.length === 0) {
callback('Non existent user');
} else {
let user = users[0];
// user.set('some', 'thing');
console.log('save');
user.save(/*{
sessionToken: user.getSessionToken()
}*/).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR- ' + require('util').inspect(err));
// console.log(callback.toString());
callback(error.message);
}
);
}
},
(error) => {
callback(error.message);
}
);
}
Called with:
var async = require('async'),
baas = require('./baas.js');
async.waterfall([
(callback) => {
callback(null, {
email: 'user#test.com',
password: 'password'
});
},
(user, callback) => {
console.log('connect');
baas.connect(() => { //Initialize the connection to Parse, and declare use of masterKey
callback(null, user);
});
},
(user, callback) => {
console.log('update');
baas.update(user, (err) => {
callback(err);
});
}
], (err) => {
console.log('Error: ' + err);
});
The logs become:
Without session token:
connect
update
save
ERR- ParseError { code: 206, message: 'cannot modify user sA20iPbC1i' }
With session token:
connect
update
save
I do not understand how it is possible that the promise just callback without printing anything, nor why no error are raised anywhere.
Edit:
Following #user866762 advice, I tried to replace the query with Parse.User.logIn and use the resulting User object.
While this solution give me a sessionToken, the end result is the same, parse crash if I don t provide the session token, or give me a error if I do.
According to the Parse Dev guide:
...you are not able to invoke any of the save or delete methods unless the Parse.User was obtained using an authenticated method, like logIn or signUp.
You might also try becoming the user before saving, but I have my doubts that will work.
When you're "get[ting] the session id" my guess is that you're really breaking something. Either Parse is having a heart attack at you asking for the session token, or when you're passing it in save you're causing something there to explode.

MongoDB - Save vs Update for specific fields in document

Using the MEAN stack, I'm attempting to have an admin account update another user's information, in this case, their title/role on the site. The problem I have is that the only function available when editing a user is the save() function. It might be that I can utilize the update function, and if that is the case please let me know, but it doesn't look possible:
The problem arises that when the user is saved, it creates a new document, and overwrites the user's password and salt to some value. I'd like to be able to call an "update" function that will only update the one field, but I can't figure out how to. Is there a way to do this with the save function?
Relevant Code:
exports.updateUserRoles = function(req, res) {
var currUser = req.body;
User.findById(currUser._id, function(err, user) {
//user.roles = currUser.roles;
user.save( { _id : '56467b28ba57d8d890242cfa', roles : 'admin' } );
//THE BELOW WAS A PREVIOUS ATTEMPT
/*user.save( function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(user);
console.log('test2');
}
});*/
});
};
Trying something else that seems very close, but still not quite there yet.
Here's what I'm running:
exports.updateUserRoles = function(req, res) {
var currUser = req.body;
User.findById(currUser._id, function(err, user) {
//user.roles = currUser.roles;
//user.roles.set(0, 'admin');
console.log('test');
user.update(
{ _id: '56467b28ba57d8d890242cfa' },
{
$set: {
roles: 'admin',
},
}
);
console.log('test2');
});
};
Upon hitting the user.update line, we have the user in the local variables, seen:
user.update goes into this Document.prototype.update function, seen:
The args look to be building right, which _id we are targeting and what the action is, seen:
But then after running, nothing seems to change. I'm very much stumped.
For updates various fields in mongodb you can use update with different atomic operators, like $set, $unset, $push etc.
Example:
var updateUserRoles = function(db, callback) {
db.collection('users').updateOne(
{ "_id", : "user_id", },
{ $set: { "password": "new_password" } },
function(err, results) {
console.log(results);
callback();
}
);
};

Mongoose - Create document if not exists, otherwise, update- return document in either case

I'm looking for a way to refactor part of my code to be shorter and simpler, but I don't know Mongoose very well and I'm not sure how to proceed.
I am trying to check a collection for the existence of a document and, if it doesn't exist, create it. If it does exist, I need to update it. In either case I need to access the document's contents afterward.
What I've managed to do so far is query the collection for a specific document and, if it's not found, create a new document. If it is found, I update it (currently using dates as dummy data for this). From there I can access either the found document from my initial find operation or the newly saved document and this works, but there must be a better way to accomplish what I'm after.
Here's my working code, sans distracting extras.
var query = Model.find({
/* query */
}).lean().limit(1);
// Find the document
query.exec(function(error, result) {
if (error) { throw error; }
// If the document doesn't exist
if (!result.length) {
// Create a new one
var model = new Model(); //use the defaults in the schema
model.save(function(error) {
if (error) { throw error; }
// do something with the document here
});
}
// If the document does exist
else {
// Update it
var query = { /* query */ },
update = {},
options = {};
Model.update(query, update, options, function(error) {
if (error) { throw error; }
// do the same something with the document here
// in this case, using result[0] from the topmost query
});
}
});
I've looked into findOneAndUpdate and other related methods but I'm not sure if they fit my use case or if I understand how to use them correctly. Can anyone point me in the right direction?
(Probably) Related questions:
How to check if that data already exist in the database during update (Mongoose And Express)
Mongoose.js: how to implement create or update?
NodeJS + Mongo: Insert if not exists, otherwise - update
Return updated collection with Mongoose
Edit
I didn't come across the question pointed out to me in my searching, but after reviewing the answers there I've come up with this. It's certainly prettier, in my opinion, and it works, so unless I'm doing something horribly wrong I think my question can probably be closed.
I would appreciate any additional input on my solution.
// Setup stuff
var query = { /* query */ },
update = { expire: new Date() },
options = { upsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (!error) {
// If the document doesn't exist
if (!result) {
// Create it
result = new Model();
}
// Save the document
result.save(function(error) {
if (!error) {
// Do something with the document
} else {
throw error;
}
});
}
});
You are looking for the new option parameter. The new option returns the newly created document(if a new document is created). Use it like this:
var query = {},
update = { expire: new Date() },
options = { upsert: true, new: true, setDefaultsOnInsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (error) return;
// do something with the document
});
Since upsert creates a document if not finds a document, you don't need to create another one manually.
Since you wish to refactor parts of your code to be shorter and simpler,
Use async / await
Use .findOneAndUpdate() as suggested in this answer
let query = { /* query */ };
let update = {expire: new Date()};
let options = {upsert: true, new: true, setDefaultsOnInsert: true};
let model = await Model.findOneAndUpdate(query, update, options);
///This is simple example explaining findByIDAndUpdate from my code added with try catch block to catch errors
try{
const options = {
upsert: true,
new: true,
setDefaultsOnInsert: true
};
const query = {
$set: {
description: req.body.description,
title: req.body.title
}
};
const survey = await Survey.findByIdAndUpdate(
req.params.id,
query,
options
).populate("questions");
}catch(e){
console.log(e)
}
Here is an example I am using. I have to return custom responses for UI updates etc. This can be even shorter. User is
const UserScheme = mongoose.Schema({
_id: String,
name: String,
city: String,
address: String,
},{timestamps: true});
const User = mongoose.model('Users', UserScheme);
async function userUpdateAdd(data){
var resp = '{"status": "error"}';
if(data){
var resp = await User.updateOne({ _id: data._id }, data).then(function(err, res){
console.log("database.userUpdateAdd -> Update data saved in database!");
if(err){
var errMessage = err.matchedCount == 0 ? "User Record does not exist, will create new..." : "Record not updated";
// If no match, create new
if(err.matchedCount == 0){
const create_user = new User(data);
resp = create_user.save().then(function(){
console.log("database.userUpdateAdd -> Data saved to database!");
return '{"status":"success", "message": "New User added successfully"}';
});
return resp;
}
// Exists, return success update message
if(err.matchedCount == 1){
return '{"status": "success", "message" : "Update saved successfully"}';
} else {
return '{"status": "error", "code": "' + err.modifiedCount + '", "message": "' + errMessage + '"}';
}
}
})
.catch((error) => {
//When there are errors We handle them here
console.log("database.userUpdateAdd -> Error, data not saved! Server error");
return '{"status": "error", "code": "400", "message": "Server error!"}';
});
}
return resp;
}
Here's an example:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/rsvp', {useNewUrlParser: true, useUnifiedTopology: true});
const db = mongoose.connection;
db.on('error', () => {
console.log('mongoose connection error');
});
db.once('open', () => {
console.log('mongoose connected successfully');
});
const rsvpSchema = mongoose.Schema({
firstName: String,
lastName: String,
email: String,
guests: Number
});
const Rsvp = mongoose.model('Rsvp', rsvpSchema);
// This is the part you will need... In this example, if first and last name match, update email and guest number. Otherwise, create a new document. The key is to learn to put "upsert" as the "options" for the argument.
const findRsvpAndUpdate = (result, callback) => {
Rsvp.findOneAndUpdate({firstName: result.firstName, lastName: result.lastName}, result, { upsert: true }, (err, results) => {
if (err) {
callback(err);
} else {
callback(null, results);
}
})
};
// From your server index.js file, call this...
app.post('/rsvps', (req, res) => {
findRsvpAndUpdate(req.body, (error, result) => {
if (error) {
res.status(500).send(error);
} else {
res.status(200).send(result);
}
})
});

How to create an update function on nodejs/mongodb?

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);
});
}
});
});

Categories

Resources