import { randomUUID } from 'crypto'; import { FeatureToggle, FeatureToggleWithEnvironment, IFeatureEnvironment, IFeatureStrategy, IFeatureToggleClient, IFeatureToggleQuery, } from '../../lib/types/model'; import NotFoundError from '../../lib/error/notfound-error'; import { IFeatureStrategiesStore } from '../../lib/types/stores/feature-strategies-store'; interface ProjectEnvironment { projectName: string; environment: string; } export default class FakeFeatureStrategiesStore implements IFeatureStrategiesStore { environmentAndFeature: Map = new Map(); projectToEnvironment: ProjectEnvironment[] = []; featureStrategies: IFeatureStrategy[] = []; featureToggles: FeatureToggle[] = []; async createStrategyConfig( strategyConfig: Omit, ): Promise { const newStrat = { ...strategyConfig, id: randomUUID() }; this.featureStrategies.push(newStrat); return Promise.resolve(newStrat); } async getStrategiesForToggle( featureName: string, ): Promise { return this.featureStrategies.filter( (fS) => fS.featureName === featureName, ); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async createFeature(feature: any): Promise { this.featureToggles.push({ project: feature.project || 'default', createdAt: new Date(), archived: false, ...feature, }); return Promise.resolve(); } async getAllFeatureStrategies(): Promise { return this.featureStrategies; } async deleteFeatureStrategies(): Promise { this.featureStrategies = []; return Promise.resolve(); } async getStrategiesForEnvironment( environment: string, ): Promise { const stratEnvs = this.featureStrategies.filter( (fS) => fS.environment === environment, ); return Promise.resolve(stratEnvs); } async hasStrategy(id: string): Promise { return this.featureStrategies.some((s) => s.id === id); } async get(id: string): Promise { return this.featureStrategies.find((s) => s.id === id); } async exists(key: string): Promise { return this.featureStrategies.some((s) => s.id === key); } async delete(key: string): Promise { this.featureStrategies.splice( this.featureStrategies.findIndex((s) => s.id === key), 1, ); } async deleteAll(): Promise { this.featureStrategies = []; } destroy(): void { throw new Error('Method not implemented.'); } async removeAllStrategiesForEnv( feature_name: string, environment: string, ): Promise { const toRemove = this.featureStrategies.filter( (fS) => fS.featureName === feature_name && fS.environment === environment, ); this.featureStrategies = this.featureStrategies.filter( (f) => !toRemove.some( (r) => r.featureName === f.featureName && r.environment === f.environment, ), ); return Promise.resolve(); } async getAll(): Promise { return Promise.resolve(this.featureStrategies); } async getStrategiesForFeature( project_name: string, feature_name: string, environment: string, ): Promise { const rows = this.featureStrategies.filter( (fS) => fS.projectName === project_name && fS.featureName === feature_name && fS.environment === environment, ); return Promise.resolve(rows); } async getStrategiesForEnv( environment: string, ): Promise { return this.featureStrategies.filter( (fS) => fS.environment === environment, ); } async getFeatureToggleAdmin( featureName: string, archived: boolean = false, ): Promise { const toggle = this.featureToggles.find( (f) => f.name === featureName && f.archived === archived, ); if (toggle) { return { ...toggle, environments: [] }; } throw new NotFoundError( `Could not find feature with name ${featureName}`, ); } async getFeatures( featureQuery?: IFeatureToggleQuery, archived: boolean = false, ): Promise { const rows = this.featureToggles.filter((toggle) => { if (featureQuery.namePrefix) { if (featureQuery.project) { return ( toggle.name.startsWith(featureQuery.namePrefix) && featureQuery.project.includes(toggle.project) ); } return toggle.name.startsWith(featureQuery.namePrefix); } if (featureQuery.project) { return featureQuery.project.includes(toggle.project); } return toggle.archived === archived; }); const clientRows: IFeatureToggleClient[] = rows.map((t) => ({ ...t, enabled: true, strategies: [], description: t.description || '', type: t.type || 'Release', stale: t.stale || false, variants: [], })); return Promise.resolve(clientRows); } async getStrategyById(id: string): Promise { const strat = this.featureStrategies.find((fS) => fS.id === id); if (strat) { return Promise.resolve(strat); } return Promise.reject( new NotFoundError(`Could not find strategy with id ${id}`), ); } async connectEnvironmentAndFeature( feature_name: string, environment: string, enabled: boolean = false, ): Promise { if (!this.environmentAndFeature.has(environment)) { this.environmentAndFeature.set(environment, []); } this.environmentAndFeature .get(environment) .push({ feature: feature_name, enabled }); return Promise.resolve(); } async enableEnvironmentForFeature( feature_name: string, environment: string, ): Promise { if (!this.environmentAndFeature.has(environment)) { this.environmentAndFeature.set(environment, [ { featureName: feature_name, enabled: true, }, ]); } const features = this.environmentAndFeature .get(environment) .map((f) => { if (f.featureName === feature_name) { // eslint-disable-next-line no-param-reassign f.enabled = true; } return f; }); this.environmentAndFeature.set(environment, features); return Promise.resolve(); } async removeEnvironmentForFeature( feature_name: string, environment: string, ): Promise { this.environmentAndFeature.set( environment, this.environmentAndFeature .get(environment) .filter((e) => e.featureName !== feature_name), ); return Promise.resolve(); } async disconnectEnvironmentFromProject( environment: string, project: string, ): Promise { this.projectToEnvironment = this.projectToEnvironment.filter( (f) => f.projectName !== project && f.environment !== environment, ); return Promise.resolve(); } async updateStrategy( id: string, updates: Partial, ): Promise { this.featureStrategies = this.featureStrategies.map((f) => { if (f.id === id) { return { ...f, ...updates }; } return f; }); return Promise.resolve(this.featureStrategies.find((f) => f.id === id)); } async getStrategiesAndMetadataForEnvironment( // eslint-disable-next-line @typescript-eslint/no-unused-vars environment: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars featureName: string, ): Promise { return Promise.resolve(); } async deleteConfigurationsForProjectAndEnvironment( // eslint-disable-next-line @typescript-eslint/no-unused-vars projectId: String, // eslint-disable-next-line @typescript-eslint/no-unused-vars environment: String, ): Promise { return Promise.resolve(); } async isEnvironmentEnabled( featureName: string, environment: string, ): Promise { const enabled = this.environmentAndFeature .get(environment) ?.find((f) => f.featureName === featureName)?.enabled || false; return Promise.resolve(enabled); } async toggleEnvironmentEnabledStatus( environment: string, featureName: string, enabled: boolean, ): Promise { return Promise.resolve(enabled); } async getAllFeatureEnvironments(): Promise { return Promise.resolve([]); } } module.exports = FakeFeatureStrategiesStore;