Why its only updating the second item of the array? - javascript

What im trying to do is to update one or more users with some data.
I receive the user phone numbers in an array, exp: ["+1234555", "+1222222"]. I loop through them and I check if they exist or not.
If they exist I update the data in the user model.
The problem is that let's say I have two users to update. When I execute the function only the second user is updated, the first one is not.
exports.createNotifications= asyncHandler(async (req, res, next) => {
const { userPhone, someData } = req.body;
let createUserData;
let updateUserData;
let findUser;
const createData = async () => {
userPhone.map(async (phone) => {
findUser = await Users.findOne({ phone: phone}).then(
async (user) => {
if (user) {
updateUserData= await Users.findOneAndUpdate(
{ phone: phone},
{ $push: { someData : someData } },
{ new: true }
);
}
}
);
});
};
await createNotifications();
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(updateUserData)
res.status(200).json({
success: true,
});
});
How can I update both users?

It fails because you dont wait for the async functions built in your 'map' (and actually you mess a lot with async, await and promises :p).
Considering that the code you shared is invalid, I assume that createData and createNotifications are the same.
You can wrap this creation into promises and wait for it to complete
const createNotifications = () => {
return userPhone.map(async (phone) => {
const user = await Users.findOne({ phone: phone})
if (user) {
await Users.findOneAndUpdate(
{ phone: phone},
{ $push: { someData : someData } },
{ new: true }
)
}
})
}
Promise.all(createNotifications())
.then(() => {
res.status(200).json({
success: true,
})
})
note: code not tested

userPhone.map(phone => {
let data = await Users.updateMany({
phone
}, {
$push: {
someData: someData
}
}, {
new: true
})
});

Related

How to make your code wait for execution of loop

Following is my getUser function
const getUsers = async (req, res) => {
try {
ddb.get({
TableName: "Tablename",
Key: { Username: req.query.username }
},(err,user) => {
if(err || Object.keys(user).length === 0) {
return res.status(404).json("Something went wrong")
} else {
const userSociety = Object.keys(user.Item.Societies)[0]
ddb.get({
TableName: "Tablename",
Key: { SocietyCode: userSociety }
},(err, society) => {
if(err || Object.keys(society).length === 0) {
return res.status(400).json({ message: "Could not fetch society members" })
} else {
const users = Object.keys(society.Item.SocietyMembers)
const usersData = []
users.forEach(async u => {
ddb.get({
TableName: "TestMyMohallaUsers",
Key: { Username: u }
},async (err,user) => {
if(err || Object.keys(user).length === 0) {
} else usersData.push({
Username: user.Item.Username,
Firstname: user.Item.Name
})
})
})
return res.status(200).json({ message: "Data detched successfully", Users: usersData })
}
})
}
})
} catch (error) {
return res.status(500).json({ error: "Internal Server Error" })
}
}
I want to wait for the execution of forEach and then send back the data via return statement but as of now the return statement gives empty array of users.
Clearly my code in not waiting for the execution of forEach and then returning the data. How can I do that someone help me?
Edit: ddb is an instance of DynamoDB
You'll have a better time if you
use the DynamoDB Promise API instead of a pyramid of callbacks
refactor your code to a couple of functions
Finally, awaiting for all user fetches to complete requires Promise.all for all of those promises.
async function getUser(ddb, username) {
const user = await ddb
.get({
TableName: "TestMyMohallaUsers",
Key: { Username: username },
})
.promise();
if (!user.Item) {
throw new Error(`User ${username} not found`);
}
return user.Item;
}
async function getSociety(ddb, societyCode) {
const society = await ddb
.get({
TableName: "Tablename",
Key: { SocietyCode: societyCode },
})
.promise();
if (!society.Item) {
throw new Error(`Society ${societyCode} not found`);
}
return society.Item;
}
const getUsers = async (req, res) => {
try {
const user = await getUser(ddb, req.params.username);
const userSocietyCode = Object.keys(user.Societies)[0];
const society = await getSociety(ddb, userSocietyCode);
const societyUsers = Object.keys(society.SocietyMembers);
const usersData = await Promise.all(
societyUsers.map(async (member) => {
const user = await getUser(ddb, member);
return {
Username: user.Username,
Firstname: user.Name,
};
}),
);
return res
.status(200)
.json({
message: "Data detched successfully",
Users: usersData,
});
} catch (e) {
return res
.status(400)
.json({ message: `Could not fetch information: ${e}` });
}
};

Error in updating profile with image using mongoose and cloudinary

updateProfile: async function(req, res) {
try {
const update = req.body;
const id = req.params.id;
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
const image = req.files.profileImage;
const cloudFile = await upload(image.tempFilePath);
const profileImage = cloudFile.url
console.log('Loging cloudfile', profileImage)
await User.updateOne(id, { update }, { profileImage }, { new: true },
function(err, doc) {
if (err) {
console.log(err)
}
if (doc) {
return res.status(200).send({ sucess: true, msg: 'Profile updated successful' })
}
});
} catch (error) {
res.status(500).json({ msg: error.message });
}
}
But I'm getting an error of "Callback must be a function, got [object Object]"
I have tried to $set: update and $set: profileImage but still not working.
So the image successful upload into the cloudinary but the update for mongoose is not working.
Upon brief research into the issue, I think you are feeding the arguments in wrong. Objects can be confusing but not to worry.
Your code is:
await User.updateOne(id, { update }, { profileImage }, { new: true }
However, I believe it should be something more like:
await User.updateOne({id: id}, { profileImagine: profileImage, new: true },
The API reference annotates use of the function as:
const filter = { name: 'John Doe' };
const update = { age: 30 };
const oldDocument = await User.updateOne(filter, update);
oldDocument.n; // Number of documents matched
oldDocument.nModified; // Number of documents modified

React useEffect is not triggering on redirect

i have a function called login that redirects the user to the main page if everything was ok. Then, on the main page, i want to fetch some user info with useEffect using the token the was stored when the user logged in, but nothing happens. Only when i refresh the page i get the data.
login function
export const login = ({ email, password, history }) => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:5000/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await response.json();
if (data.status === 200) {
localStorage.setItem("userToken", data.user);
history.push("/");
} else {
dispatch(
setNotification({
variant: "error",
message: data.message,
})
);
}
} catch (e) {
console.log(e.message);
}
};
};
fetch user funtion
export const fetchUser = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:5000/userInfo", {
headers: {
"x-access-token": localStorage.getItem("userToken"),
},
});
const data = await response.json();
dispatch(setUser({
id: data.id,
fullname: data.fullname,
email: data.email
}))
} catch (error) {}
};
};
useEffect on my main page
useEffect(() => {
dispatch(fetchUser());
}, []);
backend function
module.exports.getCurrentUser = async (req, res) => {
const token = req.headers["x-access-token"];
try {
const verifyToken = jwt.verify(token, "123");
const user = await User.findOne({ email: verifyToken.email });
return res.json({
id: user._id,
fullname: user.fullname,
email: user.email
})
} catch (error) {}
};
The 2nd parameter to useEffect tells it when it needs to run. It only runs if one of the values in the array has changed. Since you pass an empty array, none of the values in it have changed.
This is presuming your app probably starts at '/', then detects there is no user so switches to the login screen. When it goes back to the root, it only executes useEffect if something in the array has changed from the previous render.
As it is, the isMounted doesn't make much sense. This could simply be:
useEffect(() => {
dispatch(fetchUser());
});
You're calling setUser, but what is calling your login function?

How to execute bcrypt.compare inside Sequelize .then?

I'm trying to build a login page where I get the hashed password from mysql db using Sequelize and then calling bcrypt compare to dehash the password and compare it with the user's login input for authentication.
However, bcrypt compare is always executing slower than the return causing the value to always be "". I know this has to do with asynchronous behaviour but I don't know how to properly write this code to make it work.
authenticate: (req, res) => {
let userDetails = req.query;
User.findOne({
where: {
username: userDetails.username
}
})
.then((user) => {
// How can I make this so, correctPassword() finishes
// and then the authenticated variable will be either false or true?
let authenticated = correctPassword(userDetails.password, user.password);
return authenticated;
})
.then((authenticated) => {
// right now authenticated is "" in client side console.
res.send(authenticated);
})
.catch((error) => {
console.log('there was an error: ', error);
});
}
}
const correctPassword = (enteredPassword, originalPassword) => {
return bcrypt.compare(enteredPassword, originalPassword, (err, res) =>{
return res;
});
}
You're almost there. You correctly intuited that correctPassword executes asyncronously, though it is written as if it's syncronous.
First off, let's make correctPassword a promise, so we can use async/await or call .then on it
const correctPassword = (enteredPassword, originalPassword) => {
return new Promise(resolve => {
bcrypt.compare(enteredPassword, originalPassword, (err, res) =>{
resolve(res)
});
})
}
Next, you have two approaches to ensure the order of operations in your code executes correctly:
(Recommended) Use async/await syntax allowing us to write synchronous-looking code:
authenticate: async (req, res) => {
let userDetails = req.query;
try {
const user = await User.findOne({
where: {
username: userDetails.username
}
});
const authenticated = await correctPassword(userDetails.password, user.password);
res.send(authenticated);
} catch(e) {
res.status(400).send(e)
}
}
Continue using promises:
authenticate: (req, res) => {
let userDetails = req.query;
User.findOne({
where: {
username: userDetails.username
}
}).then(() => {
correctPassword(userDetails.password, user.password)
.then(authenticated => {
res.send(authenticated)
})
.catch(e => {
res.send(e)
})
})
}
You can't assign async function to variable which is used by sync code later on. If you want to do sync function, you can use await/aync. But in here I recommend you use promise for compare function as well.
User.findOne({
where: {
username: userDetails.username
}
})
.then((user) => {
return correctPassword(userDetails.password, user.password);
})
.then((authenticated) => {
res.send(authenticated);
})
Bcrypt also supports promise.
const correctPassword = (enteredPassword, originalPassword) => {
return bcrypt.compare(enteredPassword, originalPassword).then((res) =>{
return res;
});
}

How to wait on sequelize executing a findOne

I've got a route using Sequelize.js
app.get('/api/users/:username', (req, res) => {
const foundUser = getUserByUsername(req.params.username);
console.log(`foundUser = ${foundUser}`);
return res.send(foundUser);
});
the getUserByUsername function is as follows
const getUserByUsername = username => {
Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return response.dataValues;
});
};
I hoped on getting the object in my const foundUser in my route, but it seems I need to wait until the findOne has been executed, because in my console I can see that the log of foundUser (which is undefined then) is executed before the function getUserByUsername
foundUser = undefined
Executing (default): SELECT `id`, `username`, `instakluiten`, `role`, `createdAt`, `updatedAt` FROM `viewers` AS `viewer` WHERE `viewer`.`username` = 'instak' LIMIT 1;
{ id: 19,
username: 'instak',
instakluiten: 18550,
role: 'moderators',
createdAt: 2016-10-02T16:27:44.000Z,
updatedAt: 2016-10-09T10:17:40.000Z }
How can I make sure that my foundUser will be updated with the data áfter the user has been found?
You have to return the promise that Sequelize creates and then wait for it to resolve. So the getUserByUsername becomes:
const getUserByUsername = username => {
return Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return response.dataValues;
});
};
and in the route:
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username).then(foundUser => {
res.send(foundUser);
});
});
This is because you need to keep the chain of promises. If you forget to return it, the function returns undefined end even if the promise is finallly resolved, the value it resolves to never gets up back in the chain.
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username, function(err, result){
const foundUser = result;
console.log(`foundUser = ${foundUser}`);
res.send(foundUser);
});
});
const getUserByUsername = function(username, callback) {
Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return callback(null, response.dataValues);
});
};
You can avoid it with promise or with callback
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username, function(err, foundUser) {
if (!err) {
console.log(`foundUser = ${foundUser}`);
return res.send(foundUser);
} else {
res.send(err)
}
});
});
const getUserByUsername = (username, callback) => {
Viewer.findOne({
where: {
username
}
}).then(response => {
console.log(response.dataValues); //the object with the data I need
return callback(null, response.dataValues);
});
};

Categories

Resources