@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
131 lines (100 loc) • 4.03 kB
text/typescript
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
import { isFiniteNumber } from '../../utils/lang';
/**
* Default ISplitsCacheSync implementation that stores split definitions in memory.
*/
export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
private flagSetsFilter: string[];
private splitsCache: Record<string, ISplit> = {};
private ttCache: Record<string, number> = {};
private changeNumber: number = -1;
private segmentsCount: number = 0;
private flagSetsCache: Record<string, Set<string>> = {};
constructor(splitFiltersValidation?: ISplitFiltersValidation) {
super();
this.flagSetsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
}
clear() {
this.splitsCache = {};
this.ttCache = {};
this.changeNumber = -1;
this.segmentsCount = 0;
this.flagSetsCache = {};
}
addSplit(split: ISplit): boolean {
const name = split.name;
const previousSplit = this.getSplit(name);
if (previousSplit) { // We had this Split already
const previousTtName = previousSplit.trafficTypeName;
this.ttCache[previousTtName]--;
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
// Subtract from segments count for the previous version of this Split
if (usesSegments(previousSplit)) this.segmentsCount--;
}
// Store the Split.
this.splitsCache[name] = split;
// Update TT cache
const ttName = split.trafficTypeName;
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
this.addToFlagSets(split);
// Add to segments count for the new version of the Split
if (usesSegments(split)) this.segmentsCount++;
return true;
}
removeSplit(name: string): boolean {
const split = this.getSplit(name);
if (!split) return false;
// Delete the Split
delete this.splitsCache[name];
const ttName = split.trafficTypeName;
this.ttCache[ttName]--; // Update tt cache
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
this.removeFromFlagSets(split.name, split.sets);
// Update the segments count.
if (usesSegments(split)) this.segmentsCount--;
return true;
}
getSplit(name: string): ISplit | null {
return this.splitsCache[name] || null;
}
setChangeNumber(changeNumber: number): boolean {
this.changeNumber = changeNumber;
return true;
}
getChangeNumber(): number {
return this.changeNumber;
}
getSplitNames(): string[] {
return Object.keys(this.splitsCache);
}
trafficTypeExists(trafficType: string): boolean {
return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
}
usesSegments(): boolean {
return this.getChangeNumber() === -1 || this.segmentsCount > 0;
}
getNamesByFlagSets(flagSets: string[]): Set<string>[] {
return flagSets.map(flagSet => this.flagSetsCache[flagSet] || new Set());
}
private addToFlagSets(featureFlag: ISplit) {
if (!featureFlag.sets) return;
featureFlag.sets.forEach(featureFlagSet => {
if (this.flagSetsFilter.length > 0 && !this.flagSetsFilter.some(filterFlagSet => filterFlagSet === featureFlagSet)) return;
if (!this.flagSetsCache[featureFlagSet]) this.flagSetsCache[featureFlagSet] = new Set([]);
this.flagSetsCache[featureFlagSet].add(featureFlag.name);
});
}
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];
}
}