UNPKG

@onereach/webform

Version:

Content Builder includes several views for: - Content builder view itself; - Web Form view; - Slack block-kit builder;

214 lines (179 loc) 6.84 kB
import _isObject from 'lodash/isObject'; import _get from 'lodash/get'; import _set from 'lodash/set'; import _compact from 'lodash/compact'; import _join from 'lodash/join'; import _assign from 'lodash/assign'; import { nanoid } from 'nanoid'; import WSClient from '@onereach/ws-client'; import config from '@/config'; export default class EventManager { static DEFAULT_OUTBOUND_TIMEOUT = 30000; static connecting = false; static callbacks = {}; static partialResponses = {}; static previousCallback = null; static debug = true static wsClientDebug = true static wsClient = null; static initWSClient = ({ token, emBusURL, onConnect, onError, onClose, onOffline, onOnline }) => { const handleConnect = () => { if (this.previousCallback) { this.previousCallback = null; } this.connecting = false; onConnect(); }; config.debug && console.debug('[RWC::initWSClient] emBusURL:', emBusURL); config.debug && console.debug('[RWC::initWSClient] token:', token); this.wsClient = new WSClient({ debug: this.wsClientDebug, wssUrl: emBusURL, ping: 25000, token, onConnect: handleConnect, onError, onClose, onOffline, onOnline }); } static close = () => { this.wsClient.close(); } static initEventManagerWS = (onMessage) => { if (this.wsClient.name) { this.previousCallback = this.wsClient.name; } this.connecting = true; this.wsClient.onMessage = this._getOnMessageHandler(onMessage); this.wsClient.connect(); } static getCallback = () => { return this.wsClient ? this.wsClient.getCallback() : null; } static sendOutboundMessage = async (data, includeCallback = true, options) => { console.log('sendOutboundMessage'); const timeout = data.timeout || this.DEFAULT_OUTBOUND_TIMEOUT; const callbackId = nanoid(); if (!_isObject(data.message)) throw new Error('[WS::sendOutboundMessage] Message is required'); if (!this.wsClient) throw new Error('[WS::sendOutboundMessage] No active WS'); return new Promise((resolve, reject) => { console.log("sendOutboundMessage Promise data -> ", data); const message = data.message; message.callback = { id: callbackId }; message.type = _get(data, ['type'], 'sync'); message.$v = 3; this.callbacks[callbackId] = { resolve, reject, timeout: setTimeout(() => { console.warn( `[WS::sendOutboundMessage] Request Timeout: ${_get(message, 'event.name')}`, { message: data.message } ); reject(data.timeoutError || new Error('[WS::sendOutboundMessage] Timeout')); }, timeout) }; console.log('sendOutboundMessage this.callbacks -> ', this.callbacks); this._sendOutboundMessage(message, includeCallback, options); }).finally(() => { clearTimeout(_get(this.callbacks, [callbackId, 'timeout'])); delete this.callbacks[callbackId]; }); } static _getOnMessageHandler (onMessage) { return (message = {}) => { if (message.id && message.result && this.callbacks[message.id]) { this._handleResponse(message); onMessage({ ping: true }); } else if (message.id && Number(_get(message, ['part', 'n'])) > 0) { this._handlePartialResponse(message); onMessage({ ping: true }); } else { onMessage(message); } }; } static _handleResponse (message = {}) { this.debug && console.debug( `[WS::_processResponse] response: ${_get(this.callbacks[message.id].message, 'event.name')}`, { request: this.callbacks[message.id].message, response: message, time: Date.now() - this.callbacks[message.id].startTime } ); const callback = this.callbacks[message.id]; const result = message.result; if (result.resolve) { callback.resolve(result.resolve); } else { callback.reject(result.reject); } } static _handlePartialResponse (message = {}) { const { id, part, body } = message; this.debug && console.debug('[WS::_processPartialResponse] API Partial response', message); if (!this.partialResponses[id]) { this.partialResponses[id] = { parts: [], size: part.n }; } const timer = _get(this.partialResponses, [id, 'timer']); if (timer) { clearTimeout(timer); } _set(this.partialResponses, [id, 'timer'], setTimeout(() => { console.error(`[WS::_processPartialResponse] Failed to receive all parts of a multipart response ${id}`); this._cleanupPartialResponse(id); }, this.DEFAULT_OUTBOUND_TIMEOUT)); const parts = _get(this.partialResponses, [id, 'parts'], []); parts[part.i] = body; _set(this.partialResponses, [id, 'parts'], parts); if (_compact(parts).length >= _get(this.partialResponses, [id, 'size'])) { this._cleanupPartialResponse(id); const joinedParts = _join(parts, ''); const message = JSON.parse(joinedParts); this._handleResponse(message); } } static _cleanupPartialResponse (id) { const timer = _get(this.partialResponses, [id, 'timer']); clearTimeout(timer); delete this.partialResponses[id]; } static _sendOutboundMessage (message, includeCallback = true, options = {}) { const callbackId = _get(message, ['callback', 'id']); if (!this.callbacks[callbackId]) return; if (this.wsClient.connected && !this.connecting) { const wsCallback = this.wsClient.getCallback(); message.callback = _assign({}, wsCallback, { id: callbackId }); console.log('_sendOutboundMessage wsCallback -> ', wsCallback); console.log('_sendOutboundMessage message.callback -> ', message.callback); // TODO: need step refactoring to separate callback data from event params if (includeCallback) { const paramsName = message.event.params.name; message.event.params = _assign({}, message.event.params, message.callback); if (paramsName) { message.event.params.name = paramsName; } message.event.params.callback = message.callback; } if (options.token === 'UNAUTHORIZED') { message.event.name = `ws/${message.event.name}`; message.event.target = options.accountId; message.event.source = options.accountId; } if (this.debug) { this.callbacks[callbackId].message = message; this.callbacks[callbackId].startTime = Date.now(); console.debug(`[WS::_sendOutboundMessage] request: ${_get(message, 'event.name')}`, message); } this.wsClient.socket.send(JSON.stringify(message)); } else { setTimeout(() => this._sendOutboundMessage(message, includeCallback, options), 250); } } }