import { Injectable } from '@angular/core'; import { HooksService } from '@arrow/bom/hooks'; import { ConfigService } from '@arrow/bom/config'; const LOCAL_FLAGS_KEY = 'localFeatureFlags'; const MASTER_FLAGS_KEYS = 'MASTER_FEATURE_FLAGS'; const MYARROW_SCOPE_PREFIX = 'bomScope'; const BASE_FLAGS = [ // Available flags 'maintenance', 'mbomodal', 'bomsuserscore', 'showregionsinmyarrow' ]; @Injectable() /** * @deprecated Will be removed from version 2.0 * Features should be read from the store in MyArrow */ export class FeatureFlagService { private _localFlags: Map = new Map(); private _masterFlags: Map = new Map(); private _target: string; public maintenance: boolean; public mbomodal: boolean; public bomsuserscore: boolean; public showregionsinmyarrow: boolean; /** * When true will log analytics submissions to the console */ constructor( private _hooks: HooksService, private _configService: ConfigService ) { this._target = this._configService.getTarget(); this._resetFlags(); } private _resetFlags() { let masterFlags: string[] = []; if (this._target === 'myarrow') { this._hooks .getFeatureFlags() .pipe() .subscribe(flags => { masterFlags = this._importMyArrowBomScopedFlags(flags); this._processFlags(masterFlags); }); } else { masterFlags = window[MASTER_FLAGS_KEYS]; if (!masterFlags || !masterFlags.length || masterFlags.length === 0) { return console.warn("Master feature flags don't exist"); } this._processFlags(masterFlags); } } private _processFlags(masterFlags: string[]): void { BASE_FLAGS.forEach((flag: string) => this._masterFlags.set(flag, false)); masterFlags.forEach((flag: string) => this._masterFlags.set(flag, true)); this._loadLocalFlags(); this._setFlags(); } private _loadLocalFlags(): void { const _flags = localStorage.getItem(LOCAL_FLAGS_KEY); let flags: { name: string; status: boolean }[]; try { flags = JSON.parse(_flags) || []; } catch (error) { console.error(error); flags = []; } finally { flags.forEach((flag: { name: string; status: boolean }) => this._localFlags.set(flag.name, flag.status) ); } } private _setFlags(): void { this._masterFlags.forEach( (masterFlagStatus: boolean, masterFlagName: string) => { const localFlag: boolean = this._localFlags.get(masterFlagName); this[masterFlagName] = typeof localFlag === 'boolean' ? localFlag : masterFlagStatus; } ); } private _saveCurrentState(): void { const flags = Array.from(this._localFlags).map((flag: any[]) => { return { name: flag[0], status: flag[1] }; }); localStorage.setItem(LOCAL_FLAGS_KEY, JSON.stringify(flags)); } private _importMyArrowBomScopedFlags(myArrowFlags: any): string[] { const masterFlags = []; for (const key in myArrowFlags) { if (key.startsWith(MYARROW_SCOPE_PREFIX) && myArrowFlags[key] === true) { masterFlags.push(key.slice(MYARROW_SCOPE_PREFIX.length).toLowerCase()); } } return masterFlags; } /** * set local flags * @param name * @param status */ public setLocalFlag(name: string, status: boolean = true) { this._localFlags.set(name, status); this._saveCurrentState(); this._resetFlags(); } /** * remove local flags * @param name */ public removeLocalFlag(name: string) { this._localFlags.delete(name); this._saveCurrentState(); this._resetFlags(); } /** * get local flags * @param name */ public getLocalFlag(name: string) { return this._localFlags.get(name); } /** * get master flags * @param name */ public getMasterFlag(name: string) { return this._masterFlags.get(name); } }