O would like to use a custom method with socket on SailsJS.
new: function(req, res){
Talk.create({message: "text"}).exec(function created(err,created){
Talk.publishCreate(created);
res.send(created);
});
}
On client side I am doing exactly as described here: http://beta.sailsjs.org/#/documentation/reference/websockets/resourceful-pubsub/publishCreate.html
publishCreate are not sending updates to client side. Is there something I am missing?
Most likely the problem is that you have not used the .watch() method to subscribe to create messages, as stated in the documentation you linked to:
The default implementation of publishCreate only publishes messages to
the firehose, and to sockets subscribed to the model class using the
watch method
In the latest releases of Sails, the sails.config.blueprints.autoWatch property defaults to true, meaning that a socket will automatically start listening for "create" messages whenever it does a "find" call, as in io.sockets.get('/talk'). Otherwise, you need to subscribe the socket manually in your controller using Talk.watch(req);.
Related
Realising this is gonna be a very general question but I am gonna try to be as specific as possible:
What is the best way to design/structure an Socket.IO app?
I have a NodeJS backend with React frontend, with authentication (user must log in). I have several REST endpoints, for example /foo, /bar, /baz.
I know you can use rooms and namespaces, and I know you can add authentication to the connect as middleware, but I have no idea what the best solution is to glue this all together. I will be using this socket for multiple purposes. For each purpose I am curious what the best way to go is (flow).
General CRUD messages: When someone posts a "foo" on the server side, it needs to also send this to that particular user. WHen someone deletes a "foo", it also needs to send something to this user. So this CRUD messaging should only be for one specific user (based on logged in user ID). How would structure those messages? Namespace for "foo"? Multiple event listeners: on "foo create", on "foo delete", on "foo update?" How to make sure you only send to this user?
I have multiple pages on the client side, for the respective CRUD endpoint. So when I am on the "foo" page, I need to get updates on the "foo" backend object. How can I accomplish this?
General server side messages: I will be running long-running scripts on the server side, started by a user (or by a time trigger). If I go to that page in react and if there are long running scripts active that belong to me, I need to see those logging. (but again, they are personal so those messages are only for me).
Thanks in advance if you need more clarification just ask me and I will add this to my question.
EDIT:
I think the CRUD part can better be created as having only an "listener for updates" (like the firebase onSnapshot). So on page foo, I will listen to updates in the foo database, but the updates or creations are dont through normal REST API. Is that indeed the better way?
You can authenticate socket.io connection in 'connection' event or using middleware - doc.
Also you can use some package from npm, for example this
After authentication store user data in socket object or as separate object in 'connection' event scope.
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
});
So after you can use user object in other events for this connection.
Private messages according to your task I implemented in my project using rooms. Here abstract example:
// this is just a helper to get room name according to userId
getUserRoomName(userId) {
return `user_${userId}`;
}
// function to send data to user
sendToUser(userId, event, data) {
io.to(getUserRoomName(userId)).emit(event, data);
}
// in 'connection' event add join to user room
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
// join to private room
socket.join(getUserRoomName(user.userId), () => {
// some logic
});
});
So, when the user connected to socket.io we join his connection to private user room. And every connection of same user will be joined to same room, so we can isolate data sending messages to this room.
Using sendToUser method you can send any type of data to all user connections from any part of your application:
sendToUser(userId, 'foo_create', data);
OR
sendToUser(userId, 'foo', {
action: 'create',
// some other data
});
How can I (or is there a way) get the number of subscribers connected and watching a publisher's stream, using OpenTok's REST API?
I know that I can count the connections on the server side by listening respective event, but if there is a REST API that I can query session information including the connected subscribers, I prefer to use it.
OpenTok QA staff here.
As Carlos says, the answer is simple. You can't. There is no API to get the number of subscribers that are actually connected to a session. You have to handle it by yourself, using the events that the platform provides.
Check the official API docs: https://tokbox.com/developer/sdks/js/reference/ConnectionEvent.html, the session object dispatches some events that tell you when some one connects or disconnects from your session:
...
var session = OT.initSession(apiKey, sessionID);
session.on('connectionCreated', function(e) {
connectionCount++;
...
});
session.on('connectionDestroyed', function(e) {
connectionCount--;
...
});
Currently i am building app using Sails server as a backend and iOS Swift as a client.
I am able to emit from iOS side to create user and receive data from server side, but i am not able to get PubSub methods(message, publishAdd, Update,etc) provided by sails.
Here is my server side code
User.subscribe(req, [userdata.id]);
Now when there is any update occurred on above User recored then i need to receive event like
socket.on("userupdate") { data in
print("user 1 ------------>", socket)
}
But i am not receiving any event on client side.
Any help will be appreciated :)
Thanks
Actually i am missing watch on server side also need to do changes on client side like below
socket.onAny({ data in
print("ANy event =====> \(data)" )
})
socket.on("user", callback: {
data,ack in
print("call back called \(data) \(ack)")
})
socket.OnAny user to trace which event being called while there is any event for publish, update,etc
Here are server side changes i have done
User.subscribe(req, newUser, ['message']);
User.watch(req);
User.publishCreate(newUser);
Hope this will be useful for developer looking to implemented socket with Sails server.
I am developing one application using easyrtc tool with wavemaker tool.For a new user easy rtc provides automatically created easyrtc id.
In the chat window the random id are shown..i want to replace these ids with applications username..
I have find one solution where we have to set easyrtc.setUsername("") in client js file before calling easyrtc.connect function..
But this not solves the problem...
any help would be appriciated
Now, you can do it easyer, use this function:
easyrtc.idToName(easyrtcid)
Their is no easy way to solve this. However, it is possible using a mixture of server-side and client-side events to pass/receive user metadata when connected/disconnected. Here is a simple way to achieve this:
When a client connects to the server send user metadata via sendServerMessage on the connected event listener via client-side library. The server then receives the message from the client and stores the metadata about the user with that particular easyrtcid in a central location (ex. redis). The message sent to the server can be a json object with user metadata in a structured format. See details on connecting and sending a message to the server here: easyRTC Client-Side Documentation
When a client disconnects from the server remove their information from the data store using the onDisconnect event on the server side. This event provides a connectionObj which includes the easyrtcid of the user who disconnected. Use this identifier to remove the user from the datastore. You could also call generateRoomList() on the connectionObj to remove the user by easyrtcid and room from your datastore. You can read about the connection object here: connectionObj easyRTC documentation
Here is some example code of how to do this:
// Client-Side Javascript Code (Step 1)
easyrtc.connect('easyrtc.appname', function(easyrtcid){
// When we are connected we tell the server who we are by sending a message
// with our user metadata. This way we can store it so other users can
// access it.
easyrtc.sendServerMessage('newConnection', {name: 'John Smith'},
function(type, data){
// Message Was Successfully Sent to Server and a response was received
// with a the data available in the (data) variable.
}, function(code, message) {
// Something went wrong with sending the message... To be safe you
// could disconnect the client so you don't end up with an orphaned
// user with no metadata.
}
}, function(code, message) {
// Unable to connect! Notify the user something went wrong...
}
Here is how things would work on the server-side (node.js)
// Server-Side Javascript Code (Step 2)
easyrtc.events.on('disconnect', function(connectionObj, next){
connectionObj.generateRoomList(function(err, rooms){
for (room in rooms) {
// Remove the client from any data storage by room if needed
// Use "room" for room identifier and connectionObj.getEasyrtcid() to
// get the easyrtcid for the disconnected user.
}
});
// Send all other message types to the default handler. DO NOT SKIP THIS!
// If this is not in place then no other handlers will be called for the
// event. The client-side occupancy changed event depends on this.
easyrtc.events.emitDefault("disconnect", connectionObj, next);
});
Redis is a great way to keep track of the users connected if using rooms. You can use an hash style object with the first key being the room and each sub key/value being the users easyrtcid with a JSON hash of the metadata stored as it's value. It would have to be serialized to a string FYI and de-serialized on the lookup but this is simple using Javascript using the JSON.stringify and JSON.parse methods.
To detect occupancy changes in your application you could add a event listener to the easyrtc.setRoomOccupantListener method on the client-side and then when this event is fired send another message to the server to get all the users connected to it from the datastore.You would have to listen for a separate message on the server-side and return the users in the store deserialized back to the client. However, depending on your application this may or may not be needed.
We are trying to move some of our app to use backbone and tastypie. I have the REST api set up and it is working on some basic examples. However, there are a few issues where currently we post an ajax request to a custom url, and in that view do a few things like
make related objects
call a few related functions
However, now that I've switched some of this functionality to using backbone and the REST api, I'm not sure where all of that should go!
For example, I had a view to make a Message, and when I made a Message, I also made a Notification and called a function to add some points to the user. Something like
def ajax_send_message(request):
## ... set up some variables ...
## Make the new message
message = Message(user=user, content=message)
message.save()
## Make the notification
notification = Notification(message=message)
notification.save()
## Give the user points
user.add_points_for_message();
return json_response({"status": "ok"})
Now--am I just supposed to do this all in JavaScript? I have a Message Backbone model as well.
// Create message backbone object
var msg = new Message({content:content, user: user});
// Post to server
msg.save();
// Add to backbone collection
messages.add(msg);
I've looked at different parts of tastypie, and it seems you can create custom URL endpoints, and also do validation, but that doesnt seem like the right spot to call related methods. It seems that calling related methods goes against the REST part of it---but then where are they supposed to go?
If I want to add some logic to backbone only when an object is created, where does that go?
The first thing I would suggest is to switch your mindset to an event-based model, where your code reacts to events. In your example above, you save the model to the server then immediately dd it to the collection. How do you even know that the model was saved correctly? That procedural style of programming works better in a synchronous, server-side style of programming.
In the asynchronous world of client-side programming, you make a request and then set up callbacks which determine what will happen next, depending on the events your are listening for. In your case, you want to react a certain way when the message is saved successfully, correct? You can define a success callback for your save operation like so:
msg.save({
success: function(model, response, options) {
messages.add(model);
// code to add notification
// code to add points
}
});
Basically, you are saying "I would like to save this model, and then listen for a success event. When the event comes in, execute the following code." Notice also that I am adding the model returned from the API to the collection, since this is the exact object that was persisted to the server so it's more appropriate to add than the model you created.