The code below forms part of a websocket that connects to a third party API and receives messages quite frequently from it, currently there are about 3 messages that all appear in very quick succession and I was wondering how I could make sequelize wait until executing one query before the next
this.client.ws.on(WebSocketEvent.ON_MESSAGE, async (message) => {
if (message.type == 'received') {
await db.Order.create({
symbol: message.product_id,
orderId: message.order_id,
price: message.funds,
side: message.side,
orderedAt: message.time,
sold: false
}).catch(err => {
console.log(err);
process.exit();
});
} else if (message.type == 'match') {
await db.Order.update({
volume: message.size
}, {
where: {
orderId: message.taker_order_id
}
}).catch(err => {
console.log(err);
});
} else if (message.type == 'done') {
console.log(message);
await db.Order.update({
completeAt: message.time
}, {
where: {
orderId: message.order_id
}
}).catch(err => {
console.log(err);
});
}
});
Basically what should happen is a message should come through with the type received and that creates a row in the Order table, a split second later a match message should come through and theoretically it should wait for the previous query to run before updating the previously created order, although this is not the case and the model is not updated.
I've thought about simply adding a delay to the code blocks below the match and done message types but would there be any other way to implement what I am looking for?
Related
I am developing a portal for meeting room booking similar to the one available on Power Apps. I want to call graph API to get the availability of each rooms in the tenet at the filtered time provided by the user. But it only provides result for 3-4 rooms and HTTP 429 error for the rest of them.
I have even tried it using debounce but it only gives 1 result. Below is my code snippet
private async _getRooms(item: RoomListInfo): Promise<void> {
this.setState({ rooms: [] });
await this.props.context.msGraphClientFactory
.getClient("3")
.then((client: MSGraphClientV3): void => {
// Get user information from the Microsoft Graph
client
.api(
"places/" + item.emailAddress + "/microsoft.graph.roomlist/rooms"
)
.version("v1.0")
.get((err, res: any) => {
// handle the response
if (err) {
console.log("Error: ", err);
return;
}
// Map the JSON response to the output array
res.value.map((item: any) => {
this._allRooms.push({
displayName: item.displayName,
emailAddress: item.emailAddress,
capacity: item.capacity,
id: item.id,
availability: this._getAvailability(item),
});
});
// Update the component state accordingly to the result
this.setState({
rooms: this._allRooms,
});
});
});
}
private _getAvailability = debounce((item) => {
const start = this.state.selectedDate;
const end = this.state.selectedDate;
start.setUTCHours(this.state.selectedStart.getUTCHours());
start.setUTCMinutes(this.state.selectedStart.getUTCMinutes());
end.setUTCHours(this.state.selectedEnd.getUTCHours());
end.setUTCMinutes(this.state.selectedEnd.getUTCMinutes());
console.log(this.state.selectedStart, this.state.selectedEnd, start, end);
const apiMail = {
Schedules: [item.emailAddress],
StartTime: {
dateTime: this.formattedDateForAvailability(this.state.selectedStart),
timeZone: "Central Standard Time",
},
EndTime: {
dateTime: this.formattedDateForAvailability(this.state.selectedEnd),
timeZone: "Central Standard Time",
},
availabilityViewInterval: "30",
};
this.props.context.msGraphClientFactory
.getClient("3")
.then((client: MSGraphClientV3): void => {
client
.api("me/calendar/getschedule")
.version("v1.0")
.post(apiMail)
.then((res) => {
console.log(res);
res.value.map((x: any) => {
console.log("Availability: ", x);
if (x.availabilityView !== "0") {
console.log("Busy found: ", item.emailAddress);
return false;
}
});
});
});
return true;
}, 500);
How to get result for all the rooms without causing 429 error?
You will need to create on the code a back-off rule to avoid getting throttled.
The limits for the service should be on the page of the API.
It looks like you're using the exact same date/time for both the START and END filter on your call.
const start = this.state.selectedDate;
const end = this.state.selectedDate;
As a result, I believe Graph is trying to return your entire calendar schedule which results in too many API calls on the back end thus producing the 429 throttling errors you're seeing.
Try using a small date range like a single day for example.
I'm writing the backend for creating audit protocols. The user should be able to create criterias for the audit protocol. For this, i have the following backend-method to make sure, the protocol gets only created completely or the process of creating is canceled. It is possible to set several kinds of forms / criterias. But it could be, that only one kind of form is required. I do check that with the if-statement.
The creating works as expected. But the REST API always returns null to the clients. So i can't do further processing on the frontend regarding to the result of the creation process.
Technologies: Node.js and Sequelize. Frontend in angular / ionic. Database in mySQL.
I tried around with some transaction passing and return statements. I tried to compare it to a similiar code snippet, which works as expected.
exports.setAudit = (req, res, next) => {
trueFalseCriteria = req.body.trueFalseForms;
isShouldCriteria = req.body.isShouldForms;
generalCriteria = req.body.generalForms;
measurementCriteria = req.body.measurementForms;
toolId = req.body.toolId;
// Transaction is used to roll the whole transaction back if something wents wrong
return sequelize
.transaction(t => {
return audit
.create(
{
// Creating an audit referencing the tool
toolId: toolId
},
{ transaction: t }
)
.then(
// Getting the id of the audit that we just created
audit => {
return audit.id;
},
{ transaction: t }
)
.then(auditId => {
// Check wether the kind of form is used or not. If so, sequelize tries to do a bulk insert into the databases.
// Each bulk insert throws an error if it fails to cancel the whole transaction
if (trueFalseCriteria) {
console.log(1);
trueFalseCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
trueFalseCriterion.bulkCreate(trueFalseCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (isShouldCriteria) {
console.log(2);
isShouldCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
isShouldCriterion.bulkCreate(isShouldCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (generalCriteria) {
console.log(3);
generalCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
generalCriterion.bulkCreate(generalCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (measurementCriteria) {
console.log(4);
measurementCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
measurementCriterion.bulkCreate(measurementCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
}, { transaction: t });
})
.then(data => {
console.log(5);
res.status(200).json(data);
})
.catch(err => {
console.log(6);
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
};
Expected result: Http response with status code 200 on success
Actual result: null
I think you are missing a return for the last .then():
.then(auditId => {
// Check wether the kind of form is used or not. If so, sequelize tries to do a bulk insert into the databases.
.....
if (measurementCriteria) {
....
}
// RETURN SOMETHING HERE
}, { transaction: t });
db('fruit').where('fruit_name', 'apple')
.then(data => {
if (data.length === 0) {
db('fruit').insert({
amount: '2' //just for this post
})
.then(res.sendStatus(200))
.catch(res.sendStatus(500));
}
else {
db('fruit').where('fruit_name', 'apple').update({
amount: '5' //just for this post
})
.then(res.sendStatus(200))
.catch(res.sendStatus(500))
}
})
.catch(error => {
res.sendStatus(500)
})
I don't understand why the catch block is being executed and the server gave me the Can't set header after sending them error. This error occur because I am sending two res.sendStatus. The .then block are working fine and .catch block should not be executed unless the data fails to store in DB.
This is written in knex.js in a Node Express server and for just in case, the query statement is querying the fruit table where fruit_name column's item is equals to apple, insert new amount row if apple doesn't exist else if exist update the amount row.
db('fruit').where('fruit_name', 'apple')
.then(data => {
if (data.length === 0) {
db('fruit').insert({
amount: '2' //just for this post
})
.then(()=>res.sendStatus(200))
.catch(()=>res.sendStatus(500));
}
else {
db('fruit').where('fruit_name', 'apple').update({
amount: '5' //just for this post
})
.then(()=>res.sendStatus(200))
.catch(()=>res.sendStatus(500))
}
})
.catch(error => {
return res.sendStatus(500)
})
Hi calvert. You need to return your responses.This should work fine now.
I'm searching for a method to give feedback to a user of my Firebase/Firestore web app when offline. Lets see some code.
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
.then(function () {
alert('Success');
})
.catch(function (error) {
alert("Fail :" + error);
});
Online: Call the function, and the success alert works perfect.
Offline: Call the function, and nothing happens. But when user goes online, the app shows all alerts in a row.
In short, the .then part of the code only runs online. I need some sort of feedback for offline user.
What you're seeing is the expected behavior. Local write operations don't regularly fail, so there is no completion handler for them. If you want to do something after the local write completes, just use normal coding logic and put it after the set:
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
alert("Value set");
Thx #Frank, #Blackwood.
I changed the alerts for a non blocking notification.
function doSomething() {
try {
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
.then(function () {
alertify.notify('Data saved online.', 'success', 5);
})
.catch(function (error) {
alert('Error: ' + error);//or
//alertify.notify('Data saved online.', 'error', 10);
});
alertify.notify('Data saved on cache.', 'warning', 5);
} catch (error) {
alert('Error: ' + error);
}}
Online: Call the function, and launches 2 notifies (cached and online) almost at same time.
Offline: Call the function, launches 1 notify and when user goes online, the app shows all pendent online notifies in a row.
A very acceptable behavior.
I do not speak English, forgive my mistakes.
Here is my code :
server.get(url_prefix + '/user/:user_id/photos', function(req, res, next) {
if (!req.headers['x-session-id']) {
res.send({
status: {
error: 1,
message: "Session ID not present in request header"
}
})
} else {
User.findOne({
session_id: req.headers['x-session-id']
}, function(err, user) {
if (user) {
var user_id = req.params.user_id
Album.find({userId : user_id})
.populate('images')
.exec(function (err, albums) {
if (albums) {
albums.forEach(function(album, j) {
var album_images = album.images
album_images.forEach(function(image, i) {
Like.findOne({imageID : image._id, userIDs:user._id}, function(err,like){
if(like){
albums[j].images[i].userLike = true;
}
})
})
})
return res.send({
status: {
error: 0,
message: "Successful"
},
data: {
albums: albums
}
})
} else
return notify_error(res, "No Results", 1, 404)
})
}
else {
res.send({
status: {
error: 1,
message: "Invalid Session ID"
}
})
}
})
}
})
I am trying to add a extra value (albums[j].images[i].userLike = true;) to my images array, which is inside album array.
The problem is return res.send({ send the data before we get response from the foreach
How can I make it work, so that return should happen only after foreach has completed all the iteration
You will have to wait with invoking res.send until you fetched all the likes for all the images in each of the albums. E.g.
var pendingImageLikes = album_images.length;
album_images.forEach(function(image, i) {
Like.findOne({imageID : image._id, userIDs:user._id}, function(err,like){
if (like) {
albums[j].images[i].userLike = true;
}
if (!--pendingImageLikes) {
// we fetched all likes
res.send(
// ...
);
}
});
You might need to special case for album_images.length === 0.
Also, this does not take into account that you have multiple albums with multiple images each. You would have to delay res.send there in a very similar way to make this actually work. You might want to consider using a flow control library like first (or any other of your preference, just search for "flow control library") to make this a bit easier.
Also, you might want to consider not relying on semicolon insertion and manually type your semicolons. It prevents ambiguous expressions and makes the code easier to read.
Since you need your code to wait until all of the find operations have completed, I'd suggest you consider using the async package, and specifically something like each (reference). It makes using async loops cleaner, especially when dealing with MongoDB documents and queries. There are lots of nice features, including the ability to sequentially perform a series of functions or waterfall (when you want to perform a series, but pass the results from step to step).
> npm install async
Add to your module:
var async = require("async");
Your code would look something like this:
albums.forEach(function(album, j) {
async.each(album.images, function(album, done) {
Like.findOne({imageID: image._id, userIDs:user._id}, function(err, like){
if(!err && like){
albums[j].images[i].userLike = true;
}
done(err); // callback that this one has finished
})
})
}, function (err) { // called when all iterations have called done()
if (!err) {
return res.send({
status: {
error: 0,
message: "Successful"
},
data: {
albums: albums
}
});
}
return notify_error(res, "No Results", 1, 404);
});
});