UNPKG

serverless-offline-msk

Version:

A serverless offline plugin that enables AWS MSK events

172 lines (146 loc) 5 kB
'use strict'; /** * Connection */ const WS = require('isomorphic-ws'); class Connection { constructor(sdk) { this.sdk = sdk; this.connection = null; this.context = {}; } isConnected() { return !!this.connection; } /** * Connects to websockets and returns the connection with event handlers .on('open', 'message', 'error') */ async connect(options) { const self = this; if (options.orgName) { this.sdk.context.orgName = options.orgName; } if (options.orgUid) { this.sdk.context.orgUid = options.orgUid; } // Validate org if (!this.sdk.context.orgName && !this.sdk.context.orgUid) { throw new Error('You must specify an "orgName" or "orgUid" to connect'); } // Sanitize filter options.filter = options.filter || {}; options.filter.stageName = options.filter.stageName ? options.filter.stageName.trim() : null; options.filter.appName = options.filter.appName ? options.filter.appName.trim() : null; options.filter.instanceName = options.filter.instanceName ? options.filter.instanceName.trim() : null; options.filter.events = options.filter.events || ['*']; // Add data to a query param let queryParams = '?'; queryParams += `accessKey=${this.sdk.accessKey}&`; if (this.sdk.context.orgName) { queryParams += `orgName=${this.sdk.context.orgName}&`; } if (this.sdk.context.orgUid) { queryParams += `orgUid=${this.sdk.context.orgUid}&`; } queryParams += `filter=${encodeURIComponent(JSON.stringify(options.filter))}&`; // Set domain const domain = this.sdk.getDomain('events-streaming') + queryParams; // Instantiate websockets library this.connection = new WS(domain); // Handle message event if (options.onEvent) { this.connection.onmessage = (message) => { message = message && message.data ? message.data : null; if (typeof message === 'string') { message = JSON.parse(message); } return options.onEvent(message); }; } this.connection.onclose = (closeEvent) => { clearInterval(this.pingInterval); this.pingInterval = null; // to learn more about closeEvent, ref: // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent // 1000 close code is the normal close event. It is expected. if (closeEvent.code !== 1000) { // the most encountered unexpected close codes are 1001 & 1006. // 1001 is for "Going Away", which is what AWS sends on idle/timeout // not sure what causes 1006, but it happens very often // in any case, always reconnect if it's an abnormal close event return self.connect(options); } // run onDisconnect hook if provided by the user // and move on with a clean disconnect if (options.onDisconnect) { return options.onDisconnect(closeEvent); } return null; }; return new Promise((resolve, reject) => { if (options.onError) { self.connection.onerror = options.onError; } else { // Error handler for errors that don't have an unexpected response self.connection.onerror = (error) => { return reject( new Error(`Unexpected Serverless Platform Streaming Error: ${error.message}`) ); }; } self.connection.onopen = () => { // Send a `ping` every second and expect the server to respond back with // a `pong` within 500 ms. if (this.connection.ping) { // NOT available in browser this.pingInterval = setInterval(() => { this.pingTimeout = setTimeout(() => { clearInterval(this.pingInterval); this.pingInterval = null; // Use `WebSocket#terminate()`, which immediately destroys the connection, // instead of `WebSocket#close()`, which waits for the close timer. this.connection.terminate(); this.connection = null; }, 500); if (this.connection) { this.connection.ping(); } }, 1000); this.connection.on('pong', () => { clearTimeout(this.pingTimeout); this.pingTimeout = null; }); } return resolve(); }; }); } /** * Sends data up to the websocket connection * @param {*} data */ send(data) { this.connection.send(data); } /** * Terminates the websockets connection */ disconnect(code) { // close with 1000 close code by default if (this.connection) { if (this.pingInterval) { clearTimeout(this.pingInterval); this.pingInterval = null; } if (this.pingTimeout) { clearTimeout(this.pingTimeout); this.pingTimeout = null; } this.connection.close(code || 1000); this.connection = null; } } } module.exports = Connection;