Skip to content

Commit afdf656

Browse files
Add and use StorageAdapter::save method
1 parent 1c09903 commit afdf656

File tree

7 files changed

+40
-25
lines changed

7 files changed

+40
-25
lines changed

src/storages/inLocalStorage/MySegmentsCacheInLocal.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
44
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
55
import { LOG_PREFIX, DEFINED } from './constants';
66
import { StorageAdapter } from '../types';
7+
import { IMySegmentsResponse } from '../../dtos/types';
8+
import { MySegmentsData } from '../../sync/polling/types';
79

810
export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
911

@@ -19,6 +21,12 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
1921
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
2022
}
2123

24+
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
25+
const result = super.resetSegments(segmentsData);
26+
if (this.storage.save) this.storage.save();
27+
return result;
28+
}
29+
2230
protected addSegment(name: string): boolean {
2331
const segmentKey = this.keys.buildSegmentNameKey(name);
2432

src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
2727

2828
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
2929
this.setChangeNumber(changeNumber);
30-
const updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
31-
return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
30+
let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
31+
updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
32+
if (this.storage.save) this.storage.save();
33+
return updated;
3234
}
3335

3436
private setChangeNumber(changeNumber: number) {

src/storages/inLocalStorage/SplitsCacheInLocal.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
7979
this.hasSync = false;
8080
}
8181

82+
update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean {
83+
const result = super.update(toAdd, toRemove, changeNumber);
84+
if (this.storage.save) this.storage.save();
85+
return result;
86+
}
87+
8288
addSplit(split: ISplit) {
8389
try {
8490
const name = split.name;

src/storages/inLocalStorage/__tests__/storageAdapter.spec.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,22 @@ test.each([
4141
expect(storage.getItem('key1')).toBe(null);
4242
expect(storage.length).toBe(1);
4343

44-
// Until a till key is set/removed, changes should not be saved/persisted
44+
// Until `save` is called, changes should not be saved/persisted
4545
await storage.whenSaved();
4646
expect(wrapper.setItem).not.toHaveBeenCalled();
4747

48-
// When setting a till key, changes should be saved/persisted immediately
4948
storage.setItem('.till', '1');
5049
expect(storage.length).toBe(2);
5150
expect(storage.key(0)).toBe('key2');
5251
expect(storage.key(1)).toBe('.till');
5352

53+
// When `save` is called, changes should be saved/persisted immediately
54+
storage.save();
5455
await storage.whenSaved();
5556
expect(wrapper.setItem).toHaveBeenCalledWith('prefix', JSON.stringify({ key2: 'value2', '.till': '1' }));
5657

57-
// When removing a till key, changes should be saved/persisted immediately
58-
storage.removeItem('.till');
58+
expect(wrapper.setItem).toHaveBeenCalledTimes(1);
5959

6060
await storage.whenSaved();
61-
expect(wrapper.setItem).toHaveBeenCalledWith('prefix', JSON.stringify({ key2: 'value2' }));
62-
63-
await storage.whenSaved();
64-
expect(wrapper.setItem).toHaveBeenCalledTimes(2);
61+
expect(wrapper.setItem).toHaveBeenCalledTimes(1);
6562
});

src/storages/inLocalStorage/storageAdapter.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ import SplitIO from '../../../types/splitio';
33
import { LOG_PREFIX } from './constants';
44
import { StorageAdapter } from '../types';
55

6-
function isTillKey(key: string) {
7-
return key.endsWith('.till');
8-
}
96

107
export function storageAdapter(log: ILogger, prefix: string, wrapper: SplitIO.SyncStorageWrapper | SplitIO.AsyncStorageWrapper): Required<StorageAdapter> {
118
let keys: string[] = [];
@@ -14,14 +11,6 @@ export function storageAdapter(log: ILogger, prefix: string, wrapper: SplitIO.Sy
1411
let loadPromise: Promise<void> | undefined;
1512
let savePromise = Promise.resolve();
1613

17-
function _save() {
18-
return savePromise = savePromise.then(() => {
19-
return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache)));
20-
}).catch((e) => {
21-
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `setItem` method, with error: ' + e);
22-
});
23-
}
24-
2514
return {
2615
load() {
2716
return loadPromise || (loadPromise = Promise.resolve().then(() => {
@@ -33,32 +22,41 @@ export function storageAdapter(log: ILogger, prefix: string, wrapper: SplitIO.Sy
3322
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `getItem` method, with error: ' + e);
3423
}));
3524
},
25+
26+
save() {
27+
return savePromise = savePromise.then(() => {
28+
return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache)));
29+
}).catch((e) => {
30+
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `setItem` method, with error: ' + e);
31+
});
32+
},
33+
3634
whenSaved() {
3735
return savePromise;
3836
},
3937

4038
get length() {
4139
return keys.length;
4240
},
41+
4342
getItem(key: string) {
4443
return cache[key] || null;
4544
},
45+
4646
key(index: number) {
4747
return keys[index] || null;
4848
},
49+
4950
removeItem(key: string) {
5051
const index = keys.indexOf(key);
5152
if (index === -1) return;
5253
keys.splice(index, 1);
5354
delete cache[key];
54-
55-
if (isTillKey(key)) _save();
5655
},
56+
5757
setItem(key: string, value: string) {
5858
if (keys.indexOf(key) === -1) keys.push(key);
5959
cache[key] = value;
60-
61-
if (isTillKey(key)) _save();
6260
}
6361
};
6462
}

src/storages/inLocalStorage/validateCache.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export function validateCache(options: SplitIO.InLocalStorageOptions, storage: S
8787
settings.log.error(LOG_PREFIX + e);
8888
}
8989

90+
// Persist clear
91+
if (storage.save) storage.save();
92+
9093
return false;
9194
}
9295

src/storages/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ISettings } from '../types';
1111
export interface StorageAdapter {
1212
// Methods to support async storages
1313
load?: () => Promise<void>;
14+
save?: () => Promise<void>;
1415
whenSaved?: () => Promise<void>;
1516
// Methods based on https://developer.mozilla.org/en-US/docs/Web/API/Storage
1617
readonly length: number;

0 commit comments

Comments
 (0)