workspace-integrations
Version:
Webex Workspace Integrations NodeJS SDK
201 lines (200 loc) • 7.01 kB
JavaScript
"use strict";
/**
* All http calls for communicating with Webex (device) cloud
*/
Object.defineProperty(exports, "__esModule", { value: true });
// node-fetch Needs to be on low version to support CommonJS / require
// @ts-ignore
const node_fetch_1 = require("node-fetch");
const url_join_ts_1 = require("url-join-ts");
const logger_1 = require("./logger");
const util_1 = require("./util");
let dryHandler = null;
function header(accessToken) {
return {
Authorization: 'Bearer ' + accessToken,
'Content-Type': 'application/json',
};
}
// Modify fetch to throw error if http result is not 2xx, and return json always
async function fetch(url, options) {
if (dryHandler) {
return dryHandler(url, options);
}
const RetryWait = 2000;
let res;
try {
res = await (0, node_fetch_1.default)(url, options);
}
catch (e) {
// we wait and try once more before failing (recover from intermittent network fail)
logger_1.default.warn('Network failure, retrying once');
await (0, util_1.sleep)(RetryWait);
try {
res = await (0, node_fetch_1.default)(url, options);
}
catch (e) {
throw (e);
}
}
if (!res.ok) {
throw new Error(JSON.stringify(await res.json()));
}
logger_1.default.verbose(`[${options.method || 'GET'}: ${url}`);
return await res.json();
}
function get(accessToken, url) {
const headers = header(accessToken);
const options = {
headers,
};
return fetch(url, options);
}
class HttpImpl {
constructor(baseUrl, accessToken) {
this.baseUrl = '';
this.accessToken = '';
this.pollDeviceData = (url) => {
const headers = header(this.accessToken);
return fetch(url, { headers });
};
this.xCommand = (deviceId, command, args, multiline) => {
logger_1.default.info('Invoking xCommand ' + command);
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, 'xapi/command', command);
const body = {
deviceId,
};
if (args) {
body.arguments = args;
}
if (multiline) {
body.body = multiline;
}
const headers = header(this.accessToken);
const options = {
headers,
method: 'POST',
body: JSON.stringify(body),
};
return fetch(url, options);
};
this.xStatus = (deviceId, path) => {
logger_1.default.info('Getting xStatus ' + path);
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, '/xapi/status/', `?deviceId=${deviceId}&name=${path}`);
return get(this.accessToken, url);
};
this.xConfig = (deviceId, path) => {
logger_1.default.info('Getting xConfig ' + path);
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, '/deviceConfigurations/', `?deviceId=${deviceId}&key=${path}`);
return get(this.accessToken, url);
};
this.xConfigSet = (deviceId, configs) => {
logger_1.default.info('Setting xConfig ' + (configs.length === 1 ? configs[0].path : '(multiple)'));
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, '/deviceConfigurations/', `?deviceId=${deviceId}`);
const headers = {
Authorization: 'Bearer ' + this.accessToken,
'Content-Type': 'application/json-patch+json',
};
const body = configs.map((config) => ({
op: 'replace',
path: config.path + '/sources/configured/value',
value: config.value,
}));
const options = {
headers,
body: JSON.stringify(body),
method: 'PATCH',
};
return fetch(url, options);
};
this.getWorkspace = (accessToken, workspaceId) => {
logger_1.default.info('Fetching workspace');
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, '/workspaces/', workspaceId);
return get(accessToken, url);
};
this.deviceDetails = (accessToken, deviceId) => {
logger_1.default.info('Fetching device details');
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl, '/devices/', deviceId);
return get(accessToken, url);
};
this.baseUrl = baseUrl;
this.accessToken = accessToken;
}
static setDryMode(handler) {
dryHandler = handler;
}
webexApi(partialUrl, method = 'GET', body = null, contentType = 'application/json') {
const headers = header(this.accessToken);
if (contentType) {
headers['Content-Type'] = contentType;
}
const options = {
method,
headers,
};
if (body) {
options.body = JSON.stringify(body);
}
const url = partialUrl.startsWith('https://') ? partialUrl : (0, url_join_ts_1.urlJoin)(this.baseUrl, partialUrl);
return fetch(url, options);
}
setAccessToken(token) {
this.accessToken = token;
}
fullUrl(partialUrl) {
return (0, url_join_ts_1.urlJoin)(this.baseUrl, partialUrl);
}
ping(appUrl) {
return get(this.accessToken, appUrl);
}
static createAccessToken(oauth) {
const { clientId, clientSecret, oauthUrl, refreshToken } = oauth;
const headers = {
'Content-Type': 'application/json',
};
const body = {
grant_type: 'refresh_token',
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken,
};
const options = {
headers,
method: 'POST',
body: JSON.stringify(body),
};
return fetch(oauthUrl, options);
}
get(partialUrl) {
const url = (0, url_join_ts_1.urlJoin)(this.baseUrl + partialUrl);
return get(this.accessToken, url);
}
}
HttpImpl.initIntegration = (data) => {
const { accessToken, appUrl, webhook, notifications, actionsUrl } = data;
const headers = header(accessToken);
const body = {
provisioningState: 'completed',
};
if (notifications === 'webhook') {
logger_1.default.info('Subscribing to notifications with web hooks');
body.webhook = webhook;
body.actionsUrl = actionsUrl;
}
else if (notifications === 'longpolling') {
logger_1.default.info('Subscribing to notifications with long polling');
body.queue = {
state: 'enabled',
};
}
else {
logger_1.default.info('Not subscribing to notifications');
}
const options = {
headers,
method: 'PATCH',
body: JSON.stringify(body),
};
return fetch(appUrl, options);
};
exports.default = HttpImpl;