@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
137 lines (136 loc) • 8.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluggableStorage = void 0;
var tslib_1 = require("tslib");
var KeyBuilderSS_1 = require("../KeyBuilderSS");
var DefinitionsCachePluggable_1 = require("./DefinitionsCachePluggable");
var SegmentsCachePluggable_1 = require("./SegmentsCachePluggable");
var ImpressionsCachePluggable_1 = require("./ImpressionsCachePluggable");
var EventsCachePluggable_1 = require("./EventsCachePluggable");
var wrapperAdapter_1 = require("./wrapperAdapter");
var lang_1 = require("../../utils/lang");
var KeyBuilder_1 = require("../KeyBuilder");
var constants_1 = require("../../utils/constants");
var ImpressionsCacheInMemory_1 = require("../inMemory/ImpressionsCacheInMemory");
var EventsCacheInMemory_1 = require("../inMemory/EventsCacheInMemory");
var ImpressionCountsCacheInMemory_1 = require("../inMemory/ImpressionCountsCacheInMemory");
var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
var TelemetryCachePluggable_1 = require("./TelemetryCachePluggable");
var ImpressionCountsCachePluggable_1 = require("./ImpressionCountsCachePluggable");
var UniqueKeysCachePluggable_1 = require("./UniqueKeysCachePluggable");
var UniqueKeysCacheInMemory_1 = require("../inMemory/UniqueKeysCacheInMemory");
var UniqueKeysCacheInMemoryCS_1 = require("../inMemory/UniqueKeysCacheInMemoryCS");
var utils_1 = require("../utils");
var constants_2 = require("../pluggable/constants");
var RBSegmentsCachePluggable_1 = require("./RBSegmentsCachePluggable");
var key_1 = require("../../utils/key");
var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
/**
* Validate pluggable storage factory options.
*
* @param options - user options
* @throws Will throw an error if the options are invalid. Example: wrapper is not provided or doesn't have some methods.
*/
function validatePluggableStorageOptions(options) {
if (!(0, lang_1.isObject)(options) || !(0, lang_1.isObject)(options.wrapper))
throw new Error(NO_VALID_WRAPPER);
var wrapper = options.wrapper;
var missingMethods = wrapperAdapter_1.METHODS_TO_PROMISE_WRAP.filter(function (method) { return typeof wrapper[method] !== 'function'; });
if (missingMethods.length)
throw new Error("".concat(NO_VALID_WRAPPER_INTERFACE, " The following methods are missing or invalid: ").concat(missingMethods));
}
// Async return type in `client.track` method on consumer partial mode
// No need to promisify impressions cache
function promisifyEventsTrack(events) {
var origTrack = events.track;
events.track = function () {
return Promise.resolve(origTrack.apply(this, arguments));
};
return events;
}
/**
* Pluggable storage factory for consumer server-side & client-side SplitFactory.
*/
function PluggableStorage(options) {
validatePluggableStorageOptions(options);
var prefix = (0, KeyBuilder_1.validatePrefix)(options.prefix);
function PluggableStorageFactory(params) {
var onReadyCb = params.onReadyCb, settings = params.settings, _a = params.settings, log = _a.log, mode = _a.mode, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
var metadata = (0, utils_1.metadataBuilder)(settings);
var keys = new KeyBuilderSS_1.KeyBuilderSS(prefix, metadata);
var wrapper = (0, wrapperAdapter_1.wrapperAdapter)(log, options.wrapper);
var isSynchronizer = mode === undefined; // If mode is not defined, the synchronizer is running
var isPartialConsumer = mode === constants_1.CONSUMER_PARTIAL_MODE;
var telemetry = (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) || isSynchronizer ?
isPartialConsumer ?
new TelemetryCacheInMemory_1.TelemetryCacheInMemory() :
new TelemetryCachePluggable_1.TelemetryCachePluggable(log, keys, wrapper) :
undefined;
var impressionCountsCache = isPartialConsumer ?
new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() :
new ImpressionCountsCachePluggable_1.ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper);
var uniqueKeysCache = isPartialConsumer ?
(0, key_1.checkIfServerSide)(settings) ? new UniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() :
new UniqueKeysCachePluggable_1.UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper);
// Connects to wrapper and emits SDK_READY event on main client
var connectPromise = wrapper.connect().then(function () {
if (isSynchronizer) {
// @TODO reuse InLocalStorage::validateCache logic
// In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
return wrapper.get(keys.buildHashKey()).then(function (hash) {
var currentHash = (0, KeyBuilder_1.getStorageHash)(settings);
if (hash !== currentHash) {
log.info(constants_2.LOG_PREFIX + 'Storage HASH has changed (SDK key, flags filter criteria or flags spec version was modified). Clearing cache');
return wrapper.getKeysByPrefix("".concat(keys.prefix, ".")).then(function (storageKeys) {
return Promise.all(storageKeys.map(function (storageKey) { return wrapper.del(storageKey); }));
}).then(function () { return wrapper.set(keys.buildHashKey(), currentHash); });
}
}).then(function () {
onReadyCb();
});
}
else {
// Start periodic flush of async storages if not running synchronizer (producer mode)
if (impressionCountsCache.start)
impressionCountsCache.start();
if (uniqueKeysCache.start)
uniqueKeysCache.start();
if (telemetry && telemetry.recordConfig)
telemetry.recordConfig();
onReadyCb();
}
}).catch(function (e) {
e = e || new Error('Error connecting wrapper');
onReadyCb(e);
return e; // Propagate error for shared clients
});
return {
definitions: new DefinitionsCachePluggable_1.DefinitionsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
rbSegments: new RBSegmentsCachePluggable_1.RBSegmentsCachePluggable(log, keys, wrapper),
segments: new SegmentsCachePluggable_1.SegmentsCachePluggable(log, keys, wrapper),
impressions: isPartialConsumer ? new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable_1.ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
impressionCounts: impressionCountsCache,
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable_1.EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
telemetry: telemetry,
uniqueKeys: uniqueKeysCache,
// Stop periodic flush and disconnect the underlying storage
destroy: function () {
return Promise.all(isSynchronizer ? [] : [
impressionCountsCache.stop && impressionCountsCache.stop(),
uniqueKeysCache.stop && uniqueKeysCache.stop(),
]).then(function () { return wrapper.disconnect(); });
},
// emits SDK_READY event on shared clients and returns a reference to the storage
shared: function (_, onReadyCb) {
connectPromise.then(onReadyCb);
return tslib_1.__assign(tslib_1.__assign({}, this), {
// no-op destroy, to disconnect the wrapper only when the main client is destroyed
destroy: function () { } });
}
};
}
PluggableStorageFactory.type = constants_1.STORAGE_PLUGGABLE;
return PluggableStorageFactory;
}
exports.PluggableStorage = PluggableStorage;