import { FeatureConfig, RequestContext, ParameterInfo } from "@ogury/feature-config-sdk"; import { ChoiceRequest } from "../models"; import { config, isTest } from "../utils/config"; import logger from "../utils/logger"; const LOGGING_INTERVAL = 60000; export class FeatureConfigService { private featureConfig: FeatureConfig | null = null; private static instance: FeatureConfigService; /** * Initialise featureConfig object */ /* nyc ignore next */ private constructor() { if (!isTest()) { this.start(); } } /* nyc ignore next */ private start() { FeatureConfig.init( `${config.FEATURE_CONFIG_SERVER_HOST}`, parseInt(config.FEATURE_CONFIG_REFRESH_SEC || "300") ) .then((featureConf: FeatureConfig) => { this.setFeatureConfig(featureConf); logger.info("[FEATURECONFIG_SERVICE] service started"); }) .catch((error: Error) => { logger.error(error, `[FEATURECONFIG_SERVICE] error while starting service`); }); setInterval(() => this.logCurrentParameters(), LOGGING_INTERVAL); } /** * Initialise featureConfig object */ public static getInstance(): FeatureConfigService { return this.instance || (this.instance = new FeatureConfigService()); } /** * change featureConfig instance in the service by a new one * @param {FeatureConfig} newFeatureConfig * @memberof FeatureConfigService */ public setFeatureConfig(newFeatureConfig: FeatureConfig): void { this.featureConfig = newFeatureConfig; } public getStringValue( request: ChoiceRequest | null, paramName: string, defaultValue: string ): string { return this.getParameterValue(request, paramName, defaultValue); } public getBooleanValue( request: ChoiceRequest | null, paramName: string, defaultValue: boolean ): boolean { const param = this.getParameterValue(request, paramName, ""); if (param !== "true" && param !== "false") { return defaultValue; } return param === "true"; } public getFloatValue( request: ChoiceRequest | null, paramName: string, defaultValue: number ): number { const param = parseFloat(this.getParameterValue(request, paramName, "")); if (!Number.isFinite(param)) { return defaultValue; } return param; } public getIntValue( request: ChoiceRequest | null, paramName: string, defaultValue: number ): number { const param = parseInt(this.getParameterValue(request, paramName, "")); if (!Number.isFinite(param)) { return defaultValue; } return param; } /** * Get a string parameter and split it into a list of string. * @param {ChoiceRequest} request * @param {string} paramName * @param {string} separator * @param {string[]} defaultValue * @returns {string[]} */ public getParameterValueAsStringList( request: ChoiceRequest | null, paramName: string, separator: string, defaultValue: string[] ): string[] { const param = this.getParameterValue(request, paramName, ""); if (param === "") { return defaultValue; } return param.split(separator); } /** * get paramInfo for parameter paramName from FeatureConfig * @param {ChoiceRequest} request * @param {string} paramName * @param {string} defaultValue * @return {ParameterInfo} * @memberof FeatureConfigService */ public getParameterInfo( request: ChoiceRequest, paramName: string, defaultValue: string ): ParameterInfo | undefined { if (!this.featureConfig) { return undefined; } try { const context = new RequestContext(request.id, request.user?.odid); // Retrieve value return this.featureConfig.getParameter(paramName, defaultValue, context); } catch (error) { logger.warn(`[FEATURECONFIG_SERVICE] error while getting ParameterInfo, ${error}`); return undefined; } } public getParameterValue( request: ChoiceRequest | null, paramName: string, defaultValue: string ): string { if (!this.featureConfig) { return defaultValue; } try { if (request) { const context = new RequestContext(request.id, request.user?.odid); return this.featureConfig.getStringValue(paramName, defaultValue, context); } return this.featureConfig.getGlobalParameterValue(paramName, defaultValue); } catch (error) { logger.warn( `[FEATURECONFIG_SERVICE] Error while getting ParameterValue with ${request?.id}, ${error}` ); return defaultValue; } } /** * log in debug mode the current Parameters in the conf */ private logCurrentParameters(): void { if (this.featureConfig) { const nbAttempts = this.featureConfig.counters.refreshAttempts; const nbSuccess = this.featureConfig.counters.refreshSuccess; const nbFailures = this.featureConfig.counters.refreshFailures; const refreshSuccessRate = (100.0 * nbSuccess) / nbAttempts; logger.debug( () => `[FEATURECONFIG_SERVICE] ${nbAttempts} refreshs attempted, ${nbSuccess} succeeded (${refreshSuccessRate}%) and ${nbFailures} failed)` ); } else { logger.debug(() => `[FEATURECONFIG_SERVICE] featureConfig instance not yet available`); } } /** * log refresh stats from the featureConfig instance * @return {boolean} * @memberof FeatureConfigService */ public getHealth(): boolean { if (this.featureConfig) { return true; } return false; } }