/** * Thin axios wrapper for the Webex Service App. * * - Pulls the current access token from WebexServiceAppAuth on each call. * - Retries transient failures via utils/retry.withRetry. * - On a 401 response (token revoked between cache and call), forces a single * refresh and retries once. Repeated 401s after that surface as errors so * the caller can degrade gracefully. */ const axios = require('axios'); const config = require('../config'); const WebexServiceAppAuth = require('../integrations/webex/WebexServiceAppAuth'); const { withRetry } = require('../utils/retry'); const logger = require('../utils/logger'); const BASE_URL = 'https://webexapis.com/v1'; const RETRY_OPTS = { retries: 2, initialDelayMs: 500 }; let _auth = null; function auth() { if (!_auth) { _auth = WebexServiceAppAuth.getInstance({ clientId: config.webexServiceApp.clientId, clientSecret: config.webexServiceApp.clientSecret, tokensFilePath: config.webexServiceApp.tokensPath, }); } return _auth; } function resetAuthForTests() { _auth = null; } /** * Issue a Webex Service App request. Returns response.data (or an empty * object) on success; throws axios errors on hard failures. * * @param {('GET'|'POST'|'PUT'|'DELETE'|'PATCH')} method * @param {string} pathSuffix - Webex API path relative to /v1 (e.g. "people"). * @param {object|null} body - JSON body for non-GET methods. * @param {object|null} params - querystring params. */ async function request(method, pathSuffix, body = null, params = null) { const url = `${BASE_URL}/${String(pathSuffix).replace(/^\/+/, '')}`; const a = auth(); const doRequest = async (forceRefresh = false) => { const token = forceRefresh ? await a.forceRefresh() : await a.getAccessToken(); return axios({ method, url, data: body || undefined, params: params || undefined, headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, timeout: 15000, }); }; try { const res = await withRetry(() => doRequest(false), RETRY_OPTS); return res.data ?? {}; } catch (err) { if (err.response?.status === 401) { logger.warn('Webex returned 401 — refreshing and retrying once', { url }); try { const retry = await doRequest(true); return retry.data ?? {}; } catch (retryErr) { logger.error('Webex request failed after forced refresh', { url, status: retryErr.response?.status, error: retryErr.message, }); throw retryErr; } } throw err; } } module.exports = { request, resetAuthForTests, BASE_URL };