@splitsoftware/splitio-commons 2.5.0-rc.1 → 2.5.1-rc.0
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/storages/getRolloutPlan.js +1 -1
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/cjs/storages/inLocalStorage/index.js +31 -13
- package/cjs/storages/inLocalStorage/storageAdapter.js +54 -0
- package/cjs/storages/inLocalStorage/validateCache.js +28 -23
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -0
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -0
- package/cjs/sync/syncManagerOnline.js +28 -24
- package/cjs/utils/env/isLocalStorageAvailable.js +22 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/storages/getRolloutPlan.js +1 -1
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/esm/storages/inLocalStorage/index.js +32 -14
- package/esm/storages/inLocalStorage/storageAdapter.js +50 -0
- package/esm/storages/inLocalStorage/validateCache.js +28 -23
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -0
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -0
- package/esm/sync/syncManagerOnline.js +28 -24
- package/esm/utils/env/isLocalStorageAvailable.js +19 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/storages/getRolloutPlan.ts +1 -1
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +19 -18
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
- package/src/storages/inLocalStorage/index.ts +37 -16
- package/src/storages/inLocalStorage/storageAdapter.ts +62 -0
- package/src/storages/inLocalStorage/validateCache.ts +29 -23
- package/src/storages/types.ts +19 -1
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -0
- package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -1
- package/src/sync/syncManagerOnline.ts +27 -22
- package/src/utils/env/isLocalStorageAvailable.ts +20 -0
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +38 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { LOG_PREFIX } from './constants';
|
|
2
|
+
export function storageAdapter(log, prefix, wrapper) {
|
|
3
|
+
var keys = [];
|
|
4
|
+
var cache = {};
|
|
5
|
+
var loadPromise;
|
|
6
|
+
var savePromise = Promise.resolve();
|
|
7
|
+
return {
|
|
8
|
+
load: function () {
|
|
9
|
+
return loadPromise || (loadPromise = Promise.resolve().then(function () {
|
|
10
|
+
return wrapper.getItem(prefix);
|
|
11
|
+
}).then(function (storedCache) {
|
|
12
|
+
cache = JSON.parse(storedCache || '{}');
|
|
13
|
+
keys = Object.keys(cache);
|
|
14
|
+
}).catch(function (e) {
|
|
15
|
+
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `getItem` method, with error: ' + e);
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
save: function () {
|
|
19
|
+
return savePromise = savePromise.then(function () {
|
|
20
|
+
return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache)));
|
|
21
|
+
}).catch(function (e) {
|
|
22
|
+
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `setItem` method, with error: ' + e);
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
whenSaved: function () {
|
|
26
|
+
return savePromise;
|
|
27
|
+
},
|
|
28
|
+
get length() {
|
|
29
|
+
return keys.length;
|
|
30
|
+
},
|
|
31
|
+
getItem: function (key) {
|
|
32
|
+
return cache[key] || null;
|
|
33
|
+
},
|
|
34
|
+
key: function (index) {
|
|
35
|
+
return keys[index] || null;
|
|
36
|
+
},
|
|
37
|
+
removeItem: function (key) {
|
|
38
|
+
var index = keys.indexOf(key);
|
|
39
|
+
if (index === -1)
|
|
40
|
+
return;
|
|
41
|
+
keys.splice(index, 1);
|
|
42
|
+
delete cache[key];
|
|
43
|
+
},
|
|
44
|
+
setItem: function (key, value) {
|
|
45
|
+
if (keys.indexOf(key) === -1)
|
|
46
|
+
keys.push(key);
|
|
47
|
+
cache[key] = value;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -8,10 +8,10 @@ var MILLIS_IN_A_DAY = 86400000;
|
|
|
8
8
|
*
|
|
9
9
|
* @returns `true` if cache should be cleared, `false` otherwise
|
|
10
10
|
*/
|
|
11
|
-
function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
|
|
11
|
+
function validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache) {
|
|
12
12
|
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan;
|
|
13
13
|
// Check expiration
|
|
14
|
-
var lastUpdatedTimestamp = parseInt(
|
|
14
|
+
var lastUpdatedTimestamp = parseInt(storage.getItem(keys.buildLastUpdatedKey()), 10);
|
|
15
15
|
if (!isNaNNumber(lastUpdatedTimestamp)) {
|
|
16
16
|
var cacheExpirationInDays = isFiniteNumber(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
|
|
17
17
|
var expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
|
|
@@ -22,11 +22,11 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
22
22
|
}
|
|
23
23
|
// Check hash
|
|
24
24
|
var storageHashKey = keys.buildHashKey();
|
|
25
|
-
var storageHash =
|
|
25
|
+
var storageHash = storage.getItem(storageHashKey);
|
|
26
26
|
var currentStorageHash = getStorageHash(settings);
|
|
27
27
|
if (storageHash !== currentStorageHash) {
|
|
28
28
|
try {
|
|
29
|
-
|
|
29
|
+
storage.setItem(storageHashKey, currentStorageHash);
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
32
|
log.error(LOG_PREFIX + e);
|
|
@@ -39,7 +39,7 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
39
39
|
}
|
|
40
40
|
// Clear on init
|
|
41
41
|
if (options.clearOnInit) {
|
|
42
|
-
var lastClearTimestamp = parseInt(
|
|
42
|
+
var lastClearTimestamp = parseInt(storage.getItem(keys.buildLastClear()), 10);
|
|
43
43
|
if (isNaNNumber(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
|
|
44
44
|
log.info(LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
|
|
45
45
|
return true;
|
|
@@ -54,23 +54,28 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
54
54
|
*
|
|
55
55
|
* @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
|
|
56
56
|
*/
|
|
57
|
-
export function validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
export function validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
58
|
+
return Promise.resolve(storage.load && storage.load()).then(function () {
|
|
59
|
+
var currentTimestamp = Date.now();
|
|
60
|
+
var isThereCache = splits.getChangeNumber() > -1;
|
|
61
|
+
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
62
|
+
splits.clear();
|
|
63
|
+
rbSegments.clear();
|
|
64
|
+
segments.clear();
|
|
65
|
+
largeSegments.clear();
|
|
66
|
+
// Update last clear timestamp
|
|
67
|
+
try {
|
|
68
|
+
storage.setItem(keys.buildLastClear(), currentTimestamp + '');
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
settings.log.error(LOG_PREFIX + e);
|
|
72
|
+
}
|
|
73
|
+
// Persist clear
|
|
74
|
+
if (storage.save)
|
|
75
|
+
storage.save();
|
|
76
|
+
return false;
|
|
71
77
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return isThereCache;
|
|
78
|
+
// Check if ready from cache
|
|
79
|
+
return isThereCache;
|
|
80
|
+
});
|
|
76
81
|
}
|
|
@@ -42,10 +42,9 @@ export function fromObjectUpdaterFactory(splitsParser, storage, readiness, setti
|
|
|
42
42
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
43
43
|
if (startingUp) {
|
|
44
44
|
startingUp = false;
|
|
45
|
-
|
|
46
|
-
Promise.resolve().then(function () {
|
|
45
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
|
|
47
46
|
// Emits SDK_READY_FROM_CACHE
|
|
48
|
-
if (
|
|
47
|
+
if (isCacheLoaded)
|
|
49
48
|
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
50
49
|
// Emits SDK_READY
|
|
51
50
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
@@ -31,6 +31,8 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
|
|
|
31
31
|
shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
|
|
32
32
|
shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
|
|
33
33
|
}
|
|
34
|
+
if (storage.save)
|
|
35
|
+
storage.save();
|
|
34
36
|
// Notify update if required
|
|
35
37
|
if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
36
38
|
readyOnAlreadyExistentState = false;
|
|
@@ -149,6 +149,8 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
149
149
|
segments.registerSegments(setToArray(usedSegments))
|
|
150
150
|
]).then(function (_a) {
|
|
151
151
|
var ffChanged = _a[0], rbsChanged = _a[1];
|
|
152
|
+
if (storage.save)
|
|
153
|
+
storage.save();
|
|
152
154
|
if (splitsEventEmitter) {
|
|
153
155
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
154
156
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
@@ -66,35 +66,39 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
66
66
|
*/
|
|
67
67
|
start: function () {
|
|
68
68
|
running = true;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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();
|
|
82
92
|
}
|
|
83
|
-
pushManager.start();
|
|
84
93
|
}
|
|
85
94
|
else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
else {
|
|
90
|
-
if (startFirstTime) {
|
|
91
|
-
pollingManager.syncAll();
|
|
95
|
+
if (startFirstTime) {
|
|
96
|
+
pollingManager.syncAll();
|
|
97
|
+
}
|
|
92
98
|
}
|
|
93
99
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
submitterManager.start(!isConsentGranted(settings));
|
|
97
|
-
startFirstTime = false;
|
|
100
|
+
startFirstTime = false;
|
|
101
|
+
});
|
|
98
102
|
},
|
|
99
103
|
/**
|
|
100
104
|
* Method used to stop/pause the syncManager.
|
|
@@ -10,3 +10,22 @@ export function isLocalStorageAvailable() {
|
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
export function isValidStorageWrapper(wrapper) {
|
|
14
|
+
return wrapper !== null &&
|
|
15
|
+
typeof wrapper === 'object' &&
|
|
16
|
+
typeof wrapper.setItem === 'function' &&
|
|
17
|
+
typeof wrapper.getItem === 'function' &&
|
|
18
|
+
typeof wrapper.removeItem === 'function';
|
|
19
|
+
}
|
|
20
|
+
export function isWebStorage(wrapper) {
|
|
21
|
+
if (typeof wrapper.length === 'number') {
|
|
22
|
+
try {
|
|
23
|
+
wrapper.key(0);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
@@ -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 function getRolloutPlan(log: ILogger, storage: IStorageSync, options: Spl
|
|
|
14
14
|
const { keys, exposeSegments } = options;
|
|
15
15
|
const { splits, segments, rbSegments } = storage;
|
|
16
16
|
|
|
17
|
-
log.debug(`storage: get feature flags${keys ? `, and memberships for keys ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);
|
|
17
|
+
log.debug(`storage: get feature flags${keys ? `, and memberships for keys: ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
20
|
splitChanges: {
|
|
@@ -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, len = this.storage.length; i < len; 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,22 +5,24 @@ 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 {
|
|
@@ -31,8 +33,8 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
31
33
|
|
|
32
34
|
private setChangeNumber(changeNumber: number) {
|
|
33
35
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
37
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
36
38
|
} catch (e) {
|
|
37
39
|
this.log.error(LOG_PREFIX + e);
|
|
38
40
|
}
|
|
@@ -40,20 +42,19 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
40
42
|
|
|
41
43
|
private updateSegmentCount(diff: number) {
|
|
42
44
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
43
|
-
const count = toNumber(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
else localStorage.removeItem(segmentsCountKey);
|
|
45
|
+
const count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
|
|
46
|
+
if (count > 0) this.storage.setItem(segmentsCountKey, count + '');
|
|
47
|
+
else this.storage.removeItem(segmentsCountKey);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
private add(rbSegment: IRBSegment): boolean {
|
|
50
51
|
try {
|
|
51
52
|
const name = rbSegment.name;
|
|
52
53
|
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
53
|
-
const
|
|
54
|
-
const previous =
|
|
54
|
+
const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
55
|
+
const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
57
58
|
|
|
58
59
|
let usesSegmentsDiff = 0;
|
|
59
60
|
if (previous && usesSegments(previous)) usesSegmentsDiff--;
|
|
@@ -72,7 +73,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
72
73
|
const rbSegment = this.get(name);
|
|
73
74
|
if (!rbSegment) return false;
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
76
77
|
|
|
77
78
|
if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
|
|
78
79
|
|
|
@@ -84,13 +85,13 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
private getNames(): string[] {
|
|
87
|
-
const len =
|
|
88
|
+
const len = this.storage.length;
|
|
88
89
|
const accum = [];
|
|
89
90
|
|
|
90
91
|
let cur = 0;
|
|
91
92
|
|
|
92
93
|
while (cur < len) {
|
|
93
|
-
const key =
|
|
94
|
+
const key = this.storage.key(cur);
|
|
94
95
|
|
|
95
96
|
if (key != null && this.keys.isRBSegmentKey(key)) accum.push(this.keys.extractKey(key));
|
|
96
97
|
|
|
@@ -101,7 +102,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
get(name: string): IRBSegment | null {
|
|
104
|
-
const item =
|
|
105
|
+
const item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
|
|
105
106
|
return item && JSON.parse(item);
|
|
106
107
|
}
|
|
107
108
|
|
|
@@ -117,7 +118,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
117
118
|
|
|
118
119
|
getChangeNumber(): number {
|
|
119
120
|
const n = -1;
|
|
120
|
-
let value: string | number | null =
|
|
121
|
+
let value: string | number | null = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
121
122
|
|
|
122
123
|
if (value !== null) {
|
|
123
124
|
value = parseInt(value, 10);
|
|
@@ -129,7 +130,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
usesSegments(): boolean {
|
|
132
|
-
const storedCount =
|
|
133
|
+
const storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
133
134
|
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
134
135
|
|
|
135
136
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|