Skip to content

Commit

Permalink
fix flagsets typo in storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Zamora committed Sep 21, 2023
1 parent 1c8dff6 commit f906cb9
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 66 deletions.
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
6 changes: 6 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, _Set } from '../utils/lang/sets';

/**
* This class provides a skeletal implementation of the ISplitsCacheSync interface
Expand Down Expand Up @@ -77,6 +78,11 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
}
return false;
}
/** NO-OP */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getNamesByFlagSets(flagsets: string[]): ISet<string> {
return new _Set([]);
}

}

Expand Down
2 changes: 1 addition & 1 deletion src/storages/KeyBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class KeyBuilder {
return `${this.prefix}.trafficType.${trafficType}`;
}

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

Expand Down
2 changes: 1 addition & 1 deletion src/storages/__tests__/KeyBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ test('KEYS / flagset keys', () => {
const flagsetName = 'flagset_x';
const expectedKey = `${prefix}.flagset.${flagsetName}`;

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

});

Expand Down
22 changes: 11 additions & 11 deletions src/storages/inLocalStorage/SplitsCacheInLocal.ts
Original file line number Diff line number Diff line change
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,10 +257,10 @@ 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);
const flagsetKey = this.keys.buildFlagSetKey(flagset);
let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);

if (flagsetFromLocalStorage) {
Expand All @@ -272,14 +272,14 @@ export class SplitsCacheInLocal 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;

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

let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);
if (!flagsetFromLocalStorage) flagsetFromLocalStorage = '[]';
Expand All @@ -291,7 +291,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
});
}

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

flagsets.forEach(flagset => {
Expand All @@ -300,7 +300,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
}

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

let flagsetFromLocalStorage = localStorage.getItem(flagsetKey);

Expand Down
40 changes: 20 additions & 20 deletions src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,32 +174,32 @@ 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.
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']));
});
20 changes: 10 additions & 10 deletions src/storages/inMemory/SplitsCacheInMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,7 +105,7 @@ 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];
Expand All @@ -117,19 +117,19 @@ 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) {
private removeFromFlagSets(featureFlagName :string, flagsets: string[] | undefined) {
if (!flagsets) return;
flagsets.forEach(flagset => {
this.removeNames(flagset, featureFlagName);
Expand Down
40 changes: 20 additions & 20 deletions src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,32 +127,32 @@ test('SPLITS CACHE / In Memory / 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.
Expand All @@ -167,10 +167,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']));
});
12 changes: 12 additions & 0 deletions src/storages/inRedis/SplitsCacheInRedis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ILogger } from '../../logger/types';
import { LOG_PREFIX } from './constants';
import { ISplit } from '../../dtos/types';
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
import { ISet, _Set } from '../../utils/lang/sets';

/**
* Discard errors for an answer of multiple operations.
Expand Down Expand Up @@ -188,6 +189,17 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
);
}

/**
* Get list of split names related to a given flagset names list.
* The returned promise is resolved with the list of split names,
* or rejected if wrapper operation fails.
* @todo this is a no-op method to be implemented
*/
getNamesByFlagSets(): Promise<ISet<string>> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return new Promise(flagSets => new _Set([]));
}

/**
* Check traffic type existence.
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
Expand Down
12 changes: 12 additions & 0 deletions src/storages/pluggable/SplitsCachePluggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ILogger } from '../../logger/types';
import { ISplit } from '../../dtos/types';
import { LOG_PREFIX } from './constants';
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
import { ISet, _Set } from '../../utils/lang/sets';

/**
* ISplitsCacheAsync implementation for pluggable storages.
Expand Down Expand Up @@ -154,6 +155,17 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
);
}

/**
* Get list of split names related to a given flagset names list.
* The returned promise is resolved with the list of split names,
* or rejected if wrapper operation fails.
* @todo this is a no-op method to be implemented
*/
getNamesByFlagSets(): Promise<ISet<string>> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return new Promise(flagSets => new _Set([]));
}

/**
* Check traffic type existence.
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
Expand Down
Loading

0 comments on commit f906cb9

Please sign in to comment.