@splitsoftware/splitio-commons 2.4.2-rc.1 → 2.4.2-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +13 -1
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/sdkClient/sdkClient.js +0 -1
- package/cjs/sdkFactory/index.js +3 -10
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/cjs/storages/AbstractSplitsCacheSync.js +8 -3
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/cjs/storages/inLocalStorage/index.js +28 -13
- package/cjs/storages/inLocalStorage/storageAdapter.js +48 -0
- package/cjs/storages/inLocalStorage/validateCache.js +25 -23
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/pollingManagerCS.js +5 -4
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/cjs/sync/syncManagerOnline.js +31 -26
- package/cjs/trackers/impressionsTracker.js +4 -4
- package/cjs/utils/env/isLocalStorageAvailable.js +28 -5
- package/cjs/utils/settingsValidation/splitFilters.js +0 -6
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/logger/messages/error.js +1 -1
- package/esm/sdkClient/sdkClient.js +0 -1
- package/esm/sdkFactory/index.js +3 -10
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/esm/storages/AbstractSplitsCacheSync.js +6 -2
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/esm/storages/inLocalStorage/index.js +29 -14
- package/esm/storages/inLocalStorage/storageAdapter.js +44 -0
- package/esm/storages/inLocalStorage/validateCache.js +25 -23
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/pollingManagerCS.js +5 -4
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/esm/sync/syncManagerOnline.js +31 -26
- package/esm/trackers/impressionsTracker.js +4 -4
- package/esm/utils/env/isLocalStorageAvailable.js +24 -3
- package/esm/utils/settingsValidation/splitFilters.js +0 -6
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/logger/messages/error.ts +1 -1
- package/src/sdkClient/sdkClient.ts +0 -1
- package/src/sdkFactory/index.ts +3 -13
- package/src/services/splitHttpClient.ts +1 -1
- package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
- package/src/storages/AbstractSplitsCacheSync.ts +8 -3
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -20
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
- package/src/storages/inLocalStorage/index.ts +33 -16
- package/src/storages/inLocalStorage/storageAdapter.ts +50 -0
- package/src/storages/inLocalStorage/validateCache.ts +26 -23
- package/src/storages/types.ts +17 -1
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
- package/src/sync/polling/pollingManagerCS.ts +5 -4
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -2
- package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -1
- package/src/sync/syncManagerOnline.ts +30 -24
- package/src/trackers/impressionsTracker.ts +3 -3
- package/src/utils/env/isLocalStorageAvailable.ts +24 -3
- package/src/utils/settingsValidation/splitFilters.ts +0 -6
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +57 -16
|
@@ -4,6 +4,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '..
|
|
|
4
4
|
import { isConsentGranted } from '../consent';
|
|
5
5
|
import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
|
|
6
6
|
import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
|
|
7
|
+
import { usesSegmentsSync } from '../storages/AbstractSplitsCacheSync';
|
|
7
8
|
/**
|
|
8
9
|
* Online SyncManager factory.
|
|
9
10
|
* Can be used for server-side API, and client-side API with or without multiple clients.
|
|
@@ -65,35 +66,39 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
65
66
|
*/
|
|
66
67
|
start: function () {
|
|
67
68
|
running = true;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
// @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
|
|
70
|
+
submitterManager.start(!isConsentGranted(settings));
|
|
71
|
+
return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
|
|
72
|
+
if (!running)
|
|
73
|
+
return;
|
|
74
|
+
if (startFirstTime) {
|
|
75
|
+
// Emits SDK_READY_FROM_CACHE
|
|
76
|
+
if (isCacheLoaded)
|
|
77
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
78
|
+
}
|
|
79
|
+
// start syncing splits and segments
|
|
80
|
+
if (pollingManager) {
|
|
81
|
+
// If synchronization is disabled pushManager and pollingManager should not start
|
|
82
|
+
if (syncEnabled) {
|
|
83
|
+
if (pushManager) {
|
|
84
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
85
|
+
if (startFirstTime) {
|
|
86
|
+
pollingManager.syncAll();
|
|
87
|
+
}
|
|
88
|
+
pushManager.start();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
pollingManager.start();
|
|
81
92
|
}
|
|
82
|
-
pushManager.start();
|
|
83
93
|
}
|
|
84
94
|
else {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else {
|
|
89
|
-
if (startFirstTime) {
|
|
90
|
-
pollingManager.syncAll();
|
|
95
|
+
if (startFirstTime) {
|
|
96
|
+
pollingManager.syncAll();
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
submitterManager.start(!isConsentGranted(settings));
|
|
96
|
-
startFirstTime = false;
|
|
100
|
+
startFirstTime = false;
|
|
101
|
+
});
|
|
97
102
|
},
|
|
98
103
|
/**
|
|
99
104
|
* Method used to stop/pause the syncManager.
|
|
@@ -127,7 +132,7 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
127
132
|
if (pushManager) {
|
|
128
133
|
if (pollingManager.isRunning()) {
|
|
129
134
|
// if doing polling, we must start the periodic fetch of data
|
|
130
|
-
if (
|
|
135
|
+
if (usesSegmentsSync(storage))
|
|
131
136
|
mySegmentsSyncTask.start();
|
|
132
137
|
}
|
|
133
138
|
else {
|
|
@@ -137,7 +142,7 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
else {
|
|
140
|
-
if (
|
|
145
|
+
if (usesSegmentsSync(storage))
|
|
141
146
|
mySegmentsSyncTask.start();
|
|
142
147
|
}
|
|
143
148
|
}
|
|
@@ -6,7 +6,7 @@ import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
|
6
6
|
* Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
|
|
7
7
|
*/
|
|
8
8
|
export function impressionsTrackerFactory(settings, impressionsCache, noneStrategy, strategy, whenInit, integrationsManager, telemetryCache) {
|
|
9
|
-
var log = settings.log, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
|
|
9
|
+
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
|
|
10
10
|
return {
|
|
11
11
|
track: function (impressions, attributes) {
|
|
12
12
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
@@ -39,7 +39,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
// @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
|
|
42
|
-
if (
|
|
42
|
+
if (impressionListener || integrationsManager) {
|
|
43
43
|
var _loop_1 = function (i) {
|
|
44
44
|
var impressionData = {
|
|
45
45
|
// copy of impression, to avoid unexpected behavior if modified by integrations or impressionListener
|
|
@@ -56,8 +56,8 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
56
56
|
if (integrationsManager)
|
|
57
57
|
integrationsManager.handleImpression(impressionData);
|
|
58
58
|
try { // @ts-ignore. An exception on the listeners should not break the SDK.
|
|
59
|
-
if (
|
|
60
|
-
|
|
59
|
+
if (impressionListener)
|
|
60
|
+
impressionListener.logImpression(impressionData);
|
|
61
61
|
}
|
|
62
62
|
catch (err) {
|
|
63
63
|
log.error(ERROR_IMPRESSIONS_LISTENER, [err]);
|
|
@@ -1,12 +1,33 @@
|
|
|
1
|
-
/* eslint-disable no-undef */
|
|
2
1
|
export function isLocalStorageAvailable() {
|
|
2
|
+
try {
|
|
3
|
+
// eslint-disable-next-line no-undef
|
|
4
|
+
return isValidStorageWrapper(localStorage);
|
|
5
|
+
}
|
|
6
|
+
catch (e) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function isValidStorageWrapper(wrapper) {
|
|
3
11
|
var mod = '__SPLITSOFTWARE__';
|
|
4
12
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
wrapper.setItem(mod, mod);
|
|
14
|
+
wrapper.getItem(mod);
|
|
15
|
+
wrapper.removeItem(mod);
|
|
7
16
|
return true;
|
|
8
17
|
}
|
|
9
18
|
catch (e) {
|
|
10
19
|
return false;
|
|
11
20
|
}
|
|
12
21
|
}
|
|
22
|
+
export function isWebStorage(wrapper) {
|
|
23
|
+
if (typeof wrapper.length === 'number') {
|
|
24
|
+
try {
|
|
25
|
+
wrapper.key(0);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
@@ -58,12 +58,6 @@ function validateSplitFilter(log, type, values, maxLength) {
|
|
|
58
58
|
/**
|
|
59
59
|
* Returns a string representing the URL encoded query component of /splitChanges URL.
|
|
60
60
|
*
|
|
61
|
-
* The possible formats of the query string are:
|
|
62
|
-
* - null: if all filters are empty
|
|
63
|
-
* - '&names=<comma-separated-values>': if only `byPrefix` filter is undefined
|
|
64
|
-
* - '&prefixes=<comma-separated-values>': if only `byName` filter is undefined
|
|
65
|
-
* - '&names=<comma-separated-values>&prefixes=<comma-separated-values>': if no one is undefined
|
|
66
|
-
*
|
|
67
61
|
* @param groupedFilters - object of filters. Each filter must be a list of valid, unique and ordered string values.
|
|
68
62
|
* @returns null or string with the `split filter query` component of the URL.
|
|
69
63
|
*/
|
|
@@ -3,7 +3,7 @@ import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
|
|
|
3
3
|
import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
|
|
4
4
|
export function __InLocalStorageMockFactory(params) {
|
|
5
5
|
var result = InMemoryStorageCSFactory(params);
|
|
6
|
-
result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
|
|
6
|
+
result.validateCache = function () { return Promise.resolve(true); }; // to emit SDK_READY_FROM_CACHE
|
|
7
7
|
return result;
|
|
8
8
|
}
|
|
9
9
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ export const codesError: [number, string][] = [
|
|
|
14
14
|
[c.ERROR_SYNC_OFFLINE_LOADING, c.LOG_PREFIX_SYNC_OFFLINE + 'There was an issue loading the mock feature flags data. No changes will be applied to the current cache. %s'],
|
|
15
15
|
[c.ERROR_STREAMING_SSE, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to connect or error on streaming connection, with error message: %s'],
|
|
16
16
|
[c.ERROR_STREAMING_AUTH, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to authenticate for streaming. Error: %s.'],
|
|
17
|
-
[c.ERROR_HTTP, '
|
|
17
|
+
[c.ERROR_HTTP, 'HTTP request failed with %s. URL: %s. Message: %s'],
|
|
18
18
|
// client status
|
|
19
19
|
[c.ERROR_CLIENT_LISTENER, 'A listener was added for %s on the SDK, which has already fired and won\'t be emitted again. The callback won\'t be executed.'],
|
|
20
20
|
[c.ERROR_CLIENT_DESTROYED, '%s: Client has already been destroyed - no calls possible.'],
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -107,7 +107,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
107
107
|
|
|
108
108
|
log.info(NEW_FACTORY);
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
return objectAssign({
|
|
111
112
|
// Split evaluation and event tracking engine
|
|
112
113
|
client: clientMethod,
|
|
113
114
|
|
|
@@ -125,17 +126,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
125
126
|
destroy() {
|
|
126
127
|
hasInit = false;
|
|
127
128
|
return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
__ctx: ctx
|
|
129
|
+
}
|
|
131
130
|
}, extraProps && extraProps(ctx), lazyInit ? { init } : init());
|
|
132
|
-
|
|
133
|
-
// append factory to global
|
|
134
|
-
if (typeof window === 'object') { // @ts-ignore
|
|
135
|
-
// eslint-disable-next-line no-undef
|
|
136
|
-
(window.__HARNESS_FME__ = window.__HARNESS_FME__ || []).push(factory);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// @ts-ignore
|
|
140
|
-
return factory;
|
|
141
131
|
}
|
|
@@ -70,7 +70,7 @@ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFet
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (!resp || resp.status !== 403) { // 403's log we'll be handled somewhere else.
|
|
73
|
-
log[logErrorsAsInfo ? 'info' : 'error'](ERROR_HTTP, [resp ? resp.status : '
|
|
73
|
+
log[logErrorsAsInfo ? 'info' : 'error'](ERROR_HTTP, [resp ? 'status code ' + resp.status : 'no status code', url, msg]);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const networkError: NetworkError = new Error(msg);
|
|
@@ -49,12 +49,10 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
|
|
|
49
49
|
* For client-side synchronizer: it resets or updates the cache.
|
|
50
50
|
*/
|
|
51
51
|
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
|
|
52
|
-
this.setChangeNumber(segmentsData.cn);
|
|
53
|
-
|
|
54
52
|
const { added, removed } = segmentsData as MySegmentsData;
|
|
53
|
+
let isDiff = false;
|
|
55
54
|
|
|
56
55
|
if (added && removed) {
|
|
57
|
-
let isDiff = false;
|
|
58
56
|
|
|
59
57
|
added.forEach(segment => {
|
|
60
58
|
isDiff = this.addSegment(segment) || isDiff;
|
|
@@ -63,32 +61,40 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
|
|
|
63
61
|
removed.forEach(segment => {
|
|
64
62
|
isDiff = this.removeSegment(segment) || isDiff;
|
|
65
63
|
});
|
|
64
|
+
} else {
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
|
|
67
|
+
const storedSegmentKeys = this.getRegisteredSegments().sort();
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Extreme fast => everything is empty
|
|
70
|
+
if (!names.length && !storedSegmentKeys.length) {
|
|
71
|
+
isDiff = false;
|
|
72
|
+
} else {
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
if (!names.length && !storedSegmentKeys.length) return false;
|
|
74
|
+
let index = 0;
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// Quick path => no changes
|
|
79
|
+
if (index === names.length && index === storedSegmentKeys.length) {
|
|
80
|
+
isDiff = false;
|
|
81
|
+
} else {
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
// Slowest path => add and/or remove segments
|
|
84
|
+
for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
|
|
85
|
+
this.removeSegment(storedSegmentKeys[removeIndex]);
|
|
86
|
+
}
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
88
|
+
for (let addIndex = index; addIndex < names.length; addIndex++) {
|
|
89
|
+
this.addSegment(names[addIndex]);
|
|
90
|
+
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
isDiff = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
this.setChangeNumber(segmentsData.cn);
|
|
98
|
+
return isDiff;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISplitsCacheSync } from './types';
|
|
1
|
+
import { ISplitsCacheSync, IStorageSync } from './types';
|
|
2
2
|
import { IRBSegment, ISplit } from '../dtos/types';
|
|
3
3
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
4
4
|
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
|
|
@@ -14,9 +14,10 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
14
14
|
protected abstract setChangeNumber(changeNumber: number): boolean | void
|
|
15
15
|
|
|
16
16
|
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean {
|
|
17
|
+
let updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
|
|
18
|
+
updated = toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
|
|
17
19
|
this.setChangeNumber(changeNumber);
|
|
18
|
-
|
|
19
|
-
return toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
|
|
20
|
+
return updated;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
abstract getSplit(name: string): ISplit | null
|
|
@@ -88,3 +89,7 @@ export function usesSegments(ruleEntity: ISplit | IRBSegment) {
|
|
|
88
89
|
|
|
89
90
|
return false;
|
|
90
91
|
}
|
|
92
|
+
|
|
93
|
+
export function usesSegmentsSync(storage: Pick<IStorageSync, 'splits' | 'rbSegments'>) {
|
|
94
|
+
return storage.splits.usesSegments() || storage.rbSegments.usesSegments();
|
|
95
|
+
}
|
|
@@ -3,16 +3,19 @@ import { isNaNNumber } from '../../utils/lang';
|
|
|
3
3
|
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
|
|
4
4
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
5
5
|
import { LOG_PREFIX, DEFINED } from './constants';
|
|
6
|
+
import { StorageAdapter } from '../types';
|
|
6
7
|
|
|
7
8
|
export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
8
9
|
|
|
9
10
|
private readonly keys: MySegmentsKeyBuilder;
|
|
10
11
|
private readonly log: ILogger;
|
|
12
|
+
private readonly storage: StorageAdapter;
|
|
11
13
|
|
|
12
|
-
constructor(log: ILogger, keys: MySegmentsKeyBuilder) {
|
|
14
|
+
constructor(log: ILogger, keys: MySegmentsKeyBuilder, storage: StorageAdapter) {
|
|
13
15
|
super();
|
|
14
16
|
this.log = log;
|
|
15
17
|
this.keys = keys;
|
|
18
|
+
this.storage = storage;
|
|
16
19
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -20,8 +23,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
20
23
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
21
24
|
|
|
22
25
|
try {
|
|
23
|
-
if (
|
|
24
|
-
|
|
26
|
+
if (this.storage.getItem(segmentKey) === DEFINED) return false;
|
|
27
|
+
this.storage.setItem(segmentKey, DEFINED);
|
|
25
28
|
return true;
|
|
26
29
|
} catch (e) {
|
|
27
30
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -33,8 +36,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
33
36
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
34
37
|
|
|
35
38
|
try {
|
|
36
|
-
if (
|
|
37
|
-
|
|
39
|
+
if (this.storage.getItem(segmentKey) !== DEFINED) return false;
|
|
40
|
+
this.storage.removeItem(segmentKey);
|
|
38
41
|
return true;
|
|
39
42
|
} catch (e) {
|
|
40
43
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -43,18 +46,16 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
isInSegment(name: string): boolean {
|
|
46
|
-
return
|
|
49
|
+
return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
getRegisteredSegments(): string[] {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return accum;
|
|
57
|
-
}, [] as string[]);
|
|
53
|
+
const registeredSegments: string[] = [];
|
|
54
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
55
|
+
const segmentName = this.keys.extractSegmentName(this.storage.key(i)!);
|
|
56
|
+
if (segmentName) registeredSegments.push(segmentName);
|
|
57
|
+
}
|
|
58
|
+
return registeredSegments;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
getKeysCount() {
|
|
@@ -63,8 +64,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
63
64
|
|
|
64
65
|
protected setChangeNumber(changeNumber?: number) {
|
|
65
66
|
try {
|
|
66
|
-
if (changeNumber)
|
|
67
|
-
else
|
|
67
|
+
if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
68
|
+
else this.storage.removeItem(this.keys.buildTillKey());
|
|
68
69
|
} catch (e) {
|
|
69
70
|
this.log.error(e);
|
|
70
71
|
}
|
|
@@ -72,7 +73,7 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
72
73
|
|
|
73
74
|
getChangeNumber() {
|
|
74
75
|
const n = -1;
|
|
75
|
-
let value: string | number | null =
|
|
76
|
+
let value: string | number | null = this.storage.getItem(this.keys.buildTillKey());
|
|
76
77
|
|
|
77
78
|
if (value !== null) {
|
|
78
79
|
value = parseInt(value, 10);
|
|
@@ -5,34 +5,37 @@ import { isFiniteNumber, isNaNNumber, toNumber } from '../../utils/lang';
|
|
|
5
5
|
import { setToArray } from '../../utils/lang/sets';
|
|
6
6
|
import { usesSegments } from '../AbstractSplitsCacheSync';
|
|
7
7
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
8
|
-
import { IRBSegmentsCacheSync } from '../types';
|
|
8
|
+
import { IRBSegmentsCacheSync, StorageAdapter } from '../types';
|
|
9
9
|
import { LOG_PREFIX } from './constants';
|
|
10
10
|
|
|
11
11
|
export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
12
12
|
|
|
13
13
|
private readonly keys: KeyBuilderCS;
|
|
14
14
|
private readonly log: ILogger;
|
|
15
|
+
private readonly storage: StorageAdapter;
|
|
15
16
|
|
|
16
|
-
constructor(settings: ISettings, keys: KeyBuilderCS) {
|
|
17
|
+
constructor(settings: ISettings, keys: KeyBuilderCS, storage: StorageAdapter) {
|
|
17
18
|
this.keys = keys;
|
|
18
19
|
this.log = settings.log;
|
|
20
|
+
this.storage = storage;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
clear() {
|
|
22
24
|
this.getNames().forEach(name => this.remove(name));
|
|
23
|
-
|
|
25
|
+
this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
|
|
29
|
+
let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
|
|
30
|
+
updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
27
31
|
this.setChangeNumber(changeNumber);
|
|
28
|
-
|
|
29
|
-
return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
|
|
32
|
+
return updated;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
private setChangeNumber(changeNumber: number) {
|
|
33
36
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
38
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
36
39
|
} catch (e) {
|
|
37
40
|
this.log.error(LOG_PREFIX + e);
|
|
38
41
|
}
|
|
@@ -40,20 +43,19 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
40
43
|
|
|
41
44
|
private updateSegmentCount(diff: number) {
|
|
42
45
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
43
|
-
const count = toNumber(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
else localStorage.removeItem(segmentsCountKey);
|
|
46
|
+
const count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
|
|
47
|
+
if (count > 0) this.storage.setItem(segmentsCountKey, count + '');
|
|
48
|
+
else this.storage.removeItem(segmentsCountKey);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
private add(rbSegment: IRBSegment): boolean {
|
|
50
52
|
try {
|
|
51
53
|
const name = rbSegment.name;
|
|
52
54
|
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
53
|
-
const
|
|
54
|
-
const previous =
|
|
55
|
+
const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
56
|
+
const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
57
59
|
|
|
58
60
|
let usesSegmentsDiff = 0;
|
|
59
61
|
if (previous && usesSegments(previous)) usesSegmentsDiff--;
|
|
@@ -72,7 +74,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
72
74
|
const rbSegment = this.get(name);
|
|
73
75
|
if (!rbSegment) return false;
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
76
78
|
|
|
77
79
|
if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
|
|
78
80
|
|
|
@@ -84,13 +86,13 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
private getNames(): string[] {
|
|
87
|
-
const len =
|
|
89
|
+
const len = this.storage.length;
|
|
88
90
|
const accum = [];
|
|
89
91
|
|
|
90
92
|
let cur = 0;
|
|
91
93
|
|
|
92
94
|
while (cur < len) {
|
|
93
|
-
const key =
|
|
95
|
+
const key = this.storage.key(cur);
|
|
94
96
|
|
|
95
97
|
if (key != null && this.keys.isRBSegmentKey(key)) accum.push(this.keys.extractKey(key));
|
|
96
98
|
|
|
@@ -101,7 +103,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
get(name: string): IRBSegment | null {
|
|
104
|
-
const item =
|
|
106
|
+
const item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
|
|
105
107
|
return item && JSON.parse(item);
|
|
106
108
|
}
|
|
107
109
|
|
|
@@ -113,7 +115,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
113
115
|
|
|
114
116
|
getChangeNumber(): number {
|
|
115
117
|
const n = -1;
|
|
116
|
-
let value: string | number | null =
|
|
118
|
+
let value: string | number | null = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
117
119
|
|
|
118
120
|
if (value !== null) {
|
|
119
121
|
value = parseInt(value, 10);
|
|
@@ -125,7 +127,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
usesSegments(): boolean {
|
|
128
|
-
const storedCount =
|
|
130
|
+
const storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
129
131
|
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
130
132
|
|
|
131
133
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|