@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
80 lines (79 loc) • 4.02 kB
JavaScript
import { timeout } from '../../../utils/promise/timeout';
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants';
import { SYNC_FETCH_RETRY } from '../../../logger/constants';
import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
import { usesSegmentsSync } from '../../../storages/AbstractDefinitionsCacheSync';
/**
* factory of MySegments updater, a task that:
* - fetches mySegments using `mySegmentsFetcher`
* - updates `mySegmentsCache`
* - uses `segmentsEventEmitter` to emit events related to segments data updates
*/
export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
var segments = storage.segments, largeSegments = storage.largeSegments;
var readyOnAlreadyExistentState = true;
var startingUp = true;
/** timeout and telemetry decorator for `splitChangesFetcher` promise */
function _promiseDecorator(promise) {
if (startingUp)
promise = timeout(requestTimeoutBeforeReady, promise);
return promise;
}
// @TODO if allowing pluggable storages, handle async execution
function updateSegments(segmentsData) {
var shouldNotifyUpdate;
if (segmentsData.type !== undefined) {
shouldNotifyUpdate = segmentsData.type === MEMBERSHIPS_LS_UPDATE ?
largeSegments.resetSegments(segmentsData) :
segments.resetSegments(segmentsData);
}
else {
shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
}
if (storage.save)
storage.save();
// Notify update if required
if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
readyOnAlreadyExistentState = false;
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] });
}
}
function _mySegmentsUpdater(retry, segmentsData, noCache, till) {
var updaterPromise = segmentsData ?
// If segmentsData is provided, there is no need to fetch mySegments
new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
// If not provided, fetch mySegments
mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(function (segments) {
updateSegments(segments);
// Only when we have downloaded and stored segments completely, we should not keep retrying anymore
startingUp = false;
return true;
});
return updaterPromise.catch(function (error) {
if (startingUp && retriesOnFailureBeforeReady > retry) {
retry += 1;
log.warn(SYNC_FETCH_RETRY, ['memberships', retry, error]);
return _mySegmentsUpdater(retry); // no need to forward `segmentList` and `noCache` params
}
else {
startingUp = false;
}
return false;
});
}
/**
* MySegments updater returns a promise that resolves with a `false` boolean value if it fails to fetch mySegments or synchronize them with the storage.
* Returned promise will not be rejected.
*
* @param segmentsData - it can be:
* (1) the list of mySegments names to sync in the storage,
* (2) an object with a segment name and action (true: add, or false: delete) to update the storage,
* (3) or `undefined`, for which the updater will fetch mySegments in order to sync the storage.
* @param noCache - true to revalidate data to fetch
* @param till - query param to bypass CDN requests
*/
return function mySegmentsUpdater(segmentsData, noCache, till) {
return _mySegmentsUpdater(0, segmentsData, noCache, till);
};
}