UNPKG

@contentstack/management

Version:

The Content Management API is used to manage the content of your Contentstack account

377 lines (367 loc) 15.3 kB
"use strict"; var _interopRequireDefault3 = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireDefault2 = _interopRequireDefault3(require("@babel/runtime/helpers/interopRequireDefault")); Object.defineProperty(exports, "__esModule", { value: true }); var _defineProperty2 = require("@babel/runtime/helpers/defineProperty"); var _defineProperty3 = (0, _interopRequireDefault2["default"])(_defineProperty2); var _asyncToGenerator2 = require("@babel/runtime/helpers/asyncToGenerator"); var _asyncToGenerator3 = (0, _interopRequireDefault2["default"])(_asyncToGenerator2); exports.ConcurrencyQueue = ConcurrencyQueue; var _regenerator = require("@babel/runtime/regenerator"); var _regenerator2 = (0, _interopRequireDefault2["default"])(_regenerator); var _axios = require("axios"); var _axios2 = (0, _interopRequireDefault2["default"])(_axios); var _oauthHandler = require("./oauthHandler"); var _oauthHandler2 = (0, _interopRequireDefault2["default"])(_oauthHandler); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty3["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var defaultConfig = { maxRequests: 5, retryLimit: 5, retryDelay: 300 }; function ConcurrencyQueue(_ref) { var _this = this; var axios = _ref.axios, config = _ref.config; if (!axios) { throw Error('Axios instance is not present'); } if (config) { if (config.maxRequests && config.maxRequests <= 0) { throw Error('Concurrency Manager Error: minimum concurrent requests is 1'); } else if (config.retryLimit && config.retryLimit <= 0) { throw Error('Retry Policy Error: minimum retry limit is 1'); } else if (config.retryDelay && config.retryDelay < 300) { throw Error('Retry Policy Error: minimum retry delay for requests is 300'); } } this.config = Object.assign({}, defaultConfig, config); this.queue = []; this.running = []; this.paused = false; // Initial shift will check running request, // and adds request to running queue if max requests are not running this.initialShift = function () { if (_this.running.length < _this.config.maxRequests && !_this.paused) { shift(); } }; // INTERNAL: Shift the queued item to running queue var shift = function shift() { if (_this.queue.length && !_this.paused) { var queueItem = _this.queue.shift(); queueItem.resolve(queueItem.request); _this.running.push(queueItem); } }; // Append the request at start of queue this.unshift = function (requestPromise) { _this.queue.unshift(requestPromise); }; this.push = function (requestPromise) { _this.queue.push(requestPromise); _this.initialShift(); }; this.clear = function () { var requests = _this.queue.splice(0, _this.queue.length); requests.forEach(function (element) { element.request.source.cancel(); }); }; // Detach the interceptors this.detach = function () { axios.interceptors.request.eject(_this.interceptors.request); axios.interceptors.response.eject(_this.interceptors.response); _this.interceptors = { request: null, response: null }; }; // Request interceptor to queue the request var requestHandler = function requestHandler(request) { var _axios$oauth; if (typeof request.data === 'function') { request.formdata = request.data; request.data = transformFormData(request); } if (axios !== null && axios !== void 0 && (_axios$oauth = axios.oauth) !== null && _axios$oauth !== void 0 && _axios$oauth.accessToken) { var isTokenExpired = axios.oauth.tokenExpiryTime && Date.now() > axios.oauth.tokenExpiryTime; if (isTokenExpired) { return refreshAccessToken()["catch"](function (error) { throw new Error('Failed to refresh access token: ' + error.message); }); } } request.retryCount = (request === null || request === void 0 ? void 0 : request.retryCount) || 0; setAuthorizationHeaders(request); if (request.cancelToken === undefined) { var source = _axios2["default"].CancelToken.source(); request.cancelToken = source.token; request.source = source; } if (_this.paused && request.retryCount > 0) { return new Promise(function (resolve, reject) { _this.unshift({ request: request, resolve: resolve, reject: reject }); }); } else if (request.retryCount > 0) { return request; } return new Promise(function (resolve, reject) { request.onComplete = function () { _this.running.pop({ request: request, resolve: resolve, reject: reject }); }; _this.push({ request: request, resolve: resolve, reject: reject }); }); }; var setAuthorizationHeaders = function setAuthorizationHeaders(request) { var _axios$oauth2; if (request.headers.authorization && request.headers.authorization !== undefined) { if (_this.config.authorization && _this.config.authorization !== undefined) { request.headers.authorization = _this.config.authorization; request.authorization = _this.config.authorization; } delete request.headers.authtoken; } else if (request.headers.authtoken && request.headers.authtoken !== undefined && _this.config.authtoken && _this.config.authtoken !== undefined) { request.headers.authtoken = _this.config.authtoken; request.authtoken = _this.config.authtoken; } else if (axios !== null && axios !== void 0 && (_axios$oauth2 = axios.oauth) !== null && _axios$oauth2 !== void 0 && _axios$oauth2.accessToken) { // If OAuth access token is available in axios instance request.headers.authorization = "Bearer ".concat(axios.oauth.accessToken); request.authorization = "Bearer ".concat(axios.oauth.accessToken); delete request.headers.authtoken; } }; // Refresh Access Token var refreshAccessToken = /*#__PURE__*/function () { var _ref2 = (0, _asyncToGenerator3["default"])(/*#__PURE__*/_regenerator2["default"].mark(function _callee() { return _regenerator2["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.prev = 0; _context.next = 3; return new _oauthHandler2["default"](axios).refreshAccessToken(); case 3: _this.paused = false; // Resume the request queue once the token is refreshed // Retry the requests that were pending due to token expiration _this.running.forEach(function (_ref3) { var request = _ref3.request, resolve = _ref3.resolve, reject = _ref3.reject; // Retry the request axios(request).then(resolve)["catch"](reject); }); _this.running = []; // Clear the running queue after retrying requests _context.next = 13; break; case 8: _context.prev = 8; _context.t0 = _context["catch"](0); _this.paused = false; // stop queueing requests on failure _this.running.forEach(function (_ref4) { var reject = _ref4.reject; return reject(_context.t0); }); // Reject all queued requests _this.running = []; // Clear the running queue case 13: case "end": return _context.stop(); } }, _callee, null, [[0, 8]]); })); return function refreshAccessToken() { return _ref2.apply(this, arguments); }; }(); var _delay = function delay(time) { var isRefreshToken = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!_this.paused) { _this.paused = true; // Check for current running request. // Wait for running queue to complete. // Wait and prosed the Queued request. if (_this.running.length > 0) { setTimeout(function () { _delay(time, isRefreshToken); }, time); } return new Promise(function (resolve) { return setTimeout(function () { _this.paused = false; if (isRefreshToken) { return refreshToken(); } else { for (var i = 0; i < _this.config.maxRequests; i++) { _this.initialShift(); } } }, time); }); } }; var refreshToken = function refreshToken() { return config.refreshToken().then(function (token) { if (token.authorization) { axios.defaults.headers.authorization = token.authorization; axios.defaults.authorization = token.authorization; axios.httpClientParams.authorization = token.authorization; axios.httpClientParams.headers.authorization = token.authorization; _this.config.authorization = token.authorization; } else if (token.authtoken) { axios.defaults.headers.authtoken = token.authtoken; axios.defaults.authtoken = token.authtoken; axios.httpClientParams.authtoken = token.authtoken; axios.httpClientParams.headers.authtoken = token.authtoken; _this.config.authtoken = token.authtoken; } })["catch"](function (error) { _this.queue.forEach(function (queueItem) { queueItem.reject({ errorCode: '401', errorMessage: error instanceof Error ? error.message : error, code: 'Unauthorized', message: 'Unable to refresh token', name: 'Token Error', config: queueItem.request, stack: error instanceof Error ? error.stack : null }); }); _this.queue = []; _this.running = []; })["finally"](function () { _this.queue.forEach(function (queueItem) { if (_this.config.authorization) { queueItem.request.headers.authorization = _this.config.authorization; queueItem.request.authorization = _this.config.authorization; } if (_this.config.authtoken) { queueItem.request.headers.authtoken = _this.config.authtoken; queueItem.request.authtoken = _this.config.authtoken; } }); for (var i = 0; i < _this.config.maxRequests; i++) { _this.initialShift(); } }); }; // Response interceptor used for var responseHandler = function responseHandler(response) { response.config.onComplete(); shift(); return response; }; var responseErrorHandler = function responseErrorHandler(error) { var networkError = error.config.retryCount; var retryErrorType = null; if (!_this.config.retryOnError || networkError > _this.config.retryLimit) { return Promise.reject(responseHandler(error)); } // Check rate limit remaining header before retrying // Error handling var wait = _this.config.retryDelay; var response = error.response; if (!response) { if (error.code === 'ECONNABORTED') { error.response = _objectSpread(_objectSpread({}, error.response), {}, { status: 408, statusText: "timeout of ".concat(_this.config.timeout, "ms exceeded") }); response = error.response; } else { return Promise.reject(responseHandler(error)); } } else if (response.status === 401 && _this.config.refreshToken) { retryErrorType = "Error with status: ".concat(response.status); networkError++; if (networkError > _this.config.retryLimit) { return Promise.reject(responseHandler(error)); } _this.running.shift(); // Cool down the running requests _delay(wait, response.status === 401); error.config.retryCount = networkError; // deepcode ignore Ssrf: URL is dynamic return axios(updateRequestConfig(error, retryErrorType, wait)); } if (_this.config.retryCondition && _this.config.retryCondition(error)) { retryErrorType = error.response ? "Error with status: ".concat(response.status) : "Error Code:".concat(error.code); networkError++; return _this.retry(error, retryErrorType, networkError, wait); } return Promise.reject(responseHandler(error)); }; this.retry = function (error, retryErrorType, retryCount, waittime) { var delaytime = waittime; if (retryCount > _this.config.retryLimit) { return Promise.reject(responseHandler(error)); } if (_this.config.retryDelayOptions) { if (_this.config.retryDelayOptions.customBackoff) { delaytime = _this.config.retryDelayOptions.customBackoff(retryCount, error); if (delaytime && delaytime <= 0) { return Promise.reject(responseHandler(error)); } } else if (_this.config.retryDelayOptions.base) { delaytime = _this.config.retryDelayOptions.base * retryCount; } } else { delaytime = _this.config.retryDelay; } error.config.retryCount = retryCount; return new Promise(function (resolve) { return setTimeout(function () { // deepcode ignore Ssrf: URL is dynamic return resolve(axios(updateRequestConfig(error, retryErrorType, delaytime))); }, delaytime); }); }; this.interceptors = { request: null, response: null }; var updateRequestConfig = function updateRequestConfig(error, retryErrorType, wait) { var requestConfig = error.config; _this.config.logHandler('warning', "".concat(retryErrorType, " error occurred. Waiting for ").concat(wait, " ms before retrying...")); if (axios !== undefined && axios.defaults !== undefined) { if (axios.defaults.agent === requestConfig.agent) { delete requestConfig.agent; } if (axios.defaults.httpAgent === requestConfig.httpAgent) { delete requestConfig.httpAgent; } if (axios.defaults.httpsAgent === requestConfig.httpsAgent) { delete requestConfig.httpsAgent; } } requestConfig.data = transformFormData(requestConfig); requestConfig.transformRequest = [function (data) { return data; }]; return requestConfig; }; var transformFormData = function transformFormData(request) { if (request.formdata) { var formdata = request.formdata(); request.headers = _objectSpread(_objectSpread({}, request.headers), formdata.getHeaders()); return formdata; } return request.data; }; // Adds interseptors in axios to queue request this.interceptors.request = axios.interceptors.request.use(requestHandler); this.interceptors.response = axios.interceptors.response.use(responseHandler, responseErrorHandler); }