I am working on an e-commerce project. admin wants to change the user status on a click I tried a way to update the status of the user
<td id="status" onclick="changeStatus('<%=user._id%>' , '<%=user.status%>')"></td>
async function changeStatus(id , status){
try{
const res = await fetch("/admin/customers" , {
method:"POST",
body:JSON.stringify({id , status}),
headers:{"Content-Type":"application/json"}
})
const data = await res.json()
if(data.message.includes("success")){
location.reload()
}
}
catch(err){
console.log(err)
}
}
and in my serverside i tried update my status
customer_status: async (req , res) => {
let {id, status} = req.body
try{
const updateStatus = await User.findByIdAndUpdate({_id:id} , {$set:{status:!status}} , {new:true})
console.log(updateStatus)
res.json({message:"success"})
}
catch(err){
console.log(err)
}
},
It works for the first time for a user anyone can explain why it's not updating simontionously
We should use $not aggregation for toggling boolean:
User.findByIdAndUpdate({ _id: id}, { $set: { status: { $not: "$status" }}}, { new: true })
Also note that some value cannot be casted to true or false, eg empty string ''. In this case we have to tell Mongo how to handle, below snippet is for mongoose:
const mongoose = require('mongoose');
mongoose.Schema.Types.Boolean.convertToFalse.add('');
Related
I hope you are well, I have a problem, I am learning NEXTJS and working with its api, and it turns out that when I click very fast the promises get bugged. Or something similar, I leave the tests in this short video of 30s and a demonstration of the code, I hope someone can help me, thank you very much.
https://www.youtube.com/embed/K0JTMxpFLQs
------------ CODE ----------
MY REQUEST AXIOS:
axios.post("/api/reaction", data).then((response) => { return response.data }).then(response => {
if (response.react) {
setNew[key].youLike = true
setNew[key].likes = response.likes
setting(setNew => [...setNew])
} else {
setNew[key].youLike = false
setNew[key].likes = response.likes
setting(setNew => [...setNew])
}
})
My backend:
export default async function react(req, res) {
let msg, msg_type, react, likes
let { type, id } = req.body
let myId = mySession(req)
if (myId && type && id) {
react = await youLike(myId, id, type)
console.log(react)
if (react) {
await executeQuery({ query: `DELETE FROM cms_likes WHERE owner_id='${myId}' and type='${type}' and post_id='${id}'` })
await executeQuery({ query: `UPDATE cms_posts SET likes=likes-1 WHERE id='${id}' ` })
react = false
} else {
await executeQuery({ query: `INSERT INTO cms_likes (owner_id,type,post_id,time) VALUES ('${myId}','${type}','${id}','${timeNow("s")}') ` })
await executeQuery({ query: `UPDATE cms_posts SET likes=likes+1 WHERE id='${id}' ` })
react = true
}
likes = await executeQuery({ query: `SELECT likes FROM cms_posts WHERE id='${id}'` })
} else {
msg_type = "error"
msg = "!Opps error al enviar tu peticion"
}
res.send({ msg, msg_type, react, likes: likes[0]?.likes })
}
By clicking the button many times, the like button doesn't do the promise to check if it exists or not very well, and doubles the likes.
EXISTS SLOW CLIC
slowclick
response:
0 no found
1 Yes there are
0 no found
1 Yes there are
0 no found
FAST CLIC RESPONSE:
response
2 DUPLICATE
2 DUPLICATE
0 no found
1 Yes there are
0 no found
1 Yes there are
0 no found
3 duplicate
Understand how promises work well, in order to find a solution to this problem, and my post will help people who may have the same error.
My connection SQL and FUNCTION EXECUTEQUERY
const db = mysql({
config: {
host: "localhost",
database: "baalbcms",
user: "root",
password: ""
}
})
export default async function executeQuery({ query, values }) {
try {
const results = await db.query(query, values)
await db.end()
return results
} catch (err) {
return err;
}
}
To make your SQL more robust, you should always count the actual number of likes based on the likes table; as it is, it's possible for those to get out of sync.
I also took the liberty of fixing the SQL injection vulnerabilities in your code by using values.
This would be better still if you ran the queries in a single transaction, but that can't be easily done with your current db that you end after every query (which you shouldn't do).
if (react) {
await executeQuery({
query: "DELETE FROM cms_likes WHERE owner_id=? and type=? and post_id=?",
values: [myId, type, id],
});
} else {
await executeQuery({
query: "INSERT INTO cms_likes (owner_id,type,post_id,time) VALUES (?, ?, ?, ?)",
values: [myId, type, id, timeNow("s")],
});
}
await executeQuery({
query: "UPDATE cms_posts SET likes=(SELECT COUNT(*) FROM cms_likes WHERE type=? and post_id=?) WHERE id=?",
values: [type, id, id],
});
likes = await executeQuery({ query: `SELECT likes FROM cms_posts WHERE id=?`, values: [id] });
This is the basic structure of the Schema I am working with using mongoose:
const User = {
uid: {
type: String
},
routes: {
type: Array
}
}
In my application there is a POST to /route, in which uid and a new route are provided as "body parameters". In order to add to the routes array, I wrote a code similar to this (the only diference is that I check if the route already exists):
var user = await User.find({uid: uid}) // user is found, as expected
user[0].routes.push(route //parameter)
user.save()
When a POST request is made, though, it throws an error:
TypeError: user.save is not a function
What am I doing wrong?
user in your code is an array of documents
so you'll have mongo documents inside that array
you can't do array.save, you've to do document.save
await user[0].save()
var user = await User.find({uid: uid}) // user is found, as expected
if (user && user.length) {
user[0].routes.push(route //parameter)
await user[0].save(); // save the 1st element of the object
}
if your query returns only 1 record better use https://mongoosejs.com/docs/api.html#model_Model.findOne
var user = await User.findOne({uid: uid}) // user is found, as expected
if (user) {
user.routes.push(route //parameter)
await user.save(); // save the 1st element of the object
}
if you need to find only one specific user you should use findOne function instead
User.findOne({uid: uid})
.then(
(user) => {
user[0].routes.push(route //parameter);
user.save();
},
(err) => {
console.error(err);
}
)
I think bulkSave() can be what you're looking for:
var user = await User.find({uid: uid}
enter code user[0].routes.push(route //parameter)
await User.bulkSave(user)
In my code, I am sending a query to my Mongo database. The method findUser() shall return the response of this query. The query works fine, tested with console.log(users).
The problem is the function returns null, it doesn't wait till the query got a response to return the var foundUser.
How could I use await/async in this case in order to wait for the query response before returning anything ?
function findUser(username) {
foundUser = null
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology : true});
client.connect(err => {
const collection = client.db("YourCV").collection("Accounts");
result = collection.findOne({username : username }, function(err, user) {
console.log(user)
if(user){
foundUser = user
}
});
});
return foundUser
};
console.log(user) outputs :
{
_id: 601695084b28102500ae0015,
username: 'jetlime',
password: '$2b$10$EN5k/YKOMBgibqy62s0hGOX9MffHZtXkfsw0Du0j8QVS7mGab5FLi'
}
Many thanks
Update the code to the following:
async function findUser(username) {
const client = await MongoClient.connect(url, { useNewUrlParser: true })
.catch(err => { console.log(err); });
if (!client) {
return;
}
const collection = client.db("YourCV").collection("Accounts");
const user = await collection.findOne({username : username });
client.close(); // -> To be under finally clause.
return user;
};
And call the function with await findUser(username);
Caution: The above of connecting to DB is not recommended. You are establishing a DB connection for every function call. This quickly leads to running out of connections on the DB side when you have a large number of requests.
Move the DB connection establishing part to a commonplace and re-use the connection.
See whatever you do, any operation you perform with your database, it returns promise, so write async keyword before the name of your function in line 1 and then await keyword before your query in line 6, it will then behave like synchronous call and every line will execute accordingly
As you said, you need to use async/await operators for asynchron methods.
You have two choices:
1)Implement callback method on findUser
async function findUser(username, onUsersReady) {
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
}).connect();
const collection = await client.db("YourCV").collection("Accounts");
await collection.findOne({
username: username
}, function(err, user) {
console.log(user)
if (user) {
foundUser = user
onUsersReady(user);
}
});
};
2)Use function to return results directly
async function findUser(username) {
const client = await new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
}).connect();
const collection = await client.db("YourCV").collection("Accounts");
const foundUser = await collection.findOne({
username
});
return foundUser;
}
I have this request:
// PUT that updates a user.
router.put('/api/user/:id', async (req: Request, res: Response) => {
const { email, name, avatar } = req.body
const userId = req.body._id
const conditions = {
_id : userId
}
const user = {$set: { "email": email, "name": name, "avatar": avatar } }
User.updateOne(conditions, user).then(doc => {
if (!doc) { return res.status(404).end() }
return res.status(200).json(doc)
}).catch(error => console.log(error))
})
And I get this response from the request:
{
"n": 0,
"nModified": 0,
"ok": 1
}
If you can find it on StackOverflow about the updateOne() method in mongoose I've probably tried it. The document isn't updating no matter what I try.
Edit: I've tried using an ObjectID in the query instead and the same result.
Edit 2: I figured it out. Was using req.body.id instead of req.params.id and I was using parameters to send the request. Thanks everyone for the help!
nModified == 0 implies that you have no user matching this id,
your route is put /api/user/:id but your user id is in req.params.id and not in req.body._id
A couple tips:
Try running the same query from mongodb at the command line, see if you get any results.
Is the "campaign_id" defined as an ObjectId in your schema? If so, try searching using the ObjectId type.
Try to change the query to :
const ObjectId = require('mongoose').Types.ObjectId;
const conditions = {
_id : new ObjectId(userId)
}
The reason for not updating is - mongoose is unable to search the with the id you provided.
if you want to update a document based on _id you can use findByIdAndUpdate()
const userId = req.body._id;
const user = { "email": email, "name": name, "avatar": avatar }
User.findByIdAndUpdate(userId , user,
function (err, docs) {
if (err){
console.log(err)
}
else{
console.log("Updated User : ", docs);
}
});
In case you've set your DB to strict mode don't forget to add strict:false in options when adding new keys. Otherwise, inserts will be silently ignored. I've just spent 2 hours wondering why my inserts don't get saved in DB despite not throwing any error.
See dos
http://mongoosejs.com/docs/guide.html#strict
const conditions = {
_id
}
const dateToUpdate = {
$set: {
"email": "email",
"name": "name",
"avatar": "avatar"
}
}
const updateRecord = await models.pdDealModel.updateOne(conditions,dateToUpdate,{
upsert:false,
strict:false
}
)
My issue is that (seemingly) things are going out of scope, or the scope is being polluted when I enter my catch block in the function below:
export const getOne = model => async (req, res, next) => {
let id = req.params.id
let userId = req.user
try {
let item = await model.findOne({ _id: id, createdBy: userId }).exec()
if (!item) {
throw new Error('Item not found!')
} else {
res.status(200).json({ data: item }) // works perfectly
}
} catch (e) {
res.status(400).json({ error: e }) // TypeError: res.status(...).json is not a function
// also TypeError: next is not a function
// next(e)
}
}
Interestingly enough, using res.status(...).end() in the catch block works just fine, but it bothers me that I am not able to send any detail back with the response. According to the Express Documentation for res.send() and res.json I should be able to chain off of .status(), which, also interestingly enough, works just fine in the try statement above if things are successful - res.status(200).json(...) works perfectly.
Also, I tried abstracting the error handling to middleware, as suggested on the Express documentation, and through closures, I should still have access to next in the catch statement, right? Why is that coming back as not a function?
Why does res.status(...).json(...) work in my try but not catch block?
Why is next no longer a function in the catch block?
Thanks in advance!
Edit
This is failing in unit tests, the following code produces the errors described above:
describe('getOne', async () => {
// this test passes
test('finds by authenticated user and id', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const list = await List.create({ name: 'list', createdBy: user })
const req = {
params: {
id: list._id
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(200)
return this
},
json(result) {
expect(result.data._id.toString()).toBe(list._id.toString())
}
}
await getOne(List)(req, res)
})
// this test fails
test('400 if no doc was found', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const req = {
params: {
id: mongoose.Types.ObjectId()
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(400)
return this
},
end() {
expect(true).toBe(true)
}
}
await getOne(List)(req, res)
})
})
Why does res.status(...).json(...) work in my try but not catch block?
Seems like you're passing a non-express object that only has status & end methods when running using the unit testing. That's why it fails to find the json method