@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
213 lines (212 loc) • 10.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SplitsCachePluggable = void 0;
var tslib_1 = require("tslib");
var lang_1 = require("../../utils/lang");
var constants_1 = require("./constants");
var AbstractSplitsCacheAsync_1 = require("../AbstractSplitsCacheAsync");
var sets_1 = require("../../utils/lang/sets");
/**
* ISplitsCacheAsync implementation for pluggable storages.
*/
var SplitsCachePluggable = /** @class */ (function (_super) {
(0, tslib_1.__extends)(SplitsCachePluggable, _super);
/**
* Create a SplitsCache that uses a storage wrapper.
* @param log - Logger instance.
* @param keys - Key builder.
* @param wrapper - Adapted wrapper storage.
*/
function SplitsCachePluggable(log, keys, wrapper, splitFiltersValidation) {
var _this = _super.call(this) || this;
_this.log = log;
_this.keys = keys;
_this.wrapper = wrapper;
_this.flagSetsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
return _this;
}
SplitsCachePluggable.prototype._decrementCounts = function (split) {
var _this = this;
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
return this.wrapper.decr(ttKey).then(function (count) {
if (count === 0)
return _this.wrapper.del(ttKey);
});
};
SplitsCachePluggable.prototype._incrementCounts = function (split) {
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
return this.wrapper.incr(ttKey);
};
SplitsCachePluggable.prototype._updateFlagSets = function (featureFlagName, flagSetsOfRemovedFlag, flagSetsOfAddedFlag) {
var _this = this;
var removeFromFlagSets = (0, sets_1.returnDifference)(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
var addToFlagSets = (0, sets_1.returnDifference)(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
if (this.flagSetsFilter.length > 0) {
addToFlagSets = addToFlagSets.filter(function (flagSet) {
return _this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === flagSet; });
});
}
var items = [featureFlagName];
return Promise.all((0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], removeFromFlagSets.map(function (flagSetName) { return _this.wrapper.removeItems(_this.keys.buildFlagSetKey(flagSetName), items); }), true), addToFlagSets.map(function (flagSetName) { return _this.wrapper.addItems(_this.keys.buildFlagSetKey(flagSetName), items); }), true));
};
/**
* Add a given split.
* The returned promise is resolved when the operation success
* or rejected if it fails (e.g., wrapper operation fails)
*/
SplitsCachePluggable.prototype.addSplit = function (split) {
var _this = this;
var name = split.name;
var splitKey = this.keys.buildSplitKey(name);
return this.wrapper.get(splitKey).then(function (splitFromStorage) {
// handling parsing error
var parsedPreviousSplit, stringifiedNewSplit;
try {
parsedPreviousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : undefined;
stringifiedNewSplit = JSON.stringify(split);
}
catch (e) {
throw new Error('Error parsing feature flag definition: ' + e);
}
return _this.wrapper.set(splitKey, stringifiedNewSplit).then(function () {
// avoid unnecessary increment/decrement operations
if (parsedPreviousSplit && parsedPreviousSplit.trafficTypeName === split.trafficTypeName)
return;
// update traffic type counts
return _this._incrementCounts(split).then(function () {
if (parsedPreviousSplit)
return _this._decrementCounts(parsedPreviousSplit);
});
}).then(function () { return _this._updateFlagSets(name, parsedPreviousSplit && parsedPreviousSplit.sets, split.sets); });
}).then(function () { return true; });
};
/**
* Remove a given split.
* The returned promise is resolved when the operation success, with a boolean indicating if the split existed or not.
* or rejected if it fails (e.g., wrapper operation fails).
*/
SplitsCachePluggable.prototype.removeSplit = function (name) {
var _this = this;
return this.getSplit(name).then(function (split) {
if (split) {
return _this._decrementCounts(split).then(function () { return _this._updateFlagSets(name, split.sets); });
}
}).then(function () {
return _this.wrapper.del(_this.keys.buildSplitKey(name));
});
};
/**
* Get split.
* The returned promise is resolved with the split definition or null if it's not defined,
* or rejected if wrapper operation fails.
*/
SplitsCachePluggable.prototype.getSplit = function (name) {
return this.wrapper.get(this.keys.buildSplitKey(name))
.then(function (maybeSplit) { return maybeSplit && JSON.parse(maybeSplit); });
};
/**
* Get list of splits.
* The returned promise is resolved with a map of split names to their split definition or null if it's not defined,
* or rejected if wrapper operation fails.
*/
SplitsCachePluggable.prototype.getSplits = function (names) {
var _this = this;
var keys = names.map(function (name) { return _this.keys.buildSplitKey(name); });
return this.wrapper.getMany(keys).then(function (splitDefinitions) {
var splits = {};
names.forEach(function (name, idx) {
var split = splitDefinitions[idx];
splits[name] = split && JSON.parse(split);
});
return Promise.resolve(splits);
});
};
/**
* Get list of all split definitions.
* The returned promise is resolved with the list of split definitions,
* or rejected if wrapper operation fails.
*/
SplitsCachePluggable.prototype.getAll = function () {
var _this = this;
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix())
.then(function (listOfKeys) { return _this.wrapper.getMany(listOfKeys); })
.then(function (splitDefinitions) { return splitDefinitions.map(function (splitDefinition) {
return JSON.parse(splitDefinition);
}); });
};
/**
* Get list of split names.
* The returned promise is resolved with the list of split names,
* or rejected if wrapper operation fails.
*/
SplitsCachePluggable.prototype.getSplitNames = function () {
var _this = this;
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
};
/**
* Get list of feature flag names related to a given list of flag set names.
* The returned promise is resolved with the list of feature flag names per flag set.
* It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it).
*/
SplitsCachePluggable.prototype.getNamesByFlagSets = function (flagSets) {
var _this = this;
return Promise.all(flagSets.map(function (flagSet) {
var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
return _this.wrapper.getItems(flagSetKey).catch(function () { return []; });
})).then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new Set(namesByFlagSet); }); });
};
/**
* Check traffic type existence.
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
* In case of wrapper operation failures, the promise resolves with a true value, assuming that the TT might exist.
* It will never be rejected.
*/
SplitsCachePluggable.prototype.trafficTypeExists = function (trafficType) {
var _this = this;
// If there is a number there should be > 0, otherwise the TT is considered as not existent.
return this.wrapper.get(this.keys.buildTrafficTypeKey(trafficType))
.then(function (ttCount) {
if (ttCount === null)
return false; // if entry doesn't exist, means that TT doesn't exist
ttCount = parseInt(ttCount, 10);
if (!(0, lang_1.isFiniteNumber)(ttCount) || ttCount < 0) {
_this.log.info(constants_1.LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to data corruption of some sorts."));
return false;
}
return ttCount > 0;
}).catch(function (e) {
_this.log.error(constants_1.LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to an error: " + e + "."));
// If there is an error, bypass the validation so the event can get tracked.
return true;
});
};
/**
* Set till number.
* The returned promise is resolved when the operation success,
* or rejected if it fails (e.g., wrapper operation fails).
*/
SplitsCachePluggable.prototype.setChangeNumber = function (changeNumber) {
return this.wrapper.set(this.keys.buildSplitsTillKey(), changeNumber + '');
};
/**
* Get till number or -1 if it's not defined.
* The returned promise is resolved with the changeNumber or -1 if it doesn't exist or a wrapper operation fails.
* The promise will never be rejected.
*/
SplitsCachePluggable.prototype.getChangeNumber = function () {
var _this = this;
return this.wrapper.get(this.keys.buildSplitsTillKey()).then(function (value) {
var i = parseInt(value, 10);
return (0, lang_1.isNaNNumber)(i) ? -1 : i;
}).catch(function (e) {
_this.log.error(constants_1.LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
return -1;
});
};
// @TODO implement if required by DataLoader or producer mode
SplitsCachePluggable.prototype.clear = function () {
return Promise.resolve(true);
};
return SplitsCachePluggable;
}(AbstractSplitsCacheAsync_1.AbstractSplitsCacheAsync));
exports.SplitsCachePluggable = SplitsCachePluggable;