Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flagsets typo in storage #249

Merged
merged 3 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/storages/AbstractSplitsCacheAsync.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ISplitsCacheAsync } from './types';
import { ISplit } from '../dtos/types';
import { objectAssign } from '../utils/lang/objectAssign';
import { ISet } from '../utils/lang/sets';

/**
* This class provides a skeletal implementation of the ISplitsCacheAsync interface
Expand All @@ -17,6 +18,7 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
abstract getChangeNumber(): Promise<number>
abstract getAll(): Promise<ISplit[]>
abstract getSplitNames(): Promise<string[]>
abstract getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>>
abstract trafficTypeExists(trafficType: string): Promise<boolean>
abstract clear(): Promise<boolean | void>

Expand Down
3 changes: 3 additions & 0 deletions src/storages/AbstractSplitsCacheSync.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ISplitsCacheSync } from './types';
import { ISplit } from '../dtos/types';
import { objectAssign } from '../utils/lang/objectAssign';
import { ISet } from '../utils/lang/sets';

/**
* This class provides a skeletal implementation of the ISplitsCacheSync interface
Expand Down Expand Up @@ -78,6 +79,8 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
return false;
}

abstract getNamesByFlagSets(flagSets: string[]): ISet<string>

}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/storages/KeyBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class KeyBuilder {
return `${this.prefix}.trafficType.${trafficType}`;
}

buildFlagsetKey(flagset: string) {
return `${this.prefix}.flagset.${flagset}`;
buildFlagSetKey(flagSet: string) {
return `${this.prefix}.flagset.${flagSet}`;
}

buildSplitKey(splitName: string) {
Expand Down
8 changes: 4 additions & 4 deletions src/storages/__tests__/KeyBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ test('KEYS / traffic type keys', () => {

});

test('KEYS / flagset keys', () => {
test('KEYS / flag set keys', () => {
const prefix = 'unit_test.SPLITIO';
const builder = new KeyBuilder(prefix);

const flagsetName = 'flagset_x';
const expectedKey = `${prefix}.flagset.${flagsetName}`;
const flagSetName = 'flagset_x';
const expectedKey = `${prefix}.flagset.${flagSetName}`;

expect(builder.buildFlagsetKey(flagsetName)).toBe(expectedKey);
expect(builder.buildFlagSetKey(flagSetName)).toBe(expectedKey);

});

Expand Down
2 changes: 1 addition & 1 deletion src/storages/__tests__/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const something: ISplit = { name: 'something' };
//@ts-ignore
export const somethingElse: ISplit = { name: 'something else' };

// - With flagsets
// - With flag sets

//@ts-ignore
export const featureFlagWithEmptyFS: ISplit = { name: 'ff_empty', sets: [] };
Expand Down
68 changes: 34 additions & 34 deletions src/storages/inLocalStorage/SplitsCacheInLocal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {

private readonly keys: KeyBuilderCS;
private readonly splitFiltersValidation: ISplitFiltersValidation;
private readonly flagsetsFilter: string[];
private readonly flagSetsFilter: string[];
private hasSync?: boolean;
private updateNewFilter?: boolean;

Expand All @@ -26,7 +26,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
super();
this.keys = keys;
this.splitFiltersValidation = splitFiltersValidation;
this.flagsetsFilter = this.splitFiltersValidation.groupedFilters.bySet;
this.flagSetsFilter = this.splitFiltersValidation.groupedFilters.bySet;

this._checkExpiration(expirationTimestamp);

Expand Down Expand Up @@ -108,8 +108,8 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
this._incrementCounts(split);
this._decrementCounts(previousSplit);

if (previousSplit) this.removeFromFlagsets(previousSplit.name, previousSplit.sets);
this.addToFlagsets(split);
if (previousSplit) this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
this.addToFlagSets(split);

return true;
} catch (e) {
Expand All @@ -124,7 +124,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
localStorage.removeItem(this.keys.buildSplitKey(name));

this._decrementCounts(split);
if (split) this.removeFromFlagsets(split.name, split.sets);
if (split) this.removeFromFlagSets(split.name, split.sets);

return true;
} catch (e) {
Expand Down Expand Up @@ -257,64 +257,64 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
// if the filter didn't change, nothing is done
}

getNamesByFlagsets(flagsets: string[]): ISet<string>{
getNamesByFlagSets(flagSets: string[]): ISet<string>{
let toReturn: ISet<string> = new _Set([]);
flagsets.forEach(flagset => {
const flagsetKey = this.keys.buildFlagsetKey(flagset);
let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);
flagSets.forEach(flagSet => {
const flagSetKey = this.keys.buildFlagSetKey(flagSet);
let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);

if (flagsetFromLocalStorage) {
const flagsetCache = new _Set(JSON.parse(flagsetFromLocalStorage));
toReturn = returnSetsUnion(toReturn, flagsetCache);
if (flagSetFromLocalStorage) {
const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
toReturn = returnSetsUnion(toReturn, flagSetCache);
}
});
return toReturn;

}

private addToFlagsets(featureFlag: ISplit) {
private addToFlagSets(featureFlag: ISplit) {
if (!featureFlag.sets) return;

featureFlag.sets.forEach(featureFlagset => {
featureFlag.sets.forEach(featureFlagSet => {

if (this.flagsetsFilter.length > 0 && !this.flagsetsFilter.some(filterFlagset => filterFlagset === featureFlagset)) return;
if (this.flagSetsFilter.length > 0 && !this.flagSetsFilter.some(filterFlagSet => filterFlagSet === featureFlagSet)) return;

const flagsetKey = this.keys.buildFlagsetKey(featureFlagset);
const flagSetKey = this.keys.buildFlagSetKey(featureFlagSet);

let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);
if (!flagsetFromLocalStorage) flagsetFromLocalStorage = '[]';
let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
if (!flagSetFromLocalStorage) flagSetFromLocalStorage = '[]';

const flagsetCache = new _Set(JSON.parse(flagsetFromLocalStorage));
flagsetCache.add(featureFlag.name);
const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
flagSetCache.add(featureFlag.name);

localStorage.setItem(flagsetKey, JSON.stringify(setToArray(flagsetCache)));
localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
});
}

private removeFromFlagsets(featureFlagName: string, flagsets?: string[]) {
if (!flagsets) return;
private removeFromFlagSets(featureFlagName: string, flagSets?: string[]) {
if (!flagSets) return;

flagsets.forEach(flagset => {
this.removeNames(flagset, featureFlagName);
flagSets.forEach(flagSet => {
this.removeNames(flagSet, featureFlagName);
});
}

private removeNames(flagsetName: string, featureFlagName: string) {
const flagsetKey = this.keys.buildFlagsetKey(flagsetName);
private removeNames(flagSetName: string, featureFlagName: string) {
const flagSetKey = this.keys.buildFlagSetKey(flagSetName);

let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);
let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);

if (!flagsetFromLocalStorage) return;
if (!flagSetFromLocalStorage) return;

const flagsetCache = new _Set(JSON.parse(flagsetFromLocalStorage));
flagsetCache.delete(featureFlagName);
const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
flagSetCache.delete(featureFlagName);

if (flagsetCache.size === 0) {
localStorage.removeItem(flagsetKey);
if (flagSetCache.size === 0) {
localStorage.removeItem(flagSetKey);
return;
}

localStorage.setItem(flagsetKey, JSON.stringify(setToArray(flagsetCache)));
localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
}

}
44 changes: 22 additions & 22 deletions src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ test('SPLIT CACHE / LocalStorage / usesSegments', () => {
expect(cache.usesSegments()).toBe(false); // 0 splits using segments
});

test('SPLIT CACHE / LocalStorage / flagset cache tests', () => {
test('SPLIT CACHE / LocalStorage / flag set cache tests', () => {
// @ts-ignore
const cache = new SplitsCacheInLocal(loggerMock, new KeyBuilderCS('SPLITIO', 'user'), undefined, { groupedFilters: { bySet: ['o', 'n', 'e', 'x'] } });
const emptySet = new _Set([]);
Expand All @@ -174,36 +174,36 @@ test('SPLIT CACHE / LocalStorage / flagset cache tests', () => {
]);
cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS);

expect(cache.getNamesByFlagsets(['o'])).toEqual(new _Set(['ff_one', 'ff_two']));
expect(cache.getNamesByFlagsets(['n'])).toEqual(new _Set(['ff_one']));
expect(cache.getNamesByFlagsets(['e'])).toEqual(new _Set(['ff_one','ff_three']));
expect(cache.getNamesByFlagsets(['t'])).toEqual(emptySet); // 't' not in filter
expect(cache.getNamesByFlagsets(['o','n','e'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));
expect(cache.getNamesByFlagSets(['o'])).toEqual(new _Set(['ff_one', 'ff_two']));
expect(cache.getNamesByFlagSets(['n'])).toEqual(new _Set(['ff_one']));
expect(cache.getNamesByFlagSets(['e'])).toEqual(new _Set(['ff_one','ff_three']));
expect(cache.getNamesByFlagSets(['t'])).toEqual(emptySet); // 't' not in filter
expect(cache.getNamesByFlagSets(['o','n','e'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));

cache.addSplit(featureFlagOne.name, {...featureFlagOne, sets: ['1']});

expect(cache.getNamesByFlagsets(['1'])).toEqual(emptySet); // '1' not in filter
expect(cache.getNamesByFlagsets(['o'])).toEqual(new _Set(['ff_two']));
expect(cache.getNamesByFlagsets(['n'])).toEqual(emptySet);
expect(cache.getNamesByFlagSets(['1'])).toEqual(emptySet); // '1' not in filter
expect(cache.getNamesByFlagSets(['o'])).toEqual(new _Set(['ff_two']));
expect(cache.getNamesByFlagSets(['n'])).toEqual(emptySet);

cache.addSplit(featureFlagOne.name, {...featureFlagOne, sets: ['x']});
expect(cache.getNamesByFlagsets(['x'])).toEqual(new _Set(['ff_one']));
expect(cache.getNamesByFlagsets(['o','e','x'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));
expect(cache.getNamesByFlagSets(['x'])).toEqual(new _Set(['ff_one']));
expect(cache.getNamesByFlagSets(['o','e','x'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));


cache.removeSplit(featureFlagOne.name);
expect(cache.getNamesByFlagsets(['x'])).toEqual(emptySet);
expect(cache.getNamesByFlagSets(['x'])).toEqual(emptySet);

cache.removeSplit(featureFlagOne.name);
expect(cache.getNamesByFlagsets(['y'])).toEqual(emptySet); // 'y' not in filter
expect(cache.getNamesByFlagsets([])).toEqual(emptySet);
expect(cache.getNamesByFlagSets(['y'])).toEqual(emptySet); // 'y' not in filter
expect(cache.getNamesByFlagSets([])).toEqual(emptySet);

cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithoutFS);
expect(cache.getNamesByFlagsets([])).toEqual(emptySet);
expect(cache.getNamesByFlagSets([])).toEqual(emptySet);
});

// if FlagSets are not defined, it should store all FlagSets in memory.
test('SPLIT CACHE / LocalStorage / flagset cache tests without filters', () => {
test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => {
const cacheWithoutFilters = new SplitsCacheInLocal(loggerMock, new KeyBuilderCS('SPLITIO', 'user'));
const emptySet = new _Set([]);

Expand All @@ -214,10 +214,10 @@ test('SPLIT CACHE / LocalStorage / flagset cache tests without filters', () => {
]);
cacheWithoutFilters.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS);

expect(cacheWithoutFilters.getNamesByFlagsets(['o'])).toEqual(new _Set(['ff_one', 'ff_two']));
expect(cacheWithoutFilters.getNamesByFlagsets(['n'])).toEqual(new _Set(['ff_one']));
expect(cacheWithoutFilters.getNamesByFlagsets(['e'])).toEqual(new _Set(['ff_one','ff_three']));
expect(cacheWithoutFilters.getNamesByFlagsets(['t'])).toEqual(new _Set(['ff_two','ff_three']));
expect(cacheWithoutFilters.getNamesByFlagsets(['y'])).toEqual(emptySet);
expect(cacheWithoutFilters.getNamesByFlagsets(['o','n','e'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));
expect(cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual(new _Set(['ff_one', 'ff_two']));
expect(cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual(new _Set(['ff_one']));
expect(cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual(new _Set(['ff_one','ff_three']));
expect(cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual(new _Set(['ff_two','ff_three']));
expect(cacheWithoutFilters.getNamesByFlagSets(['y'])).toEqual(emptySet);
expect(cacheWithoutFilters.getNamesByFlagSets(['o','n','e'])).toEqual(new _Set(['ff_one','ff_two','ff_three']));
});
44 changes: 22 additions & 22 deletions src/storages/inMemory/SplitsCacheInMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import { ISet, _Set, returnSetsUnion } from '../../utils/lang/sets';
*/
export class SplitsCacheInMemory extends AbstractSplitsCacheSync {

private flagsetsFilter: string[];
private flagSetsFilter: string[];
private splitsCache: Record<string, ISplit> = {};
private ttCache: Record<string, number> = {};
private changeNumber: number = -1;
private splitsWithSegmentsCount: number = 0;
private flagsetsCache: Record<string, ISet<string>> = {};
private flagSetsCache: Record<string, ISet<string>> = {};

constructor(splitFiltersValidation: ISplitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }) {
super();
this.flagsetsFilter = splitFiltersValidation.groupedFilters.bySet;
this.flagSetsFilter = splitFiltersValidation.groupedFilters.bySet;
}

clear() {
Expand All @@ -36,7 +36,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
this.ttCache[previousTtName]--;
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];

this.removeFromFlagsets(previousSplit.name, previousSplit.sets);
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);

if (usesSegments(previousSplit)) { // Substract from segments count for the previous version of this Split.
this.splitsWithSegmentsCount--;
Expand All @@ -49,7 +49,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
// Update TT cache
const ttName = split.trafficTypeName;
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
this.addToFlagsets(split);
this.addToFlagSets(split);

// Add to segments count for the new version of the Split
if (usesSegments(split)) this.splitsWithSegmentsCount++;
Expand All @@ -69,7 +69,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
const ttName = split.trafficTypeName;
this.ttCache[ttName]--; // Update tt cache
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
this.removeFromFlagsets(split.name, split.sets);
this.removeFromFlagSets(split.name, split.sets);

// Update the segments count.
if (usesSegments(split)) this.splitsWithSegmentsCount--;
Expand Down Expand Up @@ -105,10 +105,10 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
return this.getChangeNumber() === -1 || this.splitsWithSegmentsCount > 0;
}

getNamesByFlagsets(flagsets: string[]): ISet<string>{
getNamesByFlagSets(flagSets: string[]): ISet<string>{
let toReturn: ISet<string> = new _Set([]);
flagsets.forEach(flagset => {
const featureFlagNames = this.flagsetsCache[flagset];
flagSets.forEach(flagSet => {
const featureFlagNames = this.flagSetsCache[flagSet];
if (featureFlagNames) {
toReturn = returnSetsUnion(toReturn, featureFlagNames);
}
Expand All @@ -117,29 +117,29 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {

}

private addToFlagsets(featureFlag: ISplit) {
private addToFlagSets(featureFlag: ISplit) {
if (!featureFlag.sets) return;
featureFlag.sets.forEach(featureFlagset => {
featureFlag.sets.forEach(featureFlagSet => {

if (this.flagsetsFilter.length > 0 && !this.flagsetsFilter.some(filterFlagset => filterFlagset === featureFlagset)) return;
if (this.flagSetsFilter.length > 0 && !this.flagSetsFilter.some(filterFlagSet => filterFlagSet === featureFlagSet)) return;

if (!this.flagsetsCache[featureFlagset]) this.flagsetsCache[featureFlagset] = new _Set([]);
if (!this.flagSetsCache[featureFlagSet]) this.flagSetsCache[featureFlagSet] = new _Set([]);

this.flagsetsCache[featureFlagset].add(featureFlag.name);
this.flagSetsCache[featureFlagSet].add(featureFlag.name);
});
}

private removeFromFlagsets(featureFlagName :string, flagsets: string[] | undefined) {
if (!flagsets) return;
flagsets.forEach(flagset => {
this.removeNames(flagset, featureFlagName);
private removeFromFlagSets(featureFlagName :string, flagSets: string[] | undefined) {
if (!flagSets) return;
flagSets.forEach(flagSet => {
this.removeNames(flagSet, featureFlagName);
});
}

private removeNames(flagsetName: string, featureFlagName: string) {
if (!this.flagsetsCache[flagsetName]) return;
this.flagsetsCache[flagsetName].delete(featureFlagName);
if (this.flagsetsCache[flagsetName].size === 0) delete this.flagsetsCache[flagsetName];
private removeNames(flagSetName: string, featureFlagName: string) {
if (!this.flagSetsCache[flagSetName]) return;
this.flagSetsCache[flagSetName].delete(featureFlagName);
if (this.flagSetsCache[flagSetName].size === 0) delete this.flagSetsCache[flagSetName];
}

}
2 changes: 1 addition & 1 deletion src/storages/inMemory/TelemetryCacheInMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
this.e = false;
}

private streamingEvents: StreamingEvent[] = []
private streamingEvents: StreamingEvent[] = [];

popStreamingEvents() {
return this.streamingEvents.splice(0);
Expand Down
Loading