UNPKG

workspace-integrations

Version:
176 lines (175 loc) 6.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("./util"); const http_1 = require("./http"); const devices_1 = require("./apis/devices"); const workspaces_1 = require("./apis/workspaces"); const xapi_1 = require("./apis/xapi"); const logger_1 = require("./logger"); const jwt_1 = require("./jwt"); function validateConfig(config) { if (!config.clientId || !config.clientSecret || !config.activationCode) { throw new TypeError('Missing clientId, clientSecret or activationCode in config'); } const jwt = config.activationCode; if (!jwt.oauthUrl || !jwt.webexapisBaseUrl) { throw new TypeError('activationCode does not contain the expected data. Please decode it and provide the required attributes as a JSON object.'); } const known = ['clientId', 'clientSecret', 'activationCode', 'notifications', 'logLevel', 'webhook', 'actionsUrl']; Object.keys(config).forEach((key) => { if (!known.includes(key)) { logger_1.default.error('Unknown config: ' + key); } }); } class IntegrationImpl { constructor(appConfig) { this.actionHandler = null; this.errorHandler = null; this.appConfig = appConfig; const { activationCode, accessToken, pollUrl } = appConfig; this.http = new http_1.default(activationCode.webexapisBaseUrl, accessToken); this.devices = new devices_1.default(this.http); this.workspaces = new workspaces_1.default(this.http); this.xapi = new xapi_1.default(this.http); this.appUrl = activationCode.appUrl; this.tokenExpiryTime = appConfig.tokenExpiryTime; const timeToExpiry = (new Date(this.tokenExpiryTime).getTime() - Date.now()) / 1000; const timeToRefresh = timeToExpiry - 60 * 15; logger_1.default.verbose(`Fetching new token on ${new Date(Date.now() + timeToRefresh * 1000)}`); setTimeout(() => this.refreshToken(), timeToRefresh * 1000); if (pollUrl) { this.pollData(pollUrl); } } onError(handler) { this.errorHandler = handler; } onAction(handler) { this.actionHandler = handler; } async getAppInfo() { return this.ping(); } webexApi(partialUrl, method, body, contentType) { return this.http.webexApi(partialUrl, method, body, contentType); } ping() { return this.http.ping(this.appUrl); } decodeJwt(jwt) { return (0, jwt_1.decodeAndVerify)(jwt); } serialize() { return this.appConfig; } static async deserialize(obj) { const { tokenExpiryTime, activationCode } = obj; const expired = new Date(tokenExpiryTime).getTime() <= Date.now(); if (expired) { const { access_token, expires_in } = await http_1.default.createAccessToken({ clientId: obj.appId, clientSecret: obj.appSecret, oauthUrl: activationCode.oauthUrl, refreshToken: activationCode.refreshToken, }); obj.accessToken = access_token; obj.tokenExpiryTime = new Date(Date.now() + (expires_in * 1000)).toISOString(); } return new IntegrationImpl(obj); } async pollData(pollUrl) { if (!pollUrl) return; while (true) { let data; try { data = await this.http.pollDeviceData(pollUrl); } catch (e) { console.log('Error polling', e); const WaitAfterError = 5000; await (0, util_1.sleep)(WaitAfterError); } if (data) { this.processNotifications(data === null || data === void 0 ? void 0 : data.messages); } } } processNotifications(notifications) { logger_1.default.verbose(`Got ${notifications.length} notifications`); notifications.forEach((n) => { if (n.type === 'action') { this.decodeAndNotifyAction(n); } else { this.xapi.processNotification(n); } }); } async decodeAndNotifyAction(action) { if (!this.actionHandler) return; const { jwt } = action; const res = await (0, jwt_1.decodeAndVerify)(jwt); if (res) { this.actionHandler(res); } else { logger_1.default.error('Not able to verify action message!'); } } static async connect(options) { var _a; validateConfig(options); const { clientId, clientSecret, notifications, webhook, actionsUrl, activationCode } = options; const { oauthUrl, refreshToken, appUrl } = activationCode; const tokenData = await http_1.default.createAccessToken({ clientId, clientSecret, oauthUrl, refreshToken }); logger_1.default.info('Got initial access token'); const appInfo = await http_1.default.initIntegration({ accessToken: tokenData.access_token, appUrl, notifications, webhook, actionsUrl, }); logger_1.default.info('Successfully initiated integration'); const { access_token, expires_in } = tokenData; const tokenExpiryTime = new Date(Date.now() + (expires_in * 1000)).toISOString(); const config = { appId: clientId, appSecret: clientSecret, accessToken: access_token, activationCode, tokenExpiryTime, }; if (notifications === 'longpolling') { config.pollUrl = (_a = appInfo.queue) === null || _a === void 0 ? void 0 : _a.pollUrl; } const integration = new IntegrationImpl(config); return integration; } async refreshToken() { const { oauthUrl, refreshToken } = this.appConfig.activationCode; try { const { access_token, expires_in } = await http_1.default.createAccessToken({ clientId: this.appConfig.appId, clientSecret: this.appConfig.appSecret, oauthUrl, refreshToken, }); this.http.setAccessToken(access_token); const nextTime = expires_in - 60 * 15; this.tokenExpiryTime = new Date(Date.now() + (expires_in * 1000)).toISOString(); logger_1.default.info('Fetched new access token'); logger_1.default.verbose(`Fetching new token on ${new Date(Date.now() + nextTime * 1000)}`); setTimeout(() => this.refreshToken(), nextTime * 1000); } catch (e) { logger_1.default.error('Unable to refresh token'); if (this.errorHandler) { this.errorHandler('Not able to refresh token. ' + (e instanceof Error && e.message)); } } } } exports.default = IntegrationImpl;