@webex/webex-core
Version:
Plugin handling for Cisco Webex
219 lines (205 loc) • 8.45 kB
JavaScript
;
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.persist = persist;
exports.waitForValue = waitForValue;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/typeof"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
var _apply = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/apply"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));
var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));
var _lodash = require("lodash");
var _common = require("@webex/common");
var _errors = require("./errors");
/*!
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
*/
/* eslint no-invalid-this: [0] */
/**
* Stores the result of fn before returning it
* @param {string} key
* @private
* @returns {Promise} resolves with the result of fn
*/
function persist() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (args.length === 3) {
return persist('@').apply(void 0, args);
}
var key = args[0],
decider = args[1];
return function persistDecorator(target, prop, descriptor) {
if (prop !== 'initialize') {
// Once we have class-based alternative to AmpersandState, it should be
// detected here.
throw new TypeError('@persist can only currently be applied to AmpersandState objects or their derivatives and must be applied to the initialize method');
}
descriptor.value = (0, _lodash.wrap)(descriptor.value, function persistExecutor(fn) {
var _this = this;
for (var _len2 = arguments.length, initializeArgs = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
initializeArgs[_key2 - 1] = arguments[_key2];
}
var ret = (0, _apply.default)(fn, this, initializeArgs);
var changeEvent = key === '@' ? 'change' : "change:".concat(key);
// Some scenarios will lead to lots of change events on a single tick; we
// really only care about writing once things have stopped changing. with
// a debounce of zero, we're effectively coalescing all the changes
// triggered by a single call to set() and commiting them on the next tick
// eslint-disable-next-line no-invalid-this
this.on(changeEvent, (0, _lodash.debounce)(function () {
var shouldPersist = !decider || _apply.default.apply(Reflect, [decider, _this].concat(initializeArgs));
if (!shouldPersist) {
return _promise.default.resolve();
}
if (key === '@') {
// eslint-disable-next-line no-invalid-this
return _this.boundedStorage.put(key, _this);
}
// eslint-disable-next-line no-invalid-this
return _this.boundedStorage.put(key, _this[key]);
}, 0));
return ret;
});
prepareInitialize(target, prop);
};
}
var M = _map.default;
var S = _set.default;
var BlockingKeyMap = (0, _common.make)(M, M, S);
var blockingKeys = new BlockingKeyMap();
/**
* Prevents fn from executing until key has been (attempted to be) loaded
* @param {string} key
* @param {Function} fn
* @private
* @returns {Promise} result of fn
*/
function waitForValue(key) {
if (!key) {
throw new Error('`key` is required');
}
return function waitForValueDecorator(target, prop, descriptor) {
blockingKeys.add(target, prop, key);
descriptor.value = (0, _lodash.wrap)(descriptor.value, function waitForValueExecutor(fn) {
var _this2 = this;
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
var keys = blockingKeys.get(target, prop);
return _promise.default.all((0, _toConsumableArray2.default)(keys).map(function (k) {
return _this2.boundedStorage.waitFor(k);
})).then(function () {
return (0, _apply.default)(fn, _this2, args);
});
});
// This *should* make decorators compatible with AmpersandState class
// definitions
if ((0, _typeof2.default)(target) === 'object' && !target.prototype) {
target[prop] = descriptor.value;
}
prepareInitialize(target, prop);
return descriptor;
};
}
var inited = new _set.default();
/**
* finds a means of identitying the `target` param passed to
* `prepareInitialize()`. When possible, avoids duplicate `init()` calls if
* namespaces collide
*
* @param {Object|Constructor} target
* @private
* @returns {String|Constructor}
*/
function identifyTarget(target) {
if (target.namespace) {
return target.namespace;
}
return target;
}
var stack = new _set.default();
/**
* @param {Function} target
* @param {string} prop
* @private
* @returns {undefined}
*/
function prepareInitialize(target, prop) {
var id = identifyTarget(target);
if (!inited.has(id)) {
inited.add(id);
if (target.initialize) {
target.initialize = (0, _lodash.wrap)(target.initialize, function applyInit(fn) {
for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
var ret = (0, _apply.default)(fn, this, args);
(0, _apply.default)(init, this, args);
return ret;
});
return;
}
target.initialize = init;
}
/**
* @private
* @returns {undefined}
*/
function init() {
var self = this;
var namespace = this.getNamespace();
this.webex.initialize = (0, _lodash.wrap)(this.webex.initialize || _lodash.identity, function applyInit(fn) {
var _this3 = this;
// Call webex's initalize method first
// Reminder: in order for MockWebex to accept initial storage data, the
// wrapped initialize() must be invoked before attempting to load data.
// Reminder: context here is `webex`, not `self`.
stack.add(namespace);
for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
args[_key5 - 1] = arguments[_key5];
}
(0, _apply.default)(fn, this, args);
// Then prepare a function for setting values retrieved from storage
var set = (0, _lodash.curry)(function (key, value) {
_this3.logger.debug("storage:(".concat(namespace, "): got `").concat(key, "` for first time"));
if (key === '@') {
self.parent.set((0, _defineProperty2.default)({}, namespace.toLowerCase(), value));
} else if ((0, _lodash.result)(self[key], 'isState')) {
self[key].set(value);
} else {
self.set(key, value);
}
_this3.logger.debug("storage:(".concat(namespace, "): set `").concat(key, "` for first time"));
});
// And prepare an error handler for when those keys can't be found
var handle = (0, _lodash.curry)(function (key, reason) {
if (reason instanceof _errors.NotFoundError || process.env.NODE_ENV !== 'production' && reason.toString().includes('MockNotFoundError')) {
_this3.logger.debug("storage(".concat(namespace, "): no data for `").concat(key, "`, continuing"));
return _promise.default.resolve();
}
_this3.logger.error("storage(".concat(namespace, "): failed to init `").concat(key, "`"), reason);
return _promise.default.reject(reason);
});
// Iterate over the list of keys marked as blocking via `@waitForValue`
var keys = blockingKeys.get(target, prop);
var promises = [];
keys.forEach(function (key) {
promises.push(_this3.boundedStorage.get(namespace, key).then(set(key)).catch(handle(key)));
});
_promise.default.all(promises).then(function () {
stack.delete(namespace);
if (stack.size === 0) {
_this3.loaded = true;
}
});
});
}
}
//# sourceMappingURL=decorators.js.map