SignalR subscribe to event with param - signalr

I need to subscribe to server events from client, so syntax is
Hub.client.[my event here]= function
It's working, but what if I need to subscribe with params, i.e. user need to see only unread messages or a list of messages with a criteria. So I want the same event subscription with modifiers. Like :
Hub.subscribe.messages({read:true}) = function ....
At server side I want to push updates only for clients that subscribed to this type of criteria and specific parameters.
I know I can make groups of clients but that seems to be overhead and not best practice.

I've used the client syntax:
Hub.client["MethodName_" + dynamicParam] = function
And server side syntax:
HubContext.Clients.All.Invoke(string.Format("MethodName_{0}", dynamicParam), data);
That way I can register to dynamic events based on the client selection of "dynamicParam"

Related

Streaming multiple events of different types using Axon

I am working on building streaming APIs for client/server communication using Axon and ServerSentEvents and not sure if it is possible to stream and identify multiple different events using Axon query update emitter and subscription query.
I am using Axon QueryUpdateEmitter.emit to emit the events from a projection based on different events. Emitter is emitting in projection whereas subscription query is taking place in the REST API that is supposed to stream the server sent events to client.
For example,
I want to emit 3 different events for a use case which creates, updates and deletes an entity.
I am wondering if we can emit different types of data from different events but still combine in one stream, i.e. send actual object upon entity create and update in the emitter but, since I don’t have any entity/data to emit in case of delete, I thinking whether to send a simple message for delete?
I also want a way to specify the type of event while emitting so when ServerSentEvent is build from subscription query, I can specify the type/action (for ex, differentiate between create or update event) along with data.
Main idea is to emit different events and add them in one stream despite knowing all events may not return exactly same data (create, update vs. delete) as part of one subscription query and to be able to accurately identify the event and specify in the stream of ServerSentEvents with appropriate event type.
Any ideas on how I can achieve this?
Here's how I am emitting an event upon creation using QueryUpdateEmitter:
#EventHandler
public void on(LibraryCreatedEvent event, #Timestamp Instant timestamp) {
final LibrarySummaryEntity librarySummary = mapper.createdEventToLibrarySummaryEntity(event, timestamp);
repository.save(librarySummary);
log.debug("On {}: Saved the first summary of the library named {}", event.getClass().getSimpleName(), event.getName());
queryUpdateEmitter.emit(
AllLibrarySummariesQuery.class,
query -> true,
librarySummary
);
log.debug("emitted library summary: {}", librarySummary.getId());
}
Since I need to distinguish between create and update so I tried using GenericSubscriptionQueryUpdateMessage.asUpdateMessage upon update event and added some metadata along with it but not sure if that is in the right direction as I am not sure how to retrieve that information during subscription query.
Map<String, String> map = new HashMap();
map.put(“Book Updated”, event.getLibraryId());
queryUpdateEmitter.emit(AllLibrarySummariesQuery.class,query → true,GenericSubscriptionQueryUpdateMessage.asUpdateMessage(librarySummary).withMetaData(map));
Here's how I am creating subscription query:
SubscriptionQueryResult<List<LibrarySummaryEntity>, LibrarySummaryEntity> result = queryGateway.subscriptionQuery(new AllLibrarySummariesQuery(),ResponseTypes.multipleInstancesOf(LibrarySummaryEntity.class),ResponseTypes.instanceOf(LibrarySummaryEntity.class));
And the part where I am building server sent event:
(.event is where I want to specify the type of event - create/update/delete and send the applicable data accordingly)
Flux<ServerSentEvent<LibrarySummaryResponseDto>> sseStream = result.initialResult()
.flatMapMany(Flux::fromIterable).map(value -> mapper.libraryEntityToResponseDto(value))
.concatWith((streamingTimeout == -1)? result.updates().map(value -> mapper.libraryEntityToResponseDto(value)): result.updates().take(Duration.ofMinutes(streamingTimeout)).map(value -> mapper.libraryEntityToResponseDto(value)))
.log()
.map(created -> ServerSentEvent.<LibrarySummaryResponseDto>builder()
.id(created.getId())
.event("library creation")
.data(created).build())
.doOnComplete(() -> {log.info("streaming completed");})
.doFinally(signal -> result.close());
As long as the object you return matches the expected type when making the subscription query, you should be good!
Note that this means you will have to make a response object that can fit your scenarios. Whether response is something you'd emit as the update (through the QueryUpdateEmitter) or a map operation from where you return the subscription query, is a different question, though.
Ideally, you'd decouple your internal messages from what you send outward, like with SSE. To move to a more specific solution, you could benefit from having a Flux response type. You can simply attach any mapping operations to adjust the responses emitted by the QueryUpdateEmitter to your desired SSE format.
Concluding, the short answer is "yes you can," as long as the emitted response object matches the expected update type when dispatching the subscription query on the QueryGateway.

How can I detect what room a user disconnected from?

I have an application that I need to pass the room the current user has just disconnected from to the server. The user can be in multiple rooms via multiple tabs.
I can detect that the user has left this way but it does not seem to be able to have data passed with it:
#socketio.on('disconnect')
def on_disconnect():
print(session['id'])
print("user left " )
In my client end I have tried this:
socket.on('disconnect', function () {
socket.emit('user_disconnect', {"channel": "{{symbol}}"});
});
This emit never goes through to the server though. I am guessing the window closes or changes before this can get called? How can I pass data to the server on the disconnect event?
I believe I have figured out a solution.
Utilizing request.sid, I can store the room name in a list along with this sid on the server-side join event.
#socketio.on('join')
def on_join(data):
active_rooms.append(json.dumps({'room':data['channel'],'socket_id':str(request.sid)}))
When the disconnect event is triggered I can view this same sid and use it as a lookup in the list. The sid appears to be unique for each socket connection so multiple tabs will result in a new sid.

Meteor - how can I empty out the collection of 10,000 objects I subscribed to after I no longer need it?

I have a template in which a user should be able to click on a button to bring up a modal and in the modal choose a handful of items out of a list of about 10,000 items which are displayed there to search or scroll through.
Since this collection is so big, I don't want to keep it around in memory when I don't absolutely need it.
So I would like to subscribe to this collection only when the modal is being viewed and I would like to ensure that I am unsubscribed if the modal is not being viewed.
Is there a way to explicitly unsubscribe from a collection?
There are a couple of ways you can do this:
Use the subscription handle
subscribe returns a handle you can call stop on. For example:
var handle = Meteor.subscribe('stuff');
handle.stop();
Use an autorun
Because an autorun will automatically start and stop subscriptions when its reactive dependencies change, this will work:
Tracker.autorun(function () {
if (Session.get('showingModal'))
Meteor.subscribe('stuff');
});
Side note - it may make more sense to use a method call for searching such a large data set rather than publishing the entire thing to the client. For example you can set a session variable whenever the user's query changes, then use an autorun to update the result set based on the method's return value.
https://docs.meteor.com/#/full/meteor_subscribe
Quoting the docs :
Meteor.subscribe returns a subscription handle, which is an object
with the following methods:
stop() Cancel the subscription. This will typically result in the
server directing the client to remove the subscription's data from the
client's cache.
So basically what you need to do is storing the subscription handle in a variable and call the stop method when you don't need those published documents anymore.
Note that if you're using iron:router (and you probably should), this is taken care of automatically for you on each route change, which is convenient but has the side effect of provoking a lot of sometimes unnecessary calls to Meteor.publish calls which are non trivial for the server and bandwidth... to address this matter you can use meteorhacks:subs-manager but it's another topic anyway.

Send message to set of users within a group with SignalR

Is it possible to send a message to a selection of clients within a Group in SignalR?
...without having to maintain your own lists of subscribers and using context.Clients.Clients(includeConnectionIds[])
SignalR does not have state by default, or they actually do with groups... But it is not very dynamic. I had the same issue as you are having. I needed to send a message to a subset of a group.. Or actually I needed to send to clients where age was between x and y...
This is impossible with groups so you have to implement the functionality your self.
Bloated and ugly...
I actually ended up using Xsocket.net instead where I can target client with lambda expressions without messing around with custom static lists/groups etc.
Do not know your requirements but sending to any subset if clients is done by:
this.SendTo(p => p.Age > x && p.Age < y, new {Message="hello world"},"message");
//Signature of the extension method is...
//SendTo<T>(this IXSocketController socket, Func<T, bool> expression, object obj, string eventname)
//So you can actually send to clients on any controller is specifying T
Best of luck with whatever you choose.
In SignalR 2.0 you can send to a many groups or many connections in a single call via Clients.Groups or Clients.Clients.

Signalr - Serialize callback as event not a function call?

In Signalr, is there any support for having events instead of callbacks.
Let me explain before you grab your pitchforks.
In following with the first example here
Clients.All.addContosoChatMessageToPage(name, message);
Wouldn't call a hub proxy's addContosoChatMessageToPage(name, message), but would dispatch a addContosoChatMessageToPage event with some extra information. (not asking that it be the same api call exactly)
The reason I'm asking all of this is because
This works much better alongside functional reactive programming frameworks like ELM and bacon.js
I don't want to do this myself and essentially create my own sub-framework. Of course I could always do Clients.All.CreateEvent(name,params...) where I'm continually calling back my method to do this event creation
I actually think events work better in some scenarios for separation of concerns.
Am I crazy? does something like this exist?
This is already supported. If you don't want to do the dispatching yourself and you know the name of the "event" or "method" at runtime you can do this:
IClientProxy proxy = Clients.All;
proxy.Invoke(name, args);
This lets you write code where you may not know the name of the event you're trying to callback on the client at compile time.

Resources