Querying Async Result in jsreportonline
-
I use jsreportonline. I recently changed my report rendering to both save reports and run them asynchronously. This was because the synchronous approach is blocking my express app:
client.render( { "template":{"shortid": myShortId}, "data": mydata, "options": {"reports": {"save": true, "async": true }} }) .then(function(response){ // Set status URL res.json({"location": response.headers.location}); });
The goal was to return the location status URL to the client's browser so that the client could poll the status URL until the report had completed rendering and then download the report. Note my web client uses Angular 1.x
$http.get(response.location,{"responseType":'document'}) .then( // Success function(result){ // Check status of report.... // If complete, download report. Otherwise, poll again .... });
In this example, response.location would read something like
https://myapp.jsreportonline.net/reports/id/status
However, I ran into a problem when the client's browser attempted to check the status by getting the location URL. Because the browser does not have the credentials to my jsreportonline instance, the status check failed with the following error:
Mixed Content: The page at 'https://myapp.com/reportpage' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://jsreportonline.net/sign'. This request has been blocked; the content must be served over HTTPS.
So, what's the best way to have my client app poll the status of the save report and then download the final report? Should I be polling the status through my NodeJS app by using the jsreport-client package?
-
So, what's the best way to have my client app poll the status of the save report and then download the final report? Should I be polling the status through my NodeJS app by using the jsreport-client package?
I would do something like this.
Your browser client sends a request to your nodejs server.
The nodejs server sends async request to jsreportonline and gets back the location for a status check.
The nodejs server sends a response back to the client with location url.
The browser client then polls to another express endpoint and sends there the location url
The nodejs then calls this location but also include the basic auth header.However, maybe this roundtrip isn't needed. What do you mean with this? How it can block your server?
I recently changed my report rendering to both save reports and run them asynchronously. This was because the synchronous approach is blocking my express app
-
Thanks Jan. Here's what the code looked like before:
client.render( { "template":{"shortid": myShortId}, "data": mydata, "options": {"reports": {"save": false}} }) .then((response) => response.pipe(res));
However, as per your documentation about Async, which read:
... but the rendering is still synchronous and you receive response back after the process is finished.
We found that
response.pipe()
code would execute only after the report completed processing. If the report took several seconds to process given our large data, then this impacted our nodejs express app by blocking the event loop until the report completed. Other requests to express would then get impacted, et... Therefore, we were looking to run it asynchronously, store the report somewhere and then serve the report once it completed processing. Initially, we were thinking of using AWS S3 as the store, but since jsreportsonline provides temporary storage, we opted to go with that approach.Your suggestion works and we will try sending the request from the nodejs server. If I have questions on that, I'll reply to this forum.
-
If the report took several seconds to process given our large data, then this impacted our nodejs express app by blocking the event loop until the report completed. Other requests to express would then get impacted, et
This isn't true for sure. The event loop isn't blocked in this case and other requests are normally processed.
-
Jan,
Finally got to working on this again. Took your advice and we're now looking to create a polling mechanism.
The nodejs then calls this location but also include the basic auth header.
However, we're having trouble actually polling the jsreportsonline server. Below is the endpoint we created on our NodeJS server. It accepts the report status URL (in the form of http://jsreport-host/reports/id/status) as a parameter and then attempts to poll that URL. I'm using the request-promise library. I've attempted using both GET and POST methods. When using GET, the response is the html for your login page. When using POST, I get an error message of
StatusCodeError: 302 - "Found. Redirecting to http://jsreportonline.net/sign"
Here is the code for my endpoint:
// Get modules const thisRouter = require('express').Router(); const rp = require('request-promise'); ====================================== ROUTE thisRouter.route('/common/jsreports/poll') // ====================== POST .post(function(req,res,next){ // Get report params let reportStatus = req.body.reportStatus; // Get status return rp({ method: 'POST', uri: reportStatus, auth: { 'user': process.env.JSREPORT_USR, 'pass': process.env.JSREPORT_PWD }, body: '', json: false }) .then(function(response){ console.log(response); // return nothing for now res.json({}); }) .catch(function(e){ console.log(e); res.status(500).send(e); }); });
Can you provide some guidance on how to check the status?
-
I see, unfortunately this is a bug in jsreportonline.
We need to fix the integration of the async reports there.
It will be fixed in the next release of jo, however, I don't know yet when this will happen.Do you really need to use the async reports? I'm asking because, as I mentioned earlier, the argument that waiting for report generation is blocking your app seems false.
-
Understood. I agree regarding your previous statement. The "render" method is not blocking the app given the "promise" implementation. However, it's still an issue for us.
We use heroku to deploy our application. Unfortunately, heroku implements a hard 30sec request timeout. This means that any request by our client to render a report from jsreports must complete in 30sec or less. If it does NOT, then the heroku web server terminates the request and returns an error to the client. We have some reports that are getting close to reaching that limit and we'd like to implement some sort of polling architecture before it becomes an issue.