class instance wont save socket inside itself - socket.io

Whenever a new user connects to socket.io an event handler will create a new class for that user called Events
const eventHandler = socket => {
new Events(socket);
};
and Events constructor will set its socket to the socket its been given upon creation
class Events {
constructor(socket) {
this.socket = socket;
this.socket.on('test', this.test)
}
test() {
console.log(this.socket); // logs undefined
}
}
the issue is why this.socket returns undefined? I think I'm missing how something in socket.io works, help is appreciated

I got my own answer, It actually has nothing to do with socket.io but rather how this works, further reading: How to access the correct `this` inside a callback?

Related

Can we use server sent events in nestjs without using interval?

I'm creating few microservices using nestjs.
For instance I have x, y & z services all interconnected by grpc but I want service x to send updates to a webapp on a particular entity change so I have considered server-sent-events [open to any other better solution].
Following the nestjs documentation, they have a function running at n interval for sse route, seems to be resource exhaustive. Is there a way to actually sent events when there's a update.
Lets say I have another api call in the same service that is triggered by a button click on another webapp, how do I trigger the event to fire only when the button is clicked and not continuously keep sending events. Also if you know any idiomatic way to achieve this which getting hacky would be appreciated, want it to be last resort.
[BONUS Question]
I also considered MQTT to send events. But I get a feeling that it isn't possible for a single service to have MQTT and gRPC. I'm skeptical of using MQTT because of its latency and how it will affect internal message passing. If I could limit to external clients it would be great (i.e, x service to use gRPC for internal connections and MQTT for webapp just need one route to be exposed by mqtt).
(PS I'm new to microservices so please be comprehensive about your solutions :p)
Thanks in advance for reading till end!
You can. The important thing is that in NestJS SSE is implemented with Observables, so as long as you have an observable you can add to, you can use it to send back SSE events. The easiest way to work with this is with Subjects. I used to have an example of this somewhere, but generally, it would look something like this
#Controller()
export class SseController {
constructor(private readonly sseService: SseService) {}
#SSE()
doTheSse() {
return this.sseService.sendEvents();
}
}
#Injectable()
export class SseService {
private events = new Subject();
addEvent(event) {
this.events.next(event);
}
sendEvents() {
return this.events.asObservable();
}
}
#Injectable()
export class ButtonTriggeredService {
constructor(private readonly sseService: SseService) {}
buttonClickedOrSomething() {
this.sseService.addEvent(buttonClickedEvent);
}
}
Pardon the pseudo-code nature of the above, but in general it does show how you can use Subjects to create observables for SSE events. So long as the #SSE() endpoint returns an observable with the proper shape, you're golden.
There is a better way to handle events with SSE of NestJS:
Please see this repo with code example:
https://github.com/ningacoding/nest-sse-bug/tree/main/src
Where basically you have a service:
import {Injectable} from '#nestjs/common';
import {fromEvent} from "rxjs";
import {EventEmitter} from "events";
#Injectable()
export class EventsService {
private readonly emitter = new EventEmitter();
subscribe(channel: string) {
return fromEvent(this.emitter, channel);
}
emit(channel: string, data?: object) {
this.emitter.emit(channel, {data});
}
}
Obviously, channel can be any string, as recommendation use path style.
For example: "events/for/<user_id>" and users subscribed to that channel will receive only the events for that channel and only when are fired ;) - Fully compatible with #UseGuards, etc. :)
Additional note: Don't inject any service inside EventsService, because of a known bug.
#Sse('sse-endpoint')
sse(): Observable<any> {
//data have to strem
const arr = ['d1','d2', 'd3'];
return new Observable((subscriber) => {
while(arr.len){
subscriber.next(arr.pop()); // data have to return in every chunk
}
if(arr.len == 0) subscriber.complete(); // complete the subscription
});
}
Yes, this is possible, instead of using interval, we can use event emitter.
Whenever the event is emitted, we can send back the response to the client.

Handle data after http get request in angular

I have a service that requests data from a get method, I'd like to map the response to an object storing some Ids and use those Ids to make other http requests.
I was told this isn't usually done in a callback manner, I looked at this How do I return the response from an asynchronous call? but I don't think it's the usual way to implement services, any hints are very appreciated.
Tried adding in onInit/constructor method in angular to be sure the object was filled before other methods were called without success.
#Injectable ()
export class ContactService {
storeIds;
getIds(callback: Function) {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
callback(response);
});
getIds(res => {
this.storeIds = {
profileId: res.profile,
refIds: res.refIds
}
}
)
// this.storeIds returns undefined as it's an async call
this.http.post<any>(WebserviceUrl + this.storeIds.profileId , data, headers )
// .....Many other web services that relay on this Ids
}
Just create another service called StoreIdsService. Update the response you get from your first api call 'getIds' in the StoreIdsService. The idea is to have StoreIdsService as singleton service to keep state of your storeIds. You can inject StoreIdsService in anywhere component you want to get the storeIds.
Its one of manyways to share data in angular between components.
Please refer to this answer someone has posted.
How do I share data between components in Angular 2?
You can simply assign the service response to the storeIds property inside the subscribe method. and call the subsequent services inside it if you need.
#Injectable ()
export class ContactService {
storeIds;
getIds() {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
this.storeIds = {
profileId: response.profile,
refIds: response.refIds
}
this.otherapicall1();
this.otherapicall2();
});
}

How can I ensure the writable flag is true before send()?

I am creating an abstraction layer for Dart WebSocket who is also protocol compatible with Socket.IO, but it has a problem I can not solve.
The follow code convert the HttpRequest into a WebSocket and save the socket instance on the Transport... Here you can see I change the value of the writable flag to true in order to inform the socket is open.
WebSocketTransformer.upgrade(req).then((socket) {
this._socket = socket;
this.writable = true;
this._socket.handleError(() {
this.onClose();
});
// start listen the socket;
socket.listen((packet) {
this.onData(packet);
});
}).catchError((Exception e) {
this.onError('Can\'t conenct with the socket.', e.toString());
});
(The full code can be founded here.)
When I debug the code first the debugger stops inside that closure and only then stops here, where the check is make but writable still false :/
void flush([_]) {
if (this._readyState != SocketStates.closed && this.transport.writable &&
!this._writeBuffer.isEmpty) {
(The full code can be founded here)
I really need help...
Your constructor makes an async call but doesn't return a future (not possible in a constructor)
WebSocketTransformer.upgrade(req).then
Use a static method instead which returns Future<WebSocketTransformer> or use an init method where you initiaize your class after creation.
There might be other problems but yor code does not show how you use your class therefore its hard to tell.

SignalR: Task Join() is not called

I'm trying to create a chat application using SignalR. To make it possible to send private messages I want to assign clients to a group with the name of their profileID. So I can simply call the addMessage function of the group to send to a specific client.
When I go to this page: https://github.com/SignalR/SignalR/wiki/Hubs
It tells me to add a function to the Hub called Join(). In here I can add the incomming client to a group. So I created this code:
[HubName("Chat")]
public class ChatHub : Hub
{
public Task Join()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Profiel_DataHelper profiel = new Profiel_DataHelper(HttpContext.Current.User.Identity.Name);
return Groups.Add(Context.ConnectionId, profiel.ProfielID.ToString());
}
else
{
return null;
}
}
.....
When I want to call a specific client I use this code:
var context = GlobalHost.ConnectionManager.GetHubContext();
context.Clients.Group(profielidNaar).addTyptOnline(profielidVan);
But when I run the program the Join() Task is not being called at all, therefore my call to the group is also not working.
What am I doing wrong?
Join is a method on your hub that you need to call from the client. Nobody is going to call it for you and "Join" isn't a special method that gets called automatically. The documentation is showing you how to declare method that "can" be called from the client.
There's other ways to know when clients connect, reconnect and disconnect and it's detailed here:
https://github.com/SignalR/SignalR/wiki/Hubs#detecting-connect-reconnect-and-disconnect-clients-in-hubs

SignalR opens new connection for the same user

I am new to SignalR and trying to implement long running result pooling. I have added JS to my ASP.NET MVC app and created bug class.
JS
<script type="text/javascript">
var message= $.connection.messageHub;
$(function () {
message.addMessage = function (htmlstring) {
alert(htmlstring);
};
$.connection.hub.start(function () {
message.longRunningMethod('#HttpContext.Current.Session.SessionID');
});
});
</script>
c# code
[HubName("messageHub")]
public class MessagesHub : Hub
{
public void longRunningMethod(string sessionId)
{
var repeatChecking = 0;
while (repeatChecking < 3000000)
{
Caller.addMessage("Test");
repeatChecking++;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}
The code works fine but there is one problem. Every time the same user refresh web page new Hub class is created and new longRunningMethod method is executed. I would like to resume connection and attach to the same hub instance and resume receiving messages. Could anyone explain how to implement this?
Hubs are created and destroyed very frequently so never put any data that you expect to last on them (unless it's static).
I'm not quite sure why you're looking to have a long running method that can take in data (because SignalR is always up and available to take in/handle data) but here's how you can do it:
Checkout the SignalR stock ticker example (you can pull it in via Nuget). It creates a single instance class that fires up a timer. That timer is used to broadcast data down to the clients. https://github.com/SignalR/SignalR-StockTicker
You can also check out ShootR. It's a multiplayer game built with SignalR that does much of the same. Creates a background timer that acts as the game loop on the server and then pushes data down to clients. https://github.com/NTaylorMullen/ShootR
Ultimately your solution will involve either making a singleton or a static timer that acts as your "long" running method.
Hope this helps!

Resources