UNPKG

@harishreddym/baqend

Version:

Baqend JavaScript SDK

149 lines (128 loc) 3.96 kB
'use strict'; const CommunicationError = require('../../lib/error/CommunicationError'); const WebSocket = require('./websocket').WebSocket; const lib = require('../../lib/baqend'); /** * @alias connector.WebSocketConnector */ class WebSocketConnector { /** * @param {connector.Connector} connector a connector * @param {String=} url The websocket connect script url * @return {connector.WebSocketConnector} a websocket connection */ static create(url) { let websocket = this.websockets[url]; if (!websocket) { websocket = new WebSocketConnector(url); this.websockets[url] = websocket; } return websocket; } /** * @param {String} url */ constructor(url) { this.observers = {}; this.socket = null; this.url = url; } open() { if (!this.socket) { const socket = new WebSocket(this.url); let socketPromise; const handleSocketCompletion = (error) => { // observable error calls can throw an exception therefore cleanup beforehand let isError = false; if (this.socket === socketPromise) { isError = socket.readyState !== 3; this.socket = null; } let firstErr; Object.keys(this.observers).forEach((id) => { const observer = this.observers[id]; delete this.observers[id]; // unsubscribe to allow resubscriptions if (!observer) { return; } try { if (isError) { observer.error(new CommunicationError(null, error)); } else { observer.complete(); } } catch (e) { if (!firstErr) { firstErr = e; } } }); if (firstErr) { throw firstErr; } }; socket.onerror = handleSocketCompletion; socket.onclose = handleSocketCompletion; socket.onmessage = (event) => { const message = JSON.parse(event.data); message.date = new Date(message.date); const id = message.id; if (!id) { if (message.type === 'error') { handleSocketCompletion(message); } return; } const observer = this.observers[id]; if (observer) { if (message.type === 'error') { observer.error(new CommunicationError(null, message)); } else { observer.next(message); } } }; socketPromise = new Promise((resolve) => { socket.onopen = resolve; }).then(() => socket); this.socket = socketPromise; } return this.socket; } close() { if (this.socket) { this.socket.then((socket) => { socket.close(); }); this.socket = null; } } /** * @param {util.TokenStorage} tokenStorage * @param {string} id subscription ID * @return {connector.ObservableStream} The channel for sending and receiving messages */ openStream(tokenStorage, id) { const stream = new lib.Observable((observer) => { if (this.observers[id]) { throw new Error('Only one subscription per stream is allowed.'); } this.observers[id] = observer; return () => { // cleanup only our subscription and handle resubscription on the same stream id correctly if (this.observers[id] === observer) { delete this.observers[id]; } }; }); stream.send = (message) => { this.open().then((socket) => { message.id = id; if (tokenStorage.token) { message.token = tokenStorage.token; } const jsonMessage = JSON.stringify(message); socket.send(jsonMessage); }); }; return stream; } } Object.assign(WebSocketConnector, /** @lends connector.WebSocketConnector */ { /** * Map of all available connectors to their respective websocket connections * @type connector.Connector[] */ websockets: {}, }); module.exports = WebSocketConnector;