send multiple responses to client via nodejs - angularjs

i am using nodejs and i want to send back multiple responses to client.And my code is below
//addwork
var agenda = require('../../schedules/job-schedule.js')(config.db);
exports.addwork = function(req, res) {
var work = new Work(req.body);
work.user = req.user._id;
var user=req.user;
work.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
console.log('created work....'+work);
console.log('dateeeeeeeeeeeee'+work.created);
console.log('calling agenda job now, user is: '+ JSON.stringify(req.user));
console.log('supervisor-------------------------'+JSON.stringify(user.supervisor));
agenda.now('Work_To_Supervisior_Notify', {supervisor:user.supervisor,title:work.title,details:work.details});
res.jsonp(work);
res.send({message:'An email has been sent to ' + user.supervisor + ' with further instructions.'});
}
});
};`
//job-schedule.js
var Agenda = require("agenda");
var emailJob = require('./jobs/email-job.js');
module.exports = function(agendaDb) {
var agenda = new Agenda({db: { address: agendaDb}});
emailJob.sendWorkToSupervisiorEmail(agenda);
agenda.start();
return agenda;
}
//email-job.js
exports.sendWorkToSupervisiorEmail = function(agenda){
agenda.define('Work_To_Supervisior_Notify',{priority: 'high', concurrency: 10}, function(job, done){
console.log('Send works to supervisior ' + JSON.stringify(job.attrs.data.supervisor)+' ,title '+job.attrs.data.title+' ,details '+job.attrs.data.details);
var smtpTransport = nodemailer.createTransport(config.mailer.options);
var mailOptions = {
to: job.attrs.data.supervisor,
from: config.mailer.from,
subject: 'work done by user',
html: '<b>work title : '+job.attrs.data.title+' <br/>work details : '+job.attrs.data.details+'</b>'
};
smtpTransport.sendMail(mailOptions, function(err) {
if (!err) {
console.log('An email has been sent to ' + job.attrs.data.supervisor + ' with further instructions.');
res.send({message:'An email has been sent to ' + user.supervisor + ' with further instructions.'});
}
});
done();
})
}
Here i want response either from agenda or from res.send() message in addwork function
If i use res.send in addwork function it shows ERROR as "can't set headers after they sent".And if i use res.send message in sendWorkToSupervisiorEmail() it show ERROR as "there is no method send".I am new to nodejs please help me with solution

A http request only gets a single http response. Using http, you only get one response. Some options for you:
1) Wait for everything to finish before replying. Make sure each part creates a result, success or failure, and send the multiple responses at once. You would need some control flow library such as async or Promises to make sure everything responded at the same time. A good choice if all parts will happen "quickly", not good if your user is waiting "too long" for a response. (Those terms were in quotes, because they are application dependent).
2) Create some scheme where the first response tells how many other responses to wait for. Then you'd have a different HTTP request asking for the first additional message, and when that returns to your client, ask for the second additional message, and so on. This is a lot of coordination though, as you'd have to cache responses, or even try again if they were not done yet. Using a memory cache like redis (or similar) could fulfill the need to holding responses until ready, with a non-existent meaning 'not ready'
3) Use an eventing protocol, such as WebSockets, that can push messages from the server. This is a good choice, especially if you don't know how long some events would occur after the trigger. (You would not want to stall a HTTP request for tens of seconds waiting for 3 parts to complete - user will get bored, or quit, or re-submit.). Definitely check out the Primus library for this option. It can even serve the client-side script, which makes integration quick and easy.

Related

AJAX Express receive array on client

I am having trouble with receiving array data as it flows from another source into my client. My goal is to have the HTML document populate with data from the array as it is received by the server.
Server:
const keywordsList = [];
const keywordsListSerialize = JSON.stringify(keywordsList);
const arrayToString = JSON.stringify(Object.assign({}, keywordsList))
// const keywordsListObject = JSON.parse(arrayToString);
app.use(cors())
const bodyParser = require('body-parser');
app.get('/', (req, res) => {
// res.writeHead(200, {
// 'Connection': 'keep-alive',
// 'Content-Type': 'text/event-stream',
// 'Cache-Control': 'no-cache',
// });
//res.flushHeaders();
res.send(keywordsList);
//res.write(arrayToString);
//res.status(200).send(arrayToString);
//res.status(500).send({ error: 'something blew up' })
});
As you can see from the server code, I have tried multiple variations of sending data as a JSON object/just an array or a string and I can't seem to get ANY of it to even show up on my client.
Client:
var xhttp = new XMLHttpRequest();
console.log(xhttp);
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(xhttp.responseText)
var jsonObj = JSON.parse(xhttp.responseText);
for(i = 0; i < jsonObj.length; i++) {
document.getElementById("keywordsList").innerHTML = jsonObj;
}
}
};
xhttp.open("GET", "/", true);
xhttp.send();
The client does two very basic but interesting things. 1. the xhttp.responseText is the whole HTML file which makes me think I should be handling the request somehow in my server (I figured I could get away with just constantly streaming data to the client) and 2. Sends me an error
VM812:1 Uncaught SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.xhttp.onreadystatechange
I believe I am having multiple issues. I understand that I will want to send data almost definitely as a JSON object and parse it on the client but at the most basic examples, I can't even get that to work. I have double checked that how I have my client and server setup is correct and if need be, I can discuss how it's setup as to make sure they are linked correctly locally.
I am looking for guidance as well as possibly a technical answer. Please do not just link me ajax documentation because I have probably read it 3 times over (minimum) at this point. I thought of just throwing everything away and using a websocket as it would accomplish what I am trying to achieve but that means learning all of that.
Thank you for your time.
So I figured it out eventually. The only post above from someone else is correct. It's that simple to send data across. Unfortunately, when trying to understand AJAX and express at the same time, you get confused on whether you are really sending data AND if that data is structured correctly so the client doesn't just throw out errors. I was conflating AJAX and express issues together which could have been resolved quicker had I understood how to correctly test stuff but alas, I am by myself working this all out.
For any poor sap who might come across this, probably don't use XMLHttpRequests and just use JQuery.
Thanks for the response.
Server:
const express = require('express')
const app = express()
const port = 3000
const keywordsList = ['apple', 'banana', 'cucumber']
app.get('/', (req, res) => {
res.send(JSON.stringify(keywordsList))
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Testing the web server:
curl http://localhost:3000
["apple","banana","cucumber"]
To iterate over the response in the client:
const arrayOnClientSide = JSON.parse(xhttp.responseText);
arrayOnClientSide.forEach(element => console.log(element));
which outputs:
apple
banana
cucumber
Note that every time you call .innerHTML you will overwrite whatever value it previously held, so construct the presentation format of your display value first, then assign it to innerHTML at the end.

Flask socketIO emit series of events

I would like server to emit series of events to client (React) after connection is established, Ive tried:
#socketio.on('connect')
def test_connect():
for i in range(10):
emit('my response', {'data': 'Connected ' + str(i)})
time_module.sleep(2)
but events shows in web browser console at the same time, like they arrived at the same moment, in react I use :
useEffect(() => {
const socket = socketIOClient(ENDPOINT);
socket.on('my response', function (data) {
console.log('my response: ', data.data);
})
})
There are two potential problems in your server code.
First of all, what is time_module in your example? I suspect that is a blocking sleep that you are doing. Instead, I suggest you sleep properly with socketio.sleep(2).
The second problem is that you are doing this in the connect event handler. The Socket.IO server waits until you return from the connect handler to decide if the client is accepted or rejected, so in general you do not want this handler to take a long time to run, because the connection isn't fully established until you return. I suggest you move this logic to a background function instead.
The following code addresses both issues:
def initial_events():
for i in range(10):
emit('my response', {'data': 'Connected ' + str(i)})
socketio.sleep(2)
#socketio.on('connect')
def test_connect():
socketio.start_background_task(initial_events)

Request Deferrer with Service Worker in PWA

I am making a PWA where users can answer the forms. I want it to make also offline, so when a user fills out a form and does not have the internet connection, the reply will be uploaded when he is back online. For this, I want to catch the requests and send them when online. I wanted to base it on the following tutorial:
https://serviceworke.rs/request-deferrer_service-worker_doc.html
I have managed to implement the localStorage and ServiceWorker, but it seems the post messages are not caught correctly.
Here is the core function:
function tryOrFallback(fakeResponse) {
// Return a handler that...
return function(req, res) {
// If offline, enqueue and answer with the fake response.
if (!navigator.onLine) {
console.log('No network availability, enqueuing');
return;
// return enqueue(req).then(function() {
// // As the fake response will be reused but Response objects
// // are one use only, we need to clone it each time we use it.
// return fakeResponse.clone();
// });
}
console.log("LET'S FLUSH");
// If online, flush the queue and answer from network.
console.log('Network available! Flushing queue.');
return flushQueue().then(function() {
return fetch(req);
});
};
}
I use it with:
worker.post("mypath/add", tryOrFallback(new Response(null, {
status: 212,
body: JSON.stringify({
message: "HELLO"
}),
})));
The path is correct. It detects when the actual post event happens. However, I can't access the actual request (the one displayed in try or fallback "req" is basically empty) and the response, when displayed, has the custom status, but does not contain the message (the body is empty). So somehow I can detect when the POST is happening, but I can't get the actual message.
How to fix it?
Thank you in advance,
Grzegorz
Regarding your sample code, the way you're constructing your new Response is incorrect; you're supplying null for the response body. If you change it to the following, you're more likely to see what you're expecting:
new Response(JSON.stringify({message: "HELLO"}), {
status: 212,
});
But, for the use case you describe, I think the best solution would be to use the Background Sync API inside of your service worker. It will automatically take care of retrying your failed POST periodically.
Background Sync is currently only available in Chrome, so if you're concerned about that, or if you would prefer not to write all the code for it by hand, you could use the background sync library provided as part of the Workbox project. It will automatically fall back to explicit retries whenever the real Background Sync API isn't available.

Asynchronously Respond in new Hangout Chat using rest API, without using google app engine

How can I post message as a bot(async) in new hangouts chat without using the Google App Engine. I have gone through the examples, but all of them use App Engine for authentication, but i need to authenticate it without using the same.
Here is a code sample that connects to a chat using an http request and a webhook from Google Hangout Chat with a Python script. Webhooks are the only alternative to using a service account. More info here: https://developers.google.com/hangouts/chat/how-tos/webhooks
`from httplib2 import Http
from json import dumps
#
# Hangouts Chat incoming webhook quickstart
#
def main():
url = '<webhook url here>'
bot_message = {
'text' : 'text go here'}
message_headers = { 'Content-Type': 'application/json; charset=UTF-8'}
http_obj = Http()
response = http_obj.request(
uri=url,
method='POST',
headers=message_headers,
body=dumps(bot_message),
)
print(response)
if __name__ == '__main__':
main()
`
If your bot implementation is with google app script try to do it with google service account and as indicated here an example of async message
// Example bot for Hangouts Chat that demonstrates bot-initiated messages
// by spamming the user every minute.
//
// This bot makes use of the Apps Script OAuth2 library at:
// https://github.com/googlesamples/apps-script-oauth2
//
// Follow the instructions there to add the library to your script.
// When added to a space, we store the space's ID in ScriptProperties.
function onAddToSpace(e) {
PropertiesService.getScriptProperties()
.setProperty(e.space.name, '');
return {
'text': 'Hi! I\'ll post a message here every minute. ' +
'Please remove me after testing or I\'ll keep spamming you!'
};
}
// When removed from a space, we remove the space's ID from ScriptProperties.
function onRemoveFromSpace(e) {
PropertiesService.getScriptProperties()
.deleteProperty(e.space.name);
}
// Add a trigger that invokes this function every minute via the
// "Edit > Current Project's Triggers" menu. When it runs, it will
// post in each space the bot was added to.
function onTrigger() {
var spaceIds = PropertiesService.getScriptProperties()
.getKeys();
var message = { 'text': 'Hi! It\'s now ' + (new Date()) };
for (var i = 0; i < spaceIds.length; ++i) {
postMessage(spaceIds[i], message);
}
}
var SCOPE = 'https://www.googleapis.com/auth/chat.bot';
// The values below are copied from the JSON file downloaded upon
// service account creation.
var SERVICE_ACCOUNT_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n';
var SERVICE_ACCOUNT_EMAIL = 'service-account#project-id.iam.gserviceaccount.com';
// Posts a message into the given space ID via the API, using
// service account authentication.
function postMessage(spaceId, message) {
var service = OAuth2.createService('chat')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(SERVICE_ACCOUNT_PRIVATE_KEY)
.setClientId(SERVICE_ACCOUNT_EMAIL)
.setPropertyStore(PropertiesService.getUserProperties())
.setScope(SCOPE);
if (!service.hasAccess()) {
Logger.log('Authentication error: %s', service.getLastError());
return;
}
var url = 'https://chat.googleapis.com/v1/' + spaceId + '/messages';
UrlFetchApp.fetch(url, {
method: 'post',
headers: { 'Authorization': 'Bearer ' + service.getAccessToken() },
contentType: 'application/json',
payload: JSON.stringify(message),
});
}
You need to perform some below steps.
Create a service-account in console.developers.google.com and download the private key in JSON format
Use below modules if you code in python.
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.discovery import build, build_from_document
from httplib2 import Http
Below snippet will post the message to user via chat.google.
scopes = ['https://www.googleapis.com/auth/chat.bot']
credentials = ServiceAccountCredentials.from_json_keyfile_name('/path/to/json',
scopes)
http = Http()
credentials.authorize(http)
chat = build('chat', 'v1', http=http)
resp = chat.spaces().messages().create(
parent=space,
body={'text': 'HELLO WORLD'}).execute()
You would require a space name where you can post the code. You will get the same from hangout chat response.
It’s possible to do so using JavaScript, python, (possibly more). You can check out examples here: https://github.com/gsuitedevs/hangouts-chat-samples/tree/master/node/basic-cloud-functions-bot
If you’re using cards and JavaScript I would encourage you to checkout my library here: https://github.com/BaReinhard/hangouts-card-helper
I’m also in the process of creating another example for JavaScript that is more async focused that should provide and example that’s a bit easier to reason about the code. Will link when the PR is pushed.
Edit:
I realize that you mentioned REST api. The above answer is more useful for a specific bot that can be accessed #mentions. However, if you can provide us with a bit more information I can better fix my answer to answer your question.

How to send backend errors/success messages from Node.js to frontend in AngularJS controller?

I have an application built on the MEAN stack, with ExpressJS.
There is a need to send success or error messages from backend (NodeJS) to frontend (AngularJS) for example - to show when a file was successfully uploaded. Below - after app.post is successfully completed, I would like to show this result on frontend (in AngularJS).
app.post('/upload' , function (req, res) {
//busboy handles multi-part file upload
console.log('Post received to upload ...');
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
//only update if the filename was change or exists
if (filename!== undefined || filename !=='') {
//reset url
options.url = '';
console.log("Uploading the file " + filename);
console.log("before", options);
options.path = '/uploads/' + filename;
options.fileName = filename; //update file name
console.log("Updating options ...", options);
fstream = fs.createWriteStream(__dirname + '/uploads/' + filename); //path where the file will be uploaded
file.pipe(fstream);
fstream.on('close', function () {
console.log("Upload finished successfully ! Uploaded " + filename);
initOntology();//initialize the ontology from upload
initEdges();
});
}
});
});
Is there any way that I can send the result from NodeJS to AngularJS controller?
There is a similar question here, but unsolved.
You should use socket.io
When the frontend starts up, it connects to a 'socket connection'. This is a different protocol to HTTP. If it can't do so, it uses a degraded version of the connection which works over HTTP.
Either end of the connection can 'emit' a message, which gets sent to the other end. The backend can send messages to all clients or to specific ones. You would set up Angular to receive the message and create its own event which one or more controller could be listening for. See https://github.com/btford/angular-socket-io When the frontend wants to send a message back to the backend, it can use the socket, or just a regular HTTP POST, etc.
Using this with Angular and Node is quite standard, there should be lots of information out there on how to do it.
Just send a http response back using the res object :
res.status(200).send('OK')
When error, send, a error status
res.status(500).send(errorMsg)
For angular :
$http.post(...).then(function successCallback(response) {
// response with 200 status
}, function errorCallback(response) {
// response with status 500
});

Resources