RxJS: Observable.webSocket() get access to onopen, onclose… - javascript

const ws = Observable.webSocket('ws://…');
ws.subscribe(
message => console.log(message),
error => console.log(error),
() => {},
);
I want to observe my WebSocket connection with RxJS. Reacting to onmessage events by subscribing to the observable works like a charm. But how can I access the onopen event of the WebSocket? And is it possible to trigger the WebSocket .close() method? RxJS is pretty new to me and I did research for hours, but maybe I just don't know the right terms. Thanks in advance.

Looking at the sourcecode of the Websocket there is a config object WebSocketSubjectConfig which contains observables which you can link to different events. You should be able to pass a NextObserver typed object to config value openObserver like so:
const openEvents = new Subject<Event>();
const ws = Observable.webSocket({
url: 'ws://…',
openObserver: openEvents
});
openEvents
.do(evt => console.log('got open event: ' + evt))
.merge(ws.do(msg => console.log('got message: ' + msg))
.subscribe();

The link of #Mark van Straten to the file is dead. The updated link is here. I also wanted to highlight the usage as suggested in the docs. A in my opinion better copy and paste solution to play around with:
import { webSocket } from "rxjs/webSocket";
webSocket({
url: "wss://echo.websocket.org",
openObserver: {
next: () => {
console.log("connection ok");
},
},
closeObserver: {
next(closeEvent) {
// ...
}
},
}).subscribe();

Related

How to mock/simulate in the jest test PubNub event which added in pubnub.addListener?

I have a package that uses PubNub, I', try to cover all package files with jest tests, but I have a problem: I can't find the way to cover events inside the listener
// add listener
const listener = {
// Need to cover these cases (status and message)
status: (statusEvent) => {
if (statusEvent.category === "PNConnectedCategory") {
console.log("Connected");
}
},
message: (messageEvent) => {
// Process message
}
};
this.pubnub.addListener(listener);
this.pubnub.subscribe({
channels: [this.channel]
});
I attached a screen with the part which I need to cover test
[![uncovered file part][1]][1]
How to mock/simulate in the jest test PubNub event which added in pubnub.addListener?
describe("publishPubNub test suites", () => {
const sideEffect = function (options) {
pubnubService.publishPubNub(options);
return true;
}
it("successfull", () => {
//TODO: mock event here
const isCompleted = sideEffect(publishPubNubOptions)
expect(isCompleted).toBeTruthy();
});
})
Thanks for any helps or advice.
[1]: https://i.stack.imgur.com/tF3c2.png
The listener status handler will be invoked whenever a connection is established (or some other connection event happens). The message handler will be invoked whenever your application receives a message that it has previously subscribed to.
You could either:
Test your application against a real PubNub instance, though that would require an Internet connection.
Create a mocked library. PubNub does not offer an official mocked library so you would need to roll your own. Something like the following based on your image:
'use strict';
class PubNub {
constructor(pubKey, subKey, uniqueId) {
this.listener = {}
}
addListener(listener) {
this.listener = listener;
}
subscribe(channelsObj) {
this.listener.status({"category": "PNConnectedCategory"})
}
publishPubNub(options) {
this.listener.message({"message": {"request": {"decision": "approved"}}})
}
}
module.exports = PubNub;

Ember.JS concurrency task, perform() is not a function

I was trying to convert a function over to a task. Here is the original code:
Call:
this.socketConnect(endpoint, token);
Function:
socketConnect = async (token, endpoint) => {
this.socket = new WebSocket(endpoint + '?auth=' + token);
this.socket.addEventListener('open', () => {
this.socket.addEventListener('message', event => this.handleMessage(event));
this.socket.addEventListener('close', event => this.retryConnection(event, endpoint));
});
}
I've been following structure on implementing Ember tasks. It all compiles with no issue, however when it gets called, it outputs that this.socketConnect(...) is not a function. Before hand I didn't have the return below and it output that this.socketConnect wasn't a function. Here is my current code for a task.
Import:
import { task } from 'ember-concurrency';
Call:
this.socketConnect(endpoint, authToken).perform();
Function:
#task *socketConnect(endpoint, token) {
yield async () => {
this.socket = new WebSocket(endpoint + '?auth=' + token);
this.socket.addEventListener('open', () => {
this.socket.addEventListener('message', event => this.handleMessage(event));
this.socket.addEventListener('close', event => this.retryConnection(event, endpoint));
});
return;
};
}
New to this, so I'm guessing there's something small I'm missing. It matches other uses. Also if anyone could help on the benefits of switching a websocket generation function to a task? Any help would be appreciated, thank you.
The #task decorator isn't part of the official ember-concurency package yet. The official version lives in ember-concurrency-decorators for now. You'll need to
ember install ember-concurrency-decorators
and then you can do
import { task } from 'ember-concurrency-decorators';
To use it.
Alternatively you can use a different syntax if you don't want another dependency.
import { task } from 'ember-concurrency';
class Foo {
#(task(function*() {
// ...
}).restartable())
doStuff;
executeTheTask() {
this.doStuff.perform();
}
}
To call the task the syntax is:
this.socketConnect.perform(endpoint, authToken);
As you're not calling socketConnect directly, you want to call the method that ember concurrency generates for you.

How can we send messages from the main process to renderer process in Electron

I'm playing with electron for the first time. Trying to create a text editor
In render I'm sending a message to indicated the content has changed and needs saving:
document.getElementById('content').onkeyup = e => {
ipcRenderer.send('SAVE_NEEDED', {
content: e.target.innerHTML,
fileDir
})
}
Then ipcMain receives it no problem. On the menu I have this:
{
label: 'Save',
click: _ => {
saveFile(message)
// trying:
// ipcMain.send('SAVED', 'File Saved')
},
accelerator: 'cmd+S', // shortcut
}
So that the user knows the files has have. But that doesn't seem to work. Is there any other way to do this? I would have thought "save" would be a pre-created role (sort of)
To send a message back to the renderer you would use:
win.webContents.send('asynchronous-message', {'SAVED': 'File Saved'});
And receive it like this:
ipcRenderer.on('asynchronous-message', function (evt, message) {
console.log(message); // Returns: {'SAVED': 'File Saved'}
});
Where asynchronous-message is simply the channel you're sending it to. It can literally be anything.
webContents.send Docs
alternatively - when you want to respond to an event received from renderer process you can do something like this:
ipcMain.on("eventFromRenderer", (event) => {
event.sender.send("eventFromMain", someReply);
}
Source: https://electronjs.org/docs/api/ipc-main
Here is what I tinkered with (using the new way of doing with contextBridge), my use was simply to have a menuItem call a navigation event in my React code:
// preload.js
const exposedAPI = {
// `(customData: string) => void` is just the typing here
onMenuNav: (cb: (customData: string) => void) => {
// Deliberately strip event as it includes `sender` (note: Not sure about that, I partly pasted it from somewhere)
// Note: The first argument is always event, but you can have as many arguments as you like, one is enough for me.
ipcRenderer.on('your-event', (event, customData) => cb(customData));
}
};
contextBridge.exposeInMainWorld("electron", exposedAPI);
// typing for curious peoples
onMenuNav(cb: ((customData: string) => void)): void;
// renderer.tsx
// Call it in the renderer process, in my case it is called once at the end of renderer.tsx.
window.electron.onMenuNav(customData => {
console.log(customData); // 'something'
});
// in main process
const customData = 'something';
mainWindow.webContents.send('your-event', customData);
Old question but I found new good solution;
// on main process index.js
ipcMain.on('event-name', (event, data) => {
const value = 'return value';
event.reply(data.waitingEventName, value);
});
// on render frame index.html
const send =(callback)=>{
const waitingEventName = 'event-name-reply';
ipcRenderer.once(waitingEventName, (event, data) => {
callback(data);
});
ipcRenderer.send('event-name', {waitingEventName});
};
send((value)=>{
console.log(value);
});
From #raksa answer
You can send a request to main and get a response, rather than sending a response that might not be delivered.
Use this sample
//render process
ipcRenderer.send('hello', ['one', 'two', 'three']);
ipcRenderer.once('nice', (e, data) => {
console.log(data); //['one','two','three']
})
//main process
ipcMain.on('hello', (e, data) => {
e.reply('nice', data)
})

Detect if the user is connected to the internet?

I'd like to route the user to a certain screen, in case he is not connected to the internet.
I just can't detect if he is connected or not.
I tried this code, but did not work:
async componentWillMount()
{
if (!await NetInfo.isConnected)
{
this.props.navigation.navigate('Saved');
}
}
Any tested solution to suggest?
Try await NetInfo.isConnected.fetch()
ref : https://facebook.github.io/react-native/docs/netinfo.html#isconnected
You can check using NetInfo .
for that you have to add connectionChange event listener like this
componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectionChange.bind(this));
NetInfo.isConnected.fetch().done(
(isConnected) => { this.setState({ isConnected: isConnected }); }
);
and then remove the event listener in componentWillUnmount
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectionChange);
}
And finally the handler method for connection change. I am storing the status in device local storage you can do whatever you want.
handleConnectionChange = (isConnected) => {
if (isConnected) {
//ToastAndroid.show('Data sync in process...', ToastAndroid.SHORT);
AsyncStorage.getItem('offlineData')
.then((json) => JSON.parse(json))
.then((data) => {
console.log(JSON.stringify(data));
});
}
else { ToastAndroid.show('You are offline.', ToastAndroid.SHORT); }
this.setState({ isConnected: isConnected });
}
Don't forget to add NetInfo from react-native :)
Another solution to your case (one without using isConnected property) is to use the object returned from the event handler directly like that:
componentDidMount() {
NetInfo.addEventListener('connectionChange', this.handleNetworkChange);
}
componentWillUnmount() {
NetInfo.removeEventListener('connectionChange', this.handleNetworkChange);
}
handleNetworkChange = (info) => {
if (info.type === 'none') {
this.props.navigation.navigate('Saved');
}
};
According to NetInfo documentation:
connectionChange event fires when the network status changes. The argument to the event handler is an object with keys:
type: A ConnectionType (listed above)
effectiveType: An EffectiveConnectionType (listed above)
The connection type can be one of the following : none, wifi, cellular, unknown.
Ideally you can store this information to your redux store and the listener to a root component.
We had a weird bug when using isConnected similar to the one you mentioned #Gabriel Bleu but for us, the NetInfo.isConnected.fetch() returned false only when the Android device was awake after some period of inactivity.We used it to display offline warning for users, so the warning never left. I found this solution on a Spencer Carli's course and it seems to work better but depending on your needs, you might want to use isConnected combined with the above code.
This is a great example to check online or offline and even you can have connection change information too. Source
NetInfo.isConnected.fetch().then(isConnected => {
console.log('First, is ' + (isConnected ? 'online' : 'offline'));
});
function handleFirstConnectivityChange(isConnected) {
console.log('Then, is ' + (isConnected ? 'online' : 'offline'));
NetInfo.isConnected.removeEventListener(
'connectionChange',
handleFirstConnectivityChange
);
}
NetInfo.isConnected.addEventListener(
'connectionChange',
handleFirstConnectivityChange
);
There are two issues with your code currently.
In newer versions of react life-cycle method componentWillMount is deprecated.
Newer versions of react-native have extracted the NetInfo Module out of the core. Use #react-native-community/netinfo instead.
In order to achieve the desired behavior you should do something like this.
import NetInfo from "#react-native-community/netinfo";
class CheckConnection extends Component {
componentDidMount() {
NetInfo.fetch().then(state => {
handleConnectionState(state)
});
}
handleConnectionState(state) {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
... your code to handle the lack of connection
}
}

How to Observe a Custom Event using RXJS in Angular 2?

I have a third party library that I am intending to integrate with RxJS. This is a messaging library called Tiger Text. According to them I can listen to an event called messages and when the stream has a message I can use it to further utilize it. The code snippet for the same is as follows:-
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' })
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session)
})
function onSignedIn(session) {
console.log('Signed in as', session.user.displayName)
client.messages.sendToUser(
'someone#mail.com',
'hello!'
).then(function (message) {
console.log('sent', message.body, 'to', message.recipient.displayName)
})
client.events.connect()
client.on('message', function (message) {
console.log(
'message event',
message.sender.displayName,
'to',
message.recipient.displayName,
':',
message.body
)
})
}
Now please have a look at the place where you have the below mentioned piece of code.
client.on('message', function (message) {
console.log(
'message event',
message.sender.displayName,
'to',
message.recipient.displayName,
':',
message.body
)
})
I wanted to know how to use RxJS so as to create an observable out of this piece of code so as to subscribe to the stream and whenever we have a change I take the new data and process it as I wish.
Please Advice.
For this use-cases you typically don't need to write a custom Observable and you can use just Observable.create(). Then it depends on whether you want to write a cold or a hot observable.
For cold Observables you create the producer of values when subscribing and close it when unsubscribing:
Observable.create(obs => {
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' });
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session);
});
client.on('message', function (message) {
obs.next(...);
});
return () => {
client.close(); // or whatever...
};
});
Or if you want to write a hot Observable the producer will exist independently on any subscriptions and just add/remove the listener:
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' });
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session);
});
Observable.create(obs => {
let listener = client.on('message', function (message) {
obs.next(...);
});
() => {
// remove the event listener somehow
listener.remove();
};
});
Sometimes you can see this solved by using a Subject but this is usually more complicated than using Observable.create() because then you need to handle the creation and tear down logic yourself and also Subjects have internal state.
Here's a very similar question as yours:
Subscribe to a stream with RxJS and twitter-stream-api module
Articles on the topics related to your question by the lead developer of RxJS:
https://medium.com/#benlesh/hot-vs-cold-observables-f8094ed53339
https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
https://medium.com/#benlesh/rxjs-dont-unsubscribe-6753ed4fda87
https://medium.com/#benlesh/learning-observable-by-building-observable-d5da57405d87
You can use fromEventPattern to create an observable from a custom event:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEventPattern';
const messages = Observable.fromEventPattern(
handler => client.on('message', handler),
handler => client.off('message', handler)
);
messages.subscribe(message => console.log(message));
You pass to fromEventPattern functions that add and remove the event handler using the custom API's add and remove mechanism. You've not included it in your question, but I've assumed the API you're using implements an off method.

Categories

Resources