loopback after delete customize the result sent to client - node.js

In my loopback application i have to customize the delete result sent to the client.
xxxxx.observe('before delete', function(ctx, next) {
xxxxx.find({
where: {
id: ctx.where.id
}
}, function (err, transaction) {
if(yyyy.length > 0){
var id = yyyy[0]['id'];
pgclient.query("delete from qqq where kkkk = '"+id+"' ", function(err, dbRes){
if(err){
next(err);
} else{
next();----------------> here i have to customize the result
}
});
} else {
next({"message":" for the given id"});
}
});
});
Now the result sent to client is:
{
"count": 1
}
But i need to modify this as:
{"message":"Deleted Successfully"}.
I tried a lot regarding this, but no result. Please share your ideas.

You can't. This is operation hook and next callback is just for determining successful/failure of operation.
For customizing messages you should do in remote method or remote hooks

Related

Node.js stops listening (gets stuck) after executing query more than 6 times

I am using expressJs, This is the query which I am executing it multiple time
router.post('/Index_api/Reminder/Delete',function(req, res)
{
Reminder_Id = req.body;
DeleteRow('Reminders','Reminder_Id',Reminder_Id.Reminder_Id);
});
int this is the DeleteRow Function
function DeleteRow(TableName,WCC, id)
{
var query ="Delete FROM `"+TableName + "` WHERE `"+ WCC +"` =" + id;
conn.query(query,function(err,result)
{
if(err)
{
console.error(err);
return;
}else{
console.log(result);
}
});
}
I am posting data to this route like this:
function DeleteRow(id)
{
$.post('/Index_api/Reminder/Delete',{
Reminder_Id:id
});
$("#row"+id).remove();
}
if I want to delete 6 records together there is no problem but by the 7th one is not executed and gets stuck.
I am also using nodemon and reload package.
Your router.post() route handler is not returning any response. Thus, the browser is still waiting for some sort of response. The browser has a maximum number of connections it will make to any given host so once you have too many connections all sitting there waiting for a response, then the browser queues the next requests until one of the prior ones finishes.
Eventually those requests will time out, but that can take a long time.
To fix, just send a response from your route handler:
router.post('/Index_api/Reminder/Delete',function(req, res) {
Reminder_Id = req.body;
DeleteRow('Reminders','Reminder_Id',Reminder_Id.Reminder_Id);
res.send("ok");
});
Or, if you want the post to actually respond after DeleteRow() is done, you can let DeleteRow() send the right response by passing res to it and letting it send the response.
router.post('/Index_api/Reminder/Delete',function(req, res) {
Reminder_Id = req.body;
DeleteRow('Reminders','Reminder_Id',Reminder_Id.Reminder_Id, res);
});
function DeleteRow(TableName, WCC, id, res) {
var query = "Delete FROM `" + TableName + "` WHERE `" + WCC + "` =" + id;
conn.query(query, function(err, result) {
if (err) {
console.error(err);
res.status(500).send("delete failed");
} else {
res.send("delete OK");
}
});
}
Then, you should probably also change your client code to actually look at the returned status and act accordingly.
function DeleteRow(id) {
$.post('/Index_api/Reminder/Delete',{
Reminder_Id:id
}).then(function(result) {
if (result.status == 200) {
// successful
$("#row"+id).remove();
} else {
// handle server error here
}
}, function(err) {
// network error here
});
}

Mongo DB concurrency issue with findOne and updateOne

I am having an issue with concurrent requests that are updating the same document. I'm not using findAndModify() because I need to access the current state of the document to make the update which I don't see supported with findAndModify(). I also would like to avoid using db.fsyncLock() since that locks the entire database and I only need to lock one document in one collection.
First I use findOne() to get a document, then I use the updateOne() in the callback of findOne() to update the same document. When I queue up a bunch of actions and run them all at once I believe they are all accessing the same state when they call findOne() instead of waiting for the updateOne() to complete from the previous action.
How should I handle this?
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
console.log(err);
return;
}
if (!result.UndoableNoteList.future.length) {
console.log("Nothing to redo");
return;
}
let past = result.UndoableNoteList.past.concat(Object.assign({},result.UndoableNoteList.present));
let present = Object.assign({},result.UndoableNoteList.future[0]);
let future = result.UndoableNoteList.future.slice(1, result.UndoableNoteList.future.length);
db.collection("notes").updateOne(
{path: noteId},
{
$set: {
UndoableNoteList: {
past: past,
present: present,
future:future
}
}
},
(err, result)=> {
if (err) {
console.log(err);
return;
}
}
)
}
);
});
As updateOne() is an async call, findOne() won't wait for it to complete and hence there can be situations where the same document is updated simultaneously, which won't be allowed in mongo.
I think updateOne() is not necessary in this case. Note that you have already found the right instance of the document which needs to be updated in findOne() query. Now, you can update that instance and save that document without doing updateOne(). I think the problem can be avoided this way:
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
console.log(err);
return;
}
if (!result.UndoableNoteList.future.length) {
console.log("Nothing to redo");
return;
}
let past = result.UndoableNoteList.past.concat(Object.assign({},result.UndoableNoteList.present));
let present = Object.assign({},result.UndoableNoteList.future[0]);
let future = result.UndoableNoteList.future.slice(1, result.UndoableNoteList.future.length);
result.UndoableNoteList.past = past;
result.UndoableNoteList.present = present;
result.UndoableNoteList.future = future;
//save the document here and return
}
);
});
Hope this answer helps you!
I was not able to find a way to sequentially run the queries using purely mongodb functions. I've written some node.js logic that blocks mongodb queries from running on the same document and adds those queries to a queue. Here's what the code currently looks like.
The Websocket Undo Listener
module.exports = (noteId, wsHelper, noteWebSocket) => {
wsHelper.addMessageListener((msg, ws)=> {
if (msg.type === "UNDO") {
noteWebSocket.broadcast(msg, noteWebSocket.getOtherClientsInPath(noteId, wsHelper));
noteWebSocket.saveUndo(noteId);
}
});
};
The saveUndo function called from the listener
saveUndo(noteId) {
this.addToActionQueue(noteId, {payload: noteId, type: "UNDO"});
this.getNoteByIdAndProcessQueue(noteId);
}
The getNoteByIdAndProcessQueue function called from saveUndo
getNoteByIdAndProcessQueue(noteId) {
if (this.isProcessing[noteId])return;
this.isProcessing[noteId] = true;
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
this.isProcessing[noteId] = false;
this.getNoteByIdAndProcessQueue(noteId);
return;
}
this.processQueueForNoteId(noteId, result.UndoableNoteList);
});
});
}
The processQueueForNoteId function
processQueueForNoteId(noteId, UndoableNoteList) {
this.actionQueue[noteId].forEach((action)=> {
if (action.type === "UNDO") {
UndoableNoteList = this.undoNoteAction(UndoableNoteList);
} else if (action.type === "REDO") {
UndoableNoteList = this.redoNoteAction(UndoableNoteList);
} else if (action.type === "ADD_NOTE") {
UndoableNoteList = this.addNoteAction(UndoableNoteList, action.payload);
} else if (action.type === "REMOVE_NOTE") {
UndoableNoteList = this.removeNoteAction(UndoableNoteList, action.payload);
}
});
let actionsBeingSaved = this.actionQueue[noteId].concat();
this.actionQueue[noteId] = [];
mongoDBPromise.then((db)=> {
db.collection("notes").updateOne(
{path: noteId},
{
$set: {
UndoableNoteList: UndoableNoteList
}
},
(err, result)=> {
this.isProcessing[noteId] = false;
// If the update failed then try again
if (err) {
console.log("update error")
this.actionQueue[noteId] = actionsBeingSaved.concat(this.actionQueue[noteId]);
}
// if action were queued during save then save again
if (this.actionQueue[noteId].length) {
this.getNoteByIdAndProcessQueue(noteId);
}
}
)
});
}

Loopback: Where does one put observers?

I'm relatively new to the loopback game. How can I get observers to work?
For example, I want something to observe whenever user information is changed or a user is created.
Thanks
//this observer will be activated whenever the user is edited or created
User.observe('after save', function(ctx, next) {
var theUserObject = ctx.instance;
if(ctx.isNewInstance){
anotherModel.create(theUserObject.name,theUserObject.ID);
}else{
anotherModel.update(theUserObject.name,theUserObject.ID);
}
next();
});
Is this the correct user of ctx? Where should this code sit? Within the User.js?
Just to put this in answer (see comments above):
In general what you are doing is mostly correct. You want to put operation hooks in the common/models/my-model.js file(s), but that context object (ctx) will change depending on the hook (read the linked documentation above).
In your case, to create a new model, you need to access the app off of the current model and then execute create(), but be sure to put your next() callback in the callback for the create call:
//this observer will be activated whenever the user is edited or created
User.observe('after save', function(ctx, next) {
var theUserObject = ctx.instance;
if(ctx.isNewInstance){
User.app.models.anotherModel.create({name: theUserObject.name, id: theUserObject.ID}, function(err, newInstance) {
next(err);
});
} else {
User.app.models.anotherModel.find({ /* some criteria */ }, function(err, instance) {
if (err) { return next(err); }
if (instance) {
instance.updateAttributes({name: theUserObject.name, id: theUserObject.ID}, function(err) {
next(err);
});
}
});
}
});

MongoDB: Waiting for available socket hanging

So I am using Mongoose's findOneAndUpdate to update a key value in a document. I am needing to increment that number by one. If it's not in there it should create a new document. It's in a post route in Node.
I have information going to the post route on a button click. My app requires users to click a button multiple times in succession and i'm getting a Waiting for available sockets... at the bottom of my browser and everything hangs.
I am just doing this on my localhost. At the end of the request once everything "unhangs" I get the following error in my console:
POST http://localhost:3000/api/songsqueued net::ERR_EMPTY_RESPONSE
This is the post route i'm working with:
router.post('/api/songsqueued', function(req, res){
Song.findOneAndUpdate({songId: req.body.songId}, { $inc:{queueTimes: 1} },
function(err, song){
if (err) {
return console.log("Error: " + err)
}
if(song){
song.queueTimes += 1;
song.save(function(err){
if(err){
console.log(err)
} else {
console.log("updated fam")
}
});
} else {
var song = new Song();
song.artistName = req.body.artistName;
song.titleName = req.body.titleName;
song.songId = req.body.songId;
song.songImg = req.body.songImg;
song.save(function(err) {
if (err) {
console.log("Error: " + err)
} else {
console.log("created fam")
}
})
console.log(song);
return res.json({message: "SongCreated"})
}
})
})
Any ideas on how to remedy this?
Your node server needs to send a response to the client in order for the response to complete. If you don't send a response, the connection will timeout and you get the socket hangup. Make sure that you send a response for each condition:
if (err) {
return res.status(500).end("some error message");
}
else if (song) {
return res.status(200).end("updated");
}
else {
return res.status(201).end("created");
}

How to make transactions with sails.js/waterline?

I am trying to put my queries into transaction and I am failing in runtime. Error I am getting is :
Object #<bound> has no method 'transaction'
I tried to follow this "documentation".
In short my model looks like that :
updateOrCreate: function (profile_id, positive,negative) {
var deferred = Q.defer();
Reputation.transaction().findOne().where({profile: profile_id}).then(function (rep) {
if (rep) {
// Reputation logic
rep.save(function (err) {deferred.resolve();});
} else {
// Reputation does not exist. Create.
Reputation.create({profile: profile_id, positive: positive,negative:negative}).exec(function (e, rep) {
deferred.resolve();});
}
}).fail(function (err) {deferred.reject()});
return deferred.promise;
}
any ideas what did I do wrong?
thanks.
w.
This is now supported in sails v1 (not official release yet at June 26, 2017).
You can follow this link for documentation in next.sailsjs.com:
Datastore.transaction()
In doc above is the following example:
sails.getDatastore()
.transaction(function (db, proceed) {
BankAccount.findOne({ owner: req.session.userId }).usingConnection(db)
.exec(function (err, myAccount) {
if (err) { return proceed(err); }
if (!myAccount) { return proceed(new Error('Consistency violation: Database is corrupted-- logged in user record has gone missing')); }
BankAccount.findOne({ owner: req.param('recipientId') }).usingConnection(db)
.exec(function (err, recipientAccount) {
if (err) { return proceed(err); }
if (!recipientAccount) {
err = new Error('There is no recipient with that id');
err.code = 'E_NO_SUCH_RECIPIENT';
return proceed(err);
}
// Do the math to subtract from the logged-in user's account balance,
// and add to the recipient's bank account balance.
var myNewBalance = myAccount.balance - req.param('amount');
// If this would put the logged-in user's account balance below zero,
// then abort. (The transaction will be rolled back automatically.)
if (myNewBalance < 0) {
err = new Error('Insufficient funds');
err.code = 'E_INSUFFICIENT_FUNDS';
return proceed(err);
}
// Update the current user's bank account
BankAccount.update({ owner: req.session.userId })
.set({
balance: myNewBalance
})
.usingConnection(db)
.exec(function (err) {
if (err) { return proceed(err); }
// Update the recipient's bank account
BankAccount.update({ owner: req.param('recipientId') })
.set({
balance: recipientAccount.balance + req.param('amount')
})
.usingConnection(db)
.exec(function (err) {
if (err) { return proceed(err); }
return proceed();
});
});
});
});
}).exec(function(err){
// At this point, we know that, if our code above passed through
// an error to `proceed`, Sails took care of rolling back the
// transaction. Otherwise, it committed it to the database.
if (err && err.code === 'E_INSUFFICIENT_FUNDS') {
return res.badRequest(err);
}
else if (err && err.code === 'E_NO_SUCH_RECIPIENT') {
return res.notFound();
}
else if (err) {
return res.serverError(err);
}
// All done!
return res.ok();
});
The "documentation" you're following is a proposal for how transaction support could be added to Sails. There is no native transaction support in Sails. See this answer for an example of how to use the .query method for the MySQL or Postgres adapters to perform transactions.
Seems they don't support this. You could use something like:
https://github.com/Shyp/pg-transactions
https://github.com/postmanlabs/sails-mysql-transactions

Resources