UNPKG

@splitsoftware/splitio-commons

Version:
137 lines (136 loc) 8.51 kB
"use strict"; 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;