UNPKG

@splitsoftware/splitio-commons

Version:
179 lines (178 loc) 9.43 kB
import { submitterManagerFactory } from './submitters/submitterManager'; import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants'; import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants'; import { isConsentGranted } from '../consent'; import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants'; import { SDK_DEFINITIONS_CACHE_LOADED } from '../readiness/constants'; import { usesSegmentsSync } from '../storages/AbstractDefinitionsCacheSync'; import { splitChangesFetcherFactory } from './polling/fetchers/splitChangesFetcher'; import { segmentChangesFetcherFactory } from './polling/fetchers/segmentChangesFetcher'; /** * Online SyncManager factory. * Can be used for server-side API, and client-side API with or without multiple clients. * * @param pollingManagerFactory - allows to specialize the SyncManager for server-side or client-side API by passing * `pollingManagerSSFactory` or `pollingManagerCSFactory` respectively. * @param pushManagerFactory - optional to build a SyncManager with or without streaming support * @param definitionChangesFetcherFactory - optional to replace the default split changes fetcher */ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory, definitionFetcherFactory, segmentFetcherFactory) { if (definitionFetcherFactory === void 0) { definitionFetcherFactory = splitChangesFetcherFactory; } if (segmentFetcherFactory === void 0) { segmentFetcherFactory = function (params) { return segmentChangesFetcherFactory(params.serviceApi.fetchSegmentChanges); }; } /** * SyncManager factory for modular SDK */ return function (params) { var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker, storage = params.storage, readiness = params.readiness; /** Polling Manager */ var pollingManager = pollingManagerFactory && pollingManagerFactory(params, definitionFetcherFactory(params), segmentFetcherFactory(params)); /** Push Manager */ var pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ? pushManagerFactory(params, pollingManager) : undefined; /** Submitter Manager */ // It is not injected as push and polling managers, because at the moment it is required var submitterManager = submitterManagerFactory(params); /** Sync Manager logic */ function startPolling() { if (pollingManager.isRunning()) { log.info(SYNC_CONTINUE_POLLING); } else { log.info(SYNC_START_POLLING); pollingManager.start(); telemetryTracker.streamingEvent(SYNC_MODE_UPDATE, POLLING); } } function stopPollingAndSyncAll() { log.info(SYNC_STOP_POLLING); // if polling, stop if (pollingManager.isRunning()) { pollingManager.stop(); telemetryTracker.streamingEvent(SYNC_MODE_UPDATE, STREAMING); } // fetch splits and segments. There is no need to catch this promise (it is always resolved) pollingManager.syncAll(); } if (pushManager) { pushManager.on(PUSH_SUBSYSTEM_UP, stopPollingAndSyncAll); pushManager.on(PUSH_SUBSYSTEM_DOWN, startPolling); } var running = false; // flag that indicates whether the syncManager has been started (true) or stopped (false) var startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization return { // Exposed for fine-grained control of synchronization. // E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline). pollingManager: pollingManager, pushManager: pushManager, submitterManager: submitterManager, /** * Method used to start the syncManager for the first time, or resume it after being stopped. */ start: function () { running = true; // @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved submitterManager.start(!isConsentGranted(settings)); return Promise.resolve(storage.validateCache ? storage.validateCache() : { initialCacheLoad: true /* Fallback: assume initial load when validateCache doesn't exist */ }).then(function (cacheMetadata) { if (!running) return; if (startFirstTime) { // Emits SDK_READY_FROM_CACHE if (!cacheMetadata.initialCacheLoad) { readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED, cacheMetadata); } } // start syncing splits and segments if (pollingManager) { // If synchronization is disabled pushManager and pollingManager should not start if (syncEnabled) { if (pushManager) { // Doesn't call `syncAll` when the syncManager is resuming if (startFirstTime) { pollingManager.syncAll(); } pushManager.start(); } else { pollingManager.start(); } } else { if (startFirstTime) { pollingManager.syncAll(); } } } startFirstTime = false; }); }, /** * Method used to stop/pause the syncManager. */ stop: function () { running = false; // stop syncing if (pushManager) pushManager.stop(); if (pollingManager && pollingManager.isRunning()) pollingManager.stop(); // stop periodic data recording (events, impressions, telemetry). submitterManager.stop(); }, isRunning: function () { return running; }, flush: function () { return submitterManager.execute(!isConsentGranted(settings)); }, // [Only used for client-side] // If polling and push managers are defined (standalone mode), they implement the interfaces for client-side shared: function (matchingKey, readinessManager, storage) { if (!pollingManager) return; var mySegmentsSyncTask = pollingManager.add(matchingKey, readinessManager, storage); if (syncEnabled && pushManager) pushManager.add(matchingKey, mySegmentsSyncTask); if (running) { if (syncEnabled) { if (pushManager) { if (pollingManager.isRunning()) { // if doing polling, we must start the periodic fetch of data if (usesSegmentsSync(storage)) mySegmentsSyncTask.start(); } else { // if not polling, we must execute the sync task for the initial fetch // of segments since `syncAll` was already executed when starting the main client mySegmentsSyncTask.execute(); } } else { if (usesSegmentsSync(storage)) mySegmentsSyncTask.start(); } } else { if (!readinessManager.isReady()) mySegmentsSyncTask.execute(); } } return { stop: function () { // check in case `client.destroy()` has been invoked more than once for the same client var mySegmentsSyncTask = pollingManager.get(matchingKey); if (mySegmentsSyncTask) { // stop syncing if (pushManager) pushManager.remove(matchingKey); if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop(); pollingManager.remove(matchingKey); } }, flush: function () { return Promise.resolve(); } }; } }; }; }