Server Sent Event with NestJS - nestjs

I would like send an event when a module is created, updated or deleted. I read the documentation NestJS SSE but it didn't help.
Here is how my methods look like and already created a controller.
addNewModule(data: ApiGateway.Event.AddModule.IPayload) {
this.logger.debug('Handle event on add new module!');
this.logger.debug(`Previous modules state: ${this.modules}`);
this.modules.push(data.moduleInfo);
this.logger.debug(`New modules state: ${this.modules}`);
}
removeModule(data: ApiGateway.Event.RemoveModule.IPayload) {
this.logger.debug('Handle event on add new module!');
this.logger.debug(`Previous modules state: ${this.modules}`);
this.modules = this.modules.filter(module => {
return module.name !== data.moduleName;
});
this.logger.debug(`New modules state: ${this.modules}`);
}
updateModuleSettings(data: ApiGateway.Event.UpdateModuleSettings.IPayload) {
this.logger.debug('Handle event on add new module!');
this.logger.debug(`Previous modules state: ${this.modules}`);
this.modules.find((module, index) => {
if (module.name === data.moduleInfo.name) {
this.modules[index] = data.moduleInfo;
return true;
}
});
this.logger.debug(`New modules state: ${this.modules}`);
}
I would like to know the best way to implement SSE whenever one these methods is called. First time trying to use SSE with Nestjs.
Thanks in advance)

Related

Vue Electron stuck when calling some Node libraries asynchronously

Context
I'm trying to build an app using the following stack:
Vue.js
Electron
I've developed simple Vue apps for a while now and I understand the basics.
What I'm trying to do here is to combine Vue and Electron using this awesome tool: Vue CLI Plugin Electron Builder.
While I've successfully managed to code simple apps with this stack, I'm facing issues when it comes to exploit Electron's nodeIntegration which is supposed to give Node access directly to my Vue code.
Issue
My asynchronous calls to some methods of the systeminformation library seem to be stuck (sometimes).
That is, when I want to assign a value my view's data by calling an asynchronous method of the systeminformation library, Vue seems to hang and await indefinitely.
The weired thing is that, sometimes, when I force refresh the page, I can briefly see in my console the log supposed to be output when the method returns data.
It's almost like refreshing the page forces Vue or something else to update.
I'd suspect the following leads but I might be wrong:
Vue's reactivity issue in my way of declaring and/or assigning.
Node integration issue making Vue+Node not "bounded" properly.
Electron's packaging of a Node library missing configuration to be used in Vue "as if it was a regular JS library".
The latter point is unclear to me as I've always used Electron+Vue to access Node libraries the same way I'd do for web libraries.
There might more to it that could lead to these issues but this is as far as my knowledge goes...
Steps to reproduce
You can create a Vue+Electron as follows:
vue create myapp
vue add electron-builder
yarn add systeminformation
I'm leaving default values for Vue CLI's interactive project creation as they don't have an impact on my issue.
As per Vue CLI Plugin Electron Builder's documentation on nodeIntegration, my vue.config.js file looks like this:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
},
},
};
You can then use the snippets in Scenario 1 and Scenario 2.
Scenario 1
Let's consider the following example where I assign a value using Axios:
<template>
<pre>{{ test }}</pre>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
test: null,
};
},
async mounted() {
console.log("Getting data from Axios...");
// Assign a value to the "test" data with an asynchronous HTTP call with Axios.
this.test = (
await axios.get("https://cat-fact.herokuapp.com/facts")
).data;
// It gets there as soon as the server responds.
console.log("Got it!", this.test);
},
};
</script>
Here, the asynchronous call works as expected, my test data updates properly and shows in my view as soon as the Axios call got an answer.
Scenario 2
Now, if I use the same logic to get my data from a method of the systeminformation library:
<template>
<pre>{{ test }}</pre>
</template>
<script>
import systeminformation from "systeminformation";
export default {
data() {
return {
test: null,
};
},
async mounted() {
console.log("Getting data from systeminformation...");
// Assign a value to the "test" data with an asynchronous call to a method of systeminformation.
this.test = await systeminformation.getStaticData();
// It just won't get there most of the time...
console.log("Got it!", this.test);
},
};
</script>
In that case, my view won't show the test data as Vue seems to hang on the systeminformation call indefinitely.
The console log won't even show as the await statement appears to make the mounted hook stuck.
I just tested systeminformation.getStaticData() function in my own Electron + Vue app and here's results:
It executes code asynchorously but the operation is so heavy, it makes the app almost completely unresponsive. It repeatedly spawns ~30 Node child processes effectively blocking 3-4 CPU threads.
I think it might be a bug with getStaticData() function. You might want to create a new issue in their repo and report it.
Solution
Either don't use this particular function and get all the needed info by running other systeminformation functions.
OR
Enable node integration in workers:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true
}
Execute this function in a worker, rather than on the main thread, and send the results back to your component, then terminate the worker. Though when you do this, open task manager and make sure it terminates all the spawned child processes with it:
Vue component
import sysInfoWorker from 'worker-loader!./workers/sysInfoWorker'
data () {
return {
sysInfoWorker: null
}
},
mounted () {
this.initSysInfoWorker()
},
methods: {
initSysInfoWorker () {
this.sysInfoWorker = new sysInfoWorker()
try {
this.sysInfoWorker.onmessage = (event) => {
console.log('sysInfoWorker message:', event)
this.test = event
}
this.sysInfoWorker.onerror = (error) => {
console.log('sysInfoWorker error:', error)
}
}
catch (error) {
console.log(error)
}
},
startSysInfoWorker () {
this.sysInfoWorker.postMessage({
operation: 'run:getStaticData'
})
},
cancelSysInfoWorker () {
this.sysInfoWorker.postMessage('cancel:getStaticData')
// this.sysInfoWorker.terminate()
}
}
Worker
const systeminformation = require('systeminformation')
const state = { cancelled: false }
// Listen to messages from parent thread
self.addEventListener('message', (event) => {
if (event.data === 'cancel:getStaticData') {
state.cancelled = true
}
else {
state.cancelled = false
initWorker(event)
}
})
async function initWorker (event) {
if (event.data.operation === 'run:getStaticData') {
const info = await systeminformation.getStaticData()
self.postMessage({ info: info })
}
}
If the worker throws errors, try adding the following to your vue.config:
module.exports = {
configureWebpack: {
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' }
}

unsubscribe to an Observable created by of

When I create an observable and I am done with it I unsubscribe it directly
const data$ = this.httpClient.get('https://jsonplaceholder.typicode.com/todos/1').subscribe(res => {
console.log('live', res);
data$.unsubscribe(); // <---- works fine
});
But say if I create an Observable using of and try to do the same
const obs$ = of(1).subscribe(e => {
console.log('test', e)
obs$.unsubscribe(); // <--- Problem while creating Observable by of
});
Whats different between these 2 observables?
Your code should be import Subscription and unsubscribe in ngOnDestroy
import { Observable, Subscription, of } from "rxjs";
private subscription$: Subscription;
this.subscription$ = of(1).subscribe(e => {
console.log('test', e)
});
ngOnDestroy() {
this.subscription$.unsubscribe();
}
Update: What I understand is http request is an observable that potentialy have incoming value in the future and of simply create a list of observable
And from #cartant comment
of completes synchronously, so you are attempting to call
obs$.unsubscribe before the assignment to obs$ has been made.
If you have only one observable in your component that would be the easiest scenario for unsubscribing by the following way:
ngOnDestroy() {
this.subscription$.unsubscribe();
}
the more complex scenario is having multiple observables, at that moment you will not to push each observable while subscribing in subscriptions: Subscription[] and while destroying do unsubscribe all added subscriptions in the array ngOnDestroy(){
this.subscriptions.forEach(sub => sub.unsubscribe());
}
or you can use that npm package
For ensuring unsubscription in a component, I would recommend creating a Subject which emits in ngOnDestroy() like this:
destroy$ = new Subject<boolean>();
...
ngOnDestroy() {
this.destroy$.next(true);
}
And adding a takeUntil on each Observable in the component:
myObs$.pipe(
takeUntil(this.destroy$),
tap(stuff => doStuff())
).subscribe();
This way you avoid polluting things with loads of unnecessary Subscription variables.

what is the best method to multi request with callback or promise on node.js

I have a situation. I use Node.js to connect to a special hardware. let assume that I have two functions to access the hardware.
hardware.send('command');
hardware.on('responce', callback);
At first, I made a class to interface this to the application layer like this (I write simplified code over here for better understanding)
class AccessHardware {
constructor() {
}
updateData(callback) {
hardware.on('responce', callback);
hardware.send('command');
}
}
Now, the problem is that if there are multiple requests from the application layer to this access layer, they should not send multiple 'command' to the hardware. Instead, they should send one command and all of those callbacks can be served once the hardware answer the command.
So I update the code something like this:
class AccessHardware {
constructor() {
this.callbackList = [];
hardware.on('responce', (value) => {
while (this.callbackList.length > 0) {
this.callbackList.pop()(value);
}
});
}
updateData(callback) {
if (this.callbackList.length == 0) {
hardware.send('command');
}
this.callbackList.push(callback);
}
}
Of course, I prefer to use promise to handle the situation. so what is your suggestion to write this code with promise?
Next question, is this approach to make a 'list of callbacks' good?
I prefer to use promise to handle the situation. So what is your suggestion to write this code with promise?
You'd store a promise in your instance that will be shared between all method callers that want to share the same result:
class AccessHardware {
constructor(hardware) {
this.hardware = hardware;
this.responsePromise = null;
}
updateData() {
if (!this.responsePromise) {
this.responsePromise = new Promise(resolve => {
this.hardware.on('responce', resolve);
this.hardware.send('command');
});
this.responsePromise.finally(() => {
this.responsePromise = null; // clear cache as soon as command is done
});
}
return this.responsePromise;
}
}
Btw, if hardware is a global variable, there's no reason to use a class here.
Is the current solution to make a 'list of callbacks' good?
Yes, that's fine as well for a non-promise approach.

Calling Set on a Firebase Database Reference Causes the Webpage to Hang

I'm using React and Node to build an web-based interface to modify data in a Firebase database. I've previously used the Firebase Web SDK in this app to load data from the database, but I've encountered a strange issue with saving a user's changes. When I call set on a database reference (i.e. firebase.database().ref('/path/to/object').set({abc: 'xyz'})), the webpage hangs. Oddly enough, the changes are saved to the database, but the callbacks specified with then are never called (depending on the browser, a This page is slowing down your browser message appears). I'm certain that the issue is related to set as removing the call removes the hang (see save() in my code below).
import React from 'react'
import * as firebase from 'firebase'
// additional (unrelated) imports
export default class Editor extends React.Component {
constructor(props) {
super(props)
this.state = {
savingModal: false,
errorModal: false,
cancelModal: false,
errors: []
}
}
save() {
// this.form is a Reactstrap Form
// validate is a function that returns an array of strings
var errors;
// validate the form, show the errors if any
if ((errors = this.form.validate()) && errors.length > 0)
this.setState({errorModal: true, errors: errors})
else {
// collect is a function that returns an object with the data that the user entered
var x = this.form.collect()
// getEditorInfo is a function that returns info such as the type of object being edited
var info = this.getEditorInfo()
firebase.database().ref(`/${info.category}/${x.id}/`).set(x).then(() => {
this.closeEditor()
}, e => {
alert(`An unexpected error occurred:\n${e}`)
})
this.setState({savingModal: true})
}
}
// closes the window or redirects to /
closeEditor() {
if (window.opener)
window.close()
else
window.location.href = '/'
}
render() {
// BasicModal is a custom component that renders a Reactstrap Modal
// IndeterminateModal is a custom component that renders a Reactstrap Modal with only a Progress element
// EditorToolbar and EditorForm are custom components that render the UI of the page (I don't think they're relevant to the issue)
var info = this.getEditorInfo()
if (!info)
return <BasicModal isOpen={true} onPrimary={this.closeEditor} primary="Close" header="Invalid Request" body="ERROR: The request is invalid."/>
else
return <div>
<EditorToolbar onSave={this.save.bind(this)} onCancel={() => this.setState({cancelModal: true})}/>
<EditorForm ref={f => this.form = f}/>
<BasicModal toggle={() => this.setState({cancelModal: !this.state.cancelModal})} isOpen={this.state.cancelModal} header="Unsaved Changes" body={<p>If you close this window, your changes will not be saved.<br/>Are you sure you want to continue?</p>} primary="Close Anyway" primaryColor="danger" secondary="Go Back" onPrimary={this.closeEditor}/>
<IndeterminateModal style={{
top: '50%',
transform: 'translateY(-50%)'
}} isOpen={this.state.savingModal} progressColor="primary" progressText="Processing..."/>
<BasicModal toggle={() => this.setState({errorModal: false, errors: []})} isOpen={this.state.errorModal} header="Validation Error" body={<div><p>Please resolve the following errors:<br/></p><ul>{(this.state.errors || []).map(e => <li key={e}>{e}</li>)}</ul></div>} primary="Dismiss" primaryColor="primary"/>
</div>
}
}
UPDATE 1/8/2018
I came across this article today and I decided to try a new solution involving JavaScript's setTimeout method. In my situation, the freeze occurred after calling this.setState in my app then calling firebase.database().ref(path).set(data). I suspect the freezing issue was caused by this. I guess JavaScript couldn't handle the state change and Firebase operation all at once. This new solution is functional, more secure, faster, and simpler. Take a look:
// to perform your desired Firebase operation:
var timeout = 50 // give JS some time (e.g. 50ms) to finish other operations first
setTimeout(() => firebase.database().ref(path).set(data).then(
() => {/* ... */},
error => {/* ... */}),
timeout)
OLD SOLUTION
I ended up finding a solution. I'm sure it could be improved, but it works. I used the Web Workers API to run my Firebase code.
Create a new JavaScript file in your public folder (Node.js)
Download a copy of the Firebase web SDK source and place it in public
I chose to communicate with my Worker with postMessage
FirebaseWorker.js
self.onmessage = event => {
importScripts('./firebase.js') // import the local Firebase script
firebase.initializeApp({/* your config */})
const promise = p => p.then(
() => self.postMessage({error: null}),
e => self.postMessage({error: e})
const doWork = () => {
switch (event.data.action) {
case 'get':
promise(firebase.database().ref(event.data.path).once('value'))
break;
case 'set':
promise(firebase.database().ref(event.data.path).set(event.data.data))
break;
case 'update':
promise(firebase.database().ref(event.data.path).update(event.data.data))
break;
case 'remove':
promise(firebase.database().ref(event.data.path).remove())
break;
}
}
if (!firebase.auth().currentUser)
firebase.auth().signInWithEmailAndPassword(event.data.email, event.data.password).then(() => doWork())
else
doWork()
}
To use the Worker:
var worker = new Worker('FirebaseWorker.js')
worker.onmessage = event => {
if (event.data.error)
alert(event.data.error)
// ...
}
worker.postMessage({
data: {/* your data (required if set or update is used) */},
path: '/path/to/reference',
action: 'get, set, update, or remove',
email: 'someone#example.com',
password: 'password123'
})

websocket interrupted while angular2 project is loading on firefox

I've just started angular 2. I've done an angular2 sample as given in the https://angular.io/guide/quickstart
when I run the project in Firefox using
npm start
command in terminal, the connection get disconnected after output showing once.Error showing like
The connection to ws://localhost:3000/browser-sync/socket.io/?EIO=3&transport=websocket&sid=6YFGHWy7oD7T7qioAAAA was interrupted while the page was loading
Any idea about how to fix this issue ?
I don't know how you manage your web socket but you could consider using the following code. This idea is to wrap the web socket into an observable.
For this you could use a service like below. The initializeWebSocket will create a shared observable (hot) to wrap a WebSocket object.
export class WebSocketService {
initializeWebSocket(url) {
this.wsObservable = Observable.create((observer) => {
this.ws = new WebSocket(url);
this.ws.onopen = (e) => {
(...)
};
this.ws.onclose = (e) => {
if (e.wasClean) {
observer.complete();
} else {
observer.error(e);
}
};
this.ws.onerror = (e) => {
observer.error(e);
}
this.ws.onmessage = (e) => {
observer.next(JSON.parse(e.data));
}
return () => {
this.ws.close();
};
}).share();
}
}
You could add a sendData to send data on the web socket:
export class WebSocketService {
(...)
sendData(message) {
this.ws.send(JSON.stringify(message));
}
}
The last point is to make things a bit robust, i.e. filter received messages based on a criteria and implement retry when there is a disconnection. For this, you need to wrap our initial websocket observable into another one. This way we can support retries when the connection is lost and integrate filtering on criteria like the client identifier (in the sample the received data is JSON and contains a sender attribute).
export class WebSocketService {
(...)
createClientObservable(clientId) {
return Observable.create((observer) => {
let subscription = this.wsObservable
.filter((data) => data.sender!==clientId)
.subscribe(observer);
return () => {
subscription.unsubscribe();
};
}).retryWhen((errors) => {
return Observable.timer(3000);
});
}
}
You can see that deconnections are handled in this code using the retryWhen operator of observable.

Resources