UNPKG

@webex/webex-core

Version:

Plugin handling for Cisco Webex

293 lines (288 loc) • 9.44 kB
"use strict"; var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property"); var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault"); _Object$defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var _lodash = require("lodash"); var _common = require("@webex/common"); var _webexPlugin = _interopRequireDefault(require("./webex-plugin")); var _webexHttpError = _interopRequireDefault(require("./webex-http-error")); /*! * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file. */ /** * Base class for coalescing requests to batched APIs * @class Batcher */ var Batcher = _webexPlugin.default.extend({ session: { deferreds: { type: 'object', default: function _default() { return new _map.default(); } }, queue: { type: 'array', default: function _default() { return []; } } }, derived: { bounce: { fn: function fn() { var _this = this; return (0, _common.cappedDebounce)(function () { return _this.executeQueue.apply(_this, arguments); }, this.config.batcherWait, { maxCalls: this.config.batcherMaxCalls, maxWait: this.config.batcherMaxWait }); } } }, /** * Requests an item from a batched API * @param {Object} item * @returns {Promise<mixed>} */ request: function request(item) { var _this2 = this; // So far, I can't find a way to avoid three layers of nesting here. /* eslint max-nested-callbacks: [0] */ var defer = new _common.Defer(); this.fingerprintRequest(item).then(function (idx) { if (_this2.deferreds.has(idx)) { defer.resolve(_this2.deferreds.get(idx).promise); return; } _this2.deferreds.set(idx, defer); _this2.prepareItem(item).then(function (req) { defer.promise = defer.promise.then((0, _common.tap)(function () { return _this2.deferreds.delete(idx); })).catch(function (reason) { _this2.deferreds.delete(idx); return _promise.default.reject(reason); }); _this2.enqueue(req).then(function () { return _this2.bounce(); }).catch(function (reason) { return defer.reject(reason); }); }).catch(function (reason) { return defer.reject(reason); }); }).catch(function (reason) { return defer.reject(reason); }); return defer.promise; }, /** * Adds an item to the queue. * Intended to be overridden * @param {mixed} req * @returns {Promise<undefined>} */ enqueue: function enqueue(req) { this.queue.push(req); return _promise.default.resolve(); }, /** * Transform the item before adding it to the queue * Intended to be overridden * @param {mixed} item * @returns {Promise<mixed>} */ prepareItem: function prepareItem(item) { return _promise.default.resolve(item); }, /** * Detaches the current queue, does any appropriate transforms, and submits it * to the API. * @returns {Promise<undefined>} */ executeQueue: function executeQueue() { var _this3 = this; var queue = this.queue.splice(0, this.config.batcherMaxCalls); return new _promise.default(function (resolve) { resolve(_this3.prepareRequest(queue).then(function (payload) { return _this3.submitHttpRequest(payload).then(function (res) { return _this3.handleHttpSuccess(res); }); }).catch(function (reason) { if (reason instanceof _webexHttpError.default) { return _this3.handleHttpError(reason); } return _promise.default.all(queue.map(function (item) { return _this3.getDeferredForRequest(item).then(function (defer) { defer.reject(reason); }); })); })); }).catch(function (reason) { _this3.logger.error(process.env.NODE_ENV === 'production' ? reason : reason.stack); return _promise.default.reject(reason); }); }, /** * Performs any final transforms on the queue before submitting it to the API * Intended to be overridden * @param {Object|Array} queue * @returns {Promise<Object>} */ prepareRequest: function prepareRequest(queue) { return _promise.default.resolve(queue); }, /** * Submits the prepared request body to the API. * This method *must* be overridden * @param {Object} payload * @returns {Promise<HttpResponseObject>} */ // eslint-disable-next-line no-unused-vars submitHttpRequest: function submitHttpRequest(payload) { throw new Error('request() must be implemented'); }, /** * Actions taken when the http request returns a success * Intended to be overridden * @param {Promise<HttpResponseObject>} res * @returns {Promise<undefined>} */ handleHttpSuccess: function handleHttpSuccess(res) { var _this4 = this; return _promise.default.all((res.body && res.body.items || res.body).map(function (item) { return _this4.acceptItem(item); })); }, /** * Actions taken when the http request returns a failure. Typically, this * means failing the entire queue, but could be overridden in some * implementations to e.g. reenqueue. * Intended to be overridden * @param {WebexHttpError} reason * @returns {Promise<undefined>} */ handleHttpError: function handleHttpError(reason) { var _this5 = this; if (reason instanceof _webexHttpError.default) { if ((0, _lodash.has)(reason, 'options.body.map')) { return _promise.default.all(reason.options.body.map(function (item) { return _this5.getDeferredForRequest(item).then(function (defer) { defer.reject(reason); }); })); } } this.logger.error('http error handler called without a WebexHttpError object', reason); return _promise.default.reject(reason); }, /** * Determines if the item succeeded or failed and delegates accordingly * @param {Object} item * @returns {Promise<undefined>} */ acceptItem: function acceptItem(item) { var _this6 = this; return this.didItemFail(item).then(function (didFail) { if (didFail) { return _this6.handleItemFailure(item); } return _this6.handleItemSuccess(item); }); }, /** * Indicates if the specified response item implies a success or a failure * Intended to be overridden * @param {Object} item * @returns {Promise<Boolean>} */ // eslint-disable-next-line no-unused-vars didItemFail: function didItemFail(item) { return _promise.default.resolve(false); }, /** * Finds the Defer for the specified item and rejects its promise * Intended to be overridden * @param {Object} item * @returns {Promise<undefined>} */ handleItemFailure: function handleItemFailure(item) { return this.getDeferredForResponse(item).then(function (defer) { defer.reject(item); }); }, /** * Finds the Defer for the specified item and resolves its promise * Intended to be overridden * @param {Object} item * @returns {Promise<undefined>} */ handleItemSuccess: function handleItemSuccess(item) { return this.getDeferredForResponse(item).then(function (defer) { defer.resolve(item); }); }, /** * Returns the Deferred for the specified request item * @param {Object} item * @returns {Promise<Defer>} */ getDeferredForRequest: function getDeferredForRequest(item) { var _this7 = this; return this.fingerprintRequest(item).then(function (idx) { var defer = _this7.deferreds.get(idx); /* istanbul ignore if */ if (!defer) { throw new Error('Could not find pending request for received response'); } return defer; }); }, /** * Returns the Deferred for the specified response item * @param {Object} item * @returns {Promise<Defer>} */ getDeferredForResponse: function getDeferredForResponse(item) { var _this8 = this; return this.fingerprintResponse(item).then(function (idx) { var defer = _this8.deferreds.get(idx); /* istanbul ignore if */ if (!defer) { throw new Error('Could not find pending request for received response'); } return defer; }); }, /** * Generates a unique identifier for the item in a request payload * Intended to be overridden * Note that overrides must return a primitive. * @param {Object} item * @returns {Promise<primitive>} */ // eslint-disable-next-line no-unused-vars fingerprintRequest: function fingerprintRequest(item) { throw new Error('fingerprintRequest() must be implemented'); }, /** * Generates a unique identifier for the item in a response payload * Intended to be overridden * Note that overrides must return a primitive. * @param {Object} item * @returns {Promise<primitive>} */ // eslint-disable-next-line no-unused-vars fingerprintResponse: function fingerprintResponse(item) { throw new Error('fingerprintResponse() must be implemented'); }, version: "3.9.0" }); var _default2 = exports.default = Batcher; //# sourceMappingURL=batcher.js.map