UNPKG

@splitsoftware/splitio-commons

Version:
133 lines (132 loc) 7.87 kB
import { __assign } from "tslib"; import { KeyBuilderSS } from '../KeyBuilderSS'; import { DefinitionsCachePluggable } from './DefinitionsCachePluggable'; import { SegmentsCachePluggable } from './SegmentsCachePluggable'; import { ImpressionsCachePluggable } from './ImpressionsCachePluggable'; import { EventsCachePluggable } from './EventsCachePluggable'; import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { getStorageHash, validatePrefix } from '../KeyBuilder'; import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'; import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory'; import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory'; import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory'; import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory'; import { TelemetryCachePluggable } from './TelemetryCachePluggable'; import { ImpressionCountsCachePluggable } from './ImpressionCountsCachePluggable'; import { UniqueKeysCachePluggable } from './UniqueKeysCachePluggable'; import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory'; import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS'; import { metadataBuilder } from '../utils'; import { LOG_PREFIX } from '../pluggable/constants'; import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable'; import { checkIfServerSide } from '../../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 (!isObject(options) || !isObject(options.wrapper)) throw new Error(NO_VALID_WRAPPER); var wrapper = options.wrapper; var missingMethods = 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. */ export function PluggableStorage(options) { validatePluggableStorageOptions(options); var prefix = 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 = metadataBuilder(settings); var keys = new KeyBuilderSS(prefix, metadata); var wrapper = wrapperAdapter(log, options.wrapper); var isSynchronizer = mode === undefined; // If mode is not defined, the synchronizer is running var isPartialConsumer = mode === CONSUMER_PARTIAL_MODE; var telemetry = shouldRecordTelemetry(params) || isSynchronizer ? isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) : undefined; var impressionCountsCache = isPartialConsumer ? new ImpressionCountsCacheInMemory() : new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper); var uniqueKeysCache = isPartialConsumer ? checkIfServerSide(settings) ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() : new 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 = getStorageHash(settings); if (hash !== currentHash) { log.info(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(log, keys, wrapper, settings.sync.__splitFiltersValidation), rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), impressionCounts: impressionCountsCache, events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new 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 __assign(__assign({}, this), { // no-op destroy, to disconnect the wrapper only when the main client is destroyed destroy: function () { } }); } }; } PluggableStorageFactory.type = STORAGE_PLUGGABLE; return PluggableStorageFactory; }