Sails JS Publish Update Not working - javascript

Writing a simple call tracker with sails js and twilio node but publishUpdate doesn't seem to be sending out messages to my frontend clients. Model.publish IS working but Model.publishUpdate is not. Am I doing any thing obviously wrong or is there something I'm misisng?
Here is my code.
In my primary controller, I have a specific route to subscribe clients to my Model
in MainController.js
calls: function(req,res){
Calls.find(function foundCalls(err, calls) {
if (err) return next(err);
// subscribe this socket to the Calls classroom
Calls.subscribe(req.socket);
// subscribe this socket to the Calls instance rooms
Calls.subscribe(req.socket, calls);
// This will avoid a warning from the socket for trying to render
// html over the socket.
res.send(calls,200);
});
},
In my app.js file I have
socket.on('connect', function socketConnected() {
socket.get('/main/calls', function(response){console.log(response)});
socket.on('message', function(message) {
console.log(message);
});
});
In another controller I have this:
CallController.js
takecall: function(req,res) {
Calls.findOneByCallSid(req.body.CallSid).done(function(err, thiscall) {
if(err){
return next(err);
} else {
// 1a. Save additional parameters
thiscall.DialCallDuration=req.body.DialCallDuration
thiscall.DialCallSid=req.body.DialCallSid
thiscall.RecordingUrl=req.body.RecordingUrl
thiscall.DialCallStatus=req.body.DialCallStatus
thiscall.RecordingDuration=req.body.RecordingDuration
thiscall.RecordingSid=req.body.RecordingSid
thiscall.save(function(err,call) {
if (err) return next(err);
console.log(call.id);
Calls.publishUpdate(call.id,{message:'hello'});
});
// 3. Display twiml that tells twilio we are done with call workflow
var twilio = require('twilio');
var resp = new twilio.TwimlResponse();
res.send(resp.toString(), 200);
}
});
}

Related

Sails js client native websocket

I'm trying to use websockets with sails-js but I can't make it work with native javascript websockets.
the tutorial example use the sails.io.js library and it goes a little bit like this:
io.socket.on('hello', function (data) {
console.log('Socket `' + data.id + '` joined the party!');
});
function sendHello () {
// And use `io.socket.get()` to send a request to the server:
io.socket.get('/websockets/hello', function gotResponse(data, jwRes) {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
});
}
This does work, but i want to use the native javascript websockets like this:
let ws = new WebSocket("ws://localhost:1337/websockets/hello");
ws.onopen = function (e) {
console.log("[open] Connection established");
console.log("Sending to server");
ws.send("My name is John");
};
ws.onmessage = function (event) {
console.log(`[message] Data received from server: ${event.data}`);
};
ws.onclose = function (event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
ws.onerror = function (error) {
console.log(`[error] ${error}`);
console.log(error);
};
Clean and native javascript websockets with no library needed. Unfortunately I can't seem to make it work.
When I try to connect to sails js server using native websockets I get this message:
WebSocket connection to 'ws://localhost:1337/websockets/hello' failed: Connection closed before receiving a handshake response
Impossible to make it connect, it seems like sails js don't even get the message because i make a log when i get a new connection (using the tutorial code):
module.exports = {
hello: function (req, res) {
console.log("web socket received",req.isSocket)
// Make sure this is a socket request (not traditional HTTP)
if (!req.isSocket) {
return res.badRequest();
}
// Have the socket which made the request join the "funSockets" room.
sails.sockets.join(req, 'funSockets');
// Broadcast a notification to all the sockets who have joined
// the "funSockets" room, excluding our newly added socket:
sails.sockets.broadcast('funSockets', 'hello', { howdy: 'hi there!' }, req);
// ^^^
// At this point, we've blasted out a socket message to all sockets who have
// joined the "funSockets" room. But that doesn't necessarily mean they
// are _listening_. In other words, to actually handle the socket message,
// connected sockets need to be listening for this particular event (in this
// case, we broadcasted our message with an event name of "hello"). The
// client-side code you'd need to write looks like this:
//
// io.socket.on('hello', function (broadcastedData){
// console.log(data.howdy);
// // => 'hi there!'
// }
//
// Now that we've broadcasted our socket message, we still have to continue on
// with any other logic we need to take care of in our action, and then send a
// response. In this case, we're just about wrapped up, so we'll continue on
// Respond to the request with a 200 OK.
// The data returned here is what we received back on the client as `data` in:
// `io.socket.get('/say/hello', function gotResponse(data, jwRes) { /* ... */ });`
return res.json({
anyData: 'we want to send back'
});
}
};
How can I make sails js work with native javascript websockets?
Found a simple solution!
Used the npm package ws: npm i ws
making a new hook: sails generate hook customWebSocket
in the hook :
/**
* WS hook
*
* #description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic.
* #docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
*/
const WebSocket = require('ws');
module.exports = function defineWsHook(sails) {
return {
/**
* Runs when this Sails app loads/lifts.
*/
initialize: async function () {
sails.log.info('Initializing custom hook (`WS`)');
console.log("custom hook")
const wss = new WebSocket.Server({ port: 3100 });
wss.on('connection', (socket) => {
console.log('New user connected wss');
socket.on('message', function incoming(message) {
console.log(message)
});
});
}
};
};
Done and done, now i can connect to is using native websocket!
now that i have done that i realize that the socket.io library might be better for handling errors.

Nodejs wait for socket and then respond to http request

I have a NodeJS code where, basically this is what happens:
HTTP request (app.get) -> Send a request to a low level socket(using net.Socket()) -> Get response from socket -> res.send(response from socket)
This doesn't work because the net.Socket() uses async functions and events (client.on("data", callback)).
I tried something like this:
app.get("/", function(req, res){
client.connect("localhost", 420, function(){
client.write("example data");
});
client.on("data", function(data){
client.destroy();
res.send(data);
});
});
But it doesn't work because it says I am re-sending the headers (the res object won't change since the function is an event, not a sync function).
Any ideas? Or a library for sync socket requests? I have tried the following:
Synket
sync-socket
netlinkwrapper
And they don't work.
Edit: I am trying something like this:
async function sendData(client, res){
client.on('data', function(data){
console.log("Got data!");
res.send(""+data);
res.end();
console.log("Sent data!");
client.destroy();
console.log("Killed connection");
return;
});
}
app.get("/", function(req, res){
var body = req.query;
client.connect(420, "localhost", function(){
client.write("some random data");
console.log("Connected & Data sent!");
sendData(client, res);
});
});
It works the first time I try to access the page, but the second time the app crashes and I get this error:
_http_outgoing.js:489
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
client triggles data event multiple times.
`app.get("/", function(req, res){
client.connect("localhost", 420, function(){
client.write("example data");
});
client.pipe(res);
});
});
`
It turns out that, each time a request comes to that endpoint, a new subscription has been registered by calling the client.on('eventName', cb). So, calls starting from the second one will trigger multiples of those registrations and will cause that header error.
So, a workaround for that:
socket.on('event', (packet) => {
socket.removeAllListeners('event')
res.json({ packet });
});
That would do the trick but I'm not sure if it's a bad practice to continuously add/remove the same event.
Edit
Found a better way. once will ensure that registered event only will run once, just as we want:
socket.once('event', (packet) => {
res.json({ packet });
});

Sails.js websockets work in localhost but not working after I deploy to Openshift

Summary:
I am working on an admin panel for a sails.js based website. I have configured websockets to work in my localhost. But when I deploy to openshift, only the io.socket.on('connect',...); is firing. But when I publishAdd a record, nothing is being received in the client.
Details:
Firstly some details about my configuration:
Sails.js version: 0.12.3
Node.js version: v0.10.36 (Changed it to this to match openshift's version v0.10.35)
Socket client: sails.io.js
Tried with both memory socket store and redis socket store and the behavior is the same
Current Behavior:
I am trying to enable a notification system to my application so I am using the default sails.io.js to connect a websocket (Using the default io.socket object) in my client side code I am using console.log() for now to log any events. The code looks like this in the client js file:
$(document).ready(function () {
io.sails.environment = "development";
if(window.location.hostname === "localhost") {
io.sails.url = 'http://localhost:1337';
} else {
io.sails.url = 'http://myapp-username.rhcloud.com:8000';
}
var adminSocket = io.socket;
// Access socket's connect event to initialize functions
adminSocket.on('connect', function () {
console.log("Connected to socket");
// Subscribe to admin model of current user
adminSocket.get('/admin/subscribeToModel');
});
// Access notification (adminAlerts) model's socket events
adminSocket.on('admin', function(event) {
console.log(event);
});
// Log Errors
adminSocket.on('error', function (event) {
console.log(event);
});
});
As you can see the code runs io.socket.get(...) after the socket connects. This works for both localhost and openshift and logs "Connected to socket" and also sails.io.js's default connection message. When the server turns off the client tries to reconnect as the default behavior.
io.socket.get runs a custom controller function in the admin controller:
subscribeToModel: function(req, res, next) {
if (!req.isSocket) {
console.log("Not Socket");
return res.badRequest();
}
console.log("Socket");
Admin.subscribe(req, [req.session.userid]); //Subscribes to Admin model's entry with the user's id
return res.json({message: "Subscribed"});
}
This logs "Socket" in Both openshift and localhost but the return message is not available. I don't know if subscribe works or not in openshift but it works for sure in localhost.
This is the setup. Now I built a function to test socket behavior. In admin controller I created:
createAndBroadcastNotification: function (req, res, next) {
AdminAlerts.create({
admin: req.session.admin.id,
alertHeader: 'Some Header',
alertBody: 'Some Message',
alertType: 'label-info',
url: '/somepage'
}).exec(function (err, alert) {
if (err) {
res.serverError(err);
}
Admin.findOne({ where: {id: req.session.admin.id} }).populate('alerts').exec(function (err, admin) {
if (err) {
res.serverError(err);
}
Admin.publishAdd(req.session.userid, 'alerts', alert.id);
return res.json({message: "Done"});
});
});
}
After this Both openshift and localhost are showing {message:Done} in the browser and the record is being created and associated in the mySQL database. But Only Localhost is posting this message in the console as expected:
Object {id: 1, verb: "addedTo", attribute: "alerts", addedId: 10}
Openshift is showing no such message. Neither is it showing any error messages.
I have been trying to figure out the problem for 5 days. I have not been able to find a clue as to why this is happening.
Objective:
I wish to get the console.log() message in the client to signify an alert being added.
I figured it out. For anyone facing the same or similar issue, the documentation for openshift (In the blog) is outdated. Letting sails.io.js figure out the host and port works "just fine". It returns a connection error in firefox though. Here are the changes:
// client-side-code.js
$(document).ready(function () {
// io.sails.environment = "development";
// if(window.location.hostname === "localhost") {
// io.sails.url = 'http://localhost:1337';
// } else {
// io.sails.url = 'http://myapp-username.rhcloud.com:8000';
// }
var adminSocket = io.socket;
// Access socket's connect event to initialize functions
adminSocket.on('connect', function () {
console.log("Connected to socket");
// Subscribe to admin model of current user
adminSocket.get('/admin/subscribeToModel');
});
// Access notification (adminAlerts) model's socket events
adminSocket.on('admin', function(event) {
console.log(event);
});
// Log Errors
adminSocket.on('error', function (event) {
console.log(event);
});
});

Pass values from Server to Client | Node to Angular

I'm very new to MEAN. I'm trying to send a dataset from Node to my Angular Controller. However, the angular controller isn't receiving the correct information and is resulting in null.
My view is called booklist.jade
Here is my server side (Node JS)
router.get('/thelist', function(req, res){
res.render('booklist');
});
router.get('/thelist/data', function(req, res){
.
.
.
// Find all books
collection.find({}).toArray(function (err, result) {
if (err) {
res.send(err);
} else if (result.length) {
res.json({booklist : result});
} else {
res.send('No documents found');
}
//Close connection
db.close();
});
}
});
});
Here is my client side (Angular JS)
function booksController($scope)
{
$http.get("http://localhost:3000/thelist/data").success(function( data ) {
$scope.book=10; //THIS WORKS
$scope.table= data;
});
}
Basically, I want $scope.table to have all data from my server. Any ideas on why this is failing?
UPDATE: On trying some console log checks, I found out that the request router.get('/thelist/data', function(req, res) isn't being called by the Angular Controller.
res.json({booklist : result});
as per your code you are sending result in booklist attribute
Hence while reading you should say $scope.table= data.booklist;
My client side controller was wrong. Here is the correct one.
app.controller('booksController',['$scope','$http',
function($scope,$http) {
$http.get("http://localhost:3000/thelist/data").success(function( data ) {
$scope.book=data;
});
}]);

LoopBack: cannot call method 'post' of undefined

I am new to loopback and node.js.
I have created two models: Rating and RatingsAggregate
using the loopback explorer, I can query and post against the API just fine.
I am try to setup some basic business logic so I am editing the file Rating.js in common/models
Here is the content of it:
module.exports = function(Rating) {
Rating.afterRemote('**', function(ctx, inst, next) {
var loopback = require('loopback');
var app = loopback();
var ratingsaggregate = app.models.ratingsaggregate;
ratingsaggregate.post({"source":"foobar","restaurantID":"foobar","itemMenuName":"foobar","itemSectionName":"foobar","itemName":"foobar","nRatings1":123,"nRatings2":123,"nRatings3":123,"nRatings4":123,"nRatings5":123,"hasImage":true,"imageSize":123,"latestImageRatingID":"foobar","imageCount":123,"lastUpdated":"foobar"}, function(err, response) {
if (err) console.error(err);
next();
});
});
};
I can load my API, but whenever I run a get statement against it, I get this error:
TypeError: Cannot call method 'post' of undefined
My guess is that somehow ratingsaggregate never gets a value... but I don't know what I am doing wrong. Obviously this is not the end state of my business logic, but I am trying some basic CRUD right now between two models
And... here is the answer. There was a getModel function hidden in the documentation
module.exports = function(Rating) {
Rating.afterRemote('create', function(ctx, inst, next) {
var loopback = require('loopback');
var ratingsaggregate = loopback.getModel('ratingsaggregate');
ratingsaggregate.create({"source":"foobar","restaurantID":"foobar","itemMenuName":"foobar","itemSectionName":"foobar","itemName":"foobar","nRatings1":123,"nRatings2":123,"nRatings3":123,"nRatings4":123,"nRatings5":123,"hasImage":true,"imageSize":123,"latestImageRatingID":"foobar","imageCount":123,"lastUpdated":"foobar"}, function(err, response) {
if (err) console.error(err);
next();
});
});
};
Fixes everything and the behaviour is the expected one

Categories