Skip to content
Merged
3 changes: 2 additions & 1 deletion src/storages/__tests__/RBSegmentsCacheSync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { IRBSegmentsCacheSync } from '../types';
import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks';

const cacheInMemory = new RBSegmentsCacheInMemory();
const cacheInLocal = new RBSegmentsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
// eslint-disable-next-line no-undef
const cacheInLocal = new RBSegmentsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'), localStorage);

describe.each([cacheInMemory, cacheInLocal])('Rule-based segments cache sync (Memory & LocalStorage)', (cache: IRBSegmentsCacheSync) => {

Expand Down
35 changes: 18 additions & 17 deletions src/storages/inLocalStorage/MySegmentsCacheInLocal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ import { isNaNNumber } from '../../utils/lang';
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
import { LOG_PREFIX, DEFINED } from './constants';
import { StorageAdapter } from '../types';

export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {

private readonly keys: MySegmentsKeyBuilder;
private readonly log: ILogger;
private readonly storage: StorageAdapter;

constructor(log: ILogger, keys: MySegmentsKeyBuilder) {
constructor(log: ILogger, keys: MySegmentsKeyBuilder, storage: StorageAdapter) {
super();
this.log = log;
this.keys = keys;
this.storage = storage;
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
}

protected addSegment(name: string): boolean {
const segmentKey = this.keys.buildSegmentNameKey(name);

try {
if (localStorage.getItem(segmentKey) === DEFINED) return false;
localStorage.setItem(segmentKey, DEFINED);
if (this.storage.getItem(segmentKey) === DEFINED) return false;
this.storage.setItem(segmentKey, DEFINED);
return true;
} catch (e) {
this.log.error(LOG_PREFIX + e);
Expand All @@ -33,8 +36,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
const segmentKey = this.keys.buildSegmentNameKey(name);

try {
if (localStorage.getItem(segmentKey) !== DEFINED) return false;
localStorage.removeItem(segmentKey);
if (this.storage.getItem(segmentKey) !== DEFINED) return false;
this.storage.removeItem(segmentKey);
return true;
} catch (e) {
this.log.error(LOG_PREFIX + e);
Expand All @@ -43,18 +46,16 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
}

isInSegment(name: string): boolean {
return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
}

getRegisteredSegments(): string[] {
// Scan current values from localStorage
return Object.keys(localStorage).reduce((accum, key) => {
let segmentName = this.keys.extractSegmentName(key);

if (segmentName) accum.push(segmentName);

return accum;
}, [] as string[]);
const registeredSegments: string[] = [];
for (let i = 0, len = this.storage.length; i < len; i++) {
const segmentName = this.keys.extractSegmentName(this.storage.key(i)!);
if (segmentName) registeredSegments.push(segmentName);
}
return registeredSegments;
}

getKeysCount() {
Expand All @@ -63,16 +64,16 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {

protected setChangeNumber(changeNumber?: number) {
try {
if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
else localStorage.removeItem(this.keys.buildTillKey());
if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
else this.storage.removeItem(this.keys.buildTillKey());
} catch (e) {
this.log.error(e);
}
}

getChangeNumber() {
const n = -1;
let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
let value: string | number | null = this.storage.getItem(this.keys.buildTillKey());

if (value !== null) {
value = parseInt(value, 10);
Expand Down
37 changes: 19 additions & 18 deletions src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ import { isFiniteNumber, isNaNNumber, toNumber } from '../../utils/lang';
import { setToArray } from '../../utils/lang/sets';
import { usesSegments } from '../AbstractSplitsCacheSync';
import { KeyBuilderCS } from '../KeyBuilderCS';
import { IRBSegmentsCacheSync } from '../types';
import { IRBSegmentsCacheSync, StorageAdapter } from '../types';
import { LOG_PREFIX } from './constants';

export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {

private readonly keys: KeyBuilderCS;
private readonly log: ILogger;
private readonly storage: StorageAdapter;

constructor(settings: ISettings, keys: KeyBuilderCS) {
constructor(settings: ISettings, keys: KeyBuilderCS, storage: StorageAdapter) {
this.keys = keys;
this.log = settings.log;
this.storage = storage;
}

clear() {
this.getNames().forEach(name => this.remove(name));
localStorage.removeItem(this.keys.buildRBSegmentsTillKey());
this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
}

update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
Expand All @@ -31,29 +33,28 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {

private setChangeNumber(changeNumber: number) {
try {
localStorage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
} catch (e) {
this.log.error(LOG_PREFIX + e);
}
}

private updateSegmentCount(diff: number) {
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
const count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
// @ts-expect-error
if (count > 0) localStorage.setItem(segmentsCountKey, count);
else localStorage.removeItem(segmentsCountKey);
const count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
if (count > 0) this.storage.setItem(segmentsCountKey, count + '');
else this.storage.removeItem(segmentsCountKey);
}

private add(rbSegment: IRBSegment): boolean {
try {
const name = rbSegment.name;
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
const rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
const previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;

localStorage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));

let usesSegmentsDiff = 0;
if (previous && usesSegments(previous)) usesSegmentsDiff--;
Expand All @@ -72,7 +73,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
const rbSegment = this.get(name);
if (!rbSegment) return false;

localStorage.removeItem(this.keys.buildRBSegmentKey(name));
this.storage.removeItem(this.keys.buildRBSegmentKey(name));

if (usesSegments(rbSegment)) this.updateSegmentCount(-1);

Expand All @@ -84,13 +85,13 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
}

private getNames(): string[] {
const len = localStorage.length;
const len = this.storage.length;
const accum = [];

let cur = 0;

while (cur < len) {
const key = localStorage.key(cur);
const key = this.storage.key(cur);

if (key != null && this.keys.isRBSegmentKey(key)) accum.push(this.keys.extractKey(key));

Expand All @@ -101,7 +102,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
}

get(name: string): IRBSegment | null {
const item = localStorage.getItem(this.keys.buildRBSegmentKey(name));
const item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
return item && JSON.parse(item);
}

Expand All @@ -113,7 +114,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {

getChangeNumber(): number {
const n = -1;
let value: string | number | null = localStorage.getItem(this.keys.buildRBSegmentsTillKey());
let value: string | number | null = this.storage.getItem(this.keys.buildRBSegmentsTillKey());

if (value !== null) {
value = parseInt(value, 10);
Expand All @@ -125,7 +126,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
}

usesSegments(): boolean {
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
const storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);

return isFiniteNumber(splitsWithSegmentsCount) ?
Expand Down
Loading