X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Futils%2FConfiguration.ts;h=1f370480d4d1b6fc47828792ccbf8271cfb81724;hb=42b8cf5cdca8eaab1e7442f7c92c2a5ed97434f6;hp=4e908a5e0579dcf505f1bfbf8346c4e8f7010bce;hpb=5f742aac345f2eb8897c24651d00c7fb43dabbf8;p=e-mobility-charging-stations-simulator.git diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index 4e908a5e..1f370480 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -1,12 +1,20 @@ import { type FSWatcher, readFileSync, watch } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; +import { env } from 'node:process'; import { fileURLToPath } from 'node:url'; import chalk from 'chalk'; import merge from 'just-merge'; import { Constants } from './Constants'; -import { hasOwnProp, isCFEnvironment, isNotEmptyString, isUndefined, once } from './Utils'; +import { + hasOwnProp, + isCFEnvironment, + isNotEmptyString, + isUndefined, + logPrefix, + once, +} from './Utils'; import { ApplicationProtocol, type ConfigurationData, @@ -34,13 +42,21 @@ type ConfigurationSectionType = | WorkerConfiguration | UIServerConfiguration; +// Avoid ESM race condition at class initialization +const configurationLogPrefix = (): string => { + return logPrefix(' Simulator configuration |'); +}; + export class Configuration { + public static configurationChangeCallback: () => Promise; + private static configurationFile = join( dirname(fileURLToPath(import.meta.url)), 'assets', 'config.json', ); + private static configurationFileReloading = false; private static configurationData?: ConfigurationData; private static configurationFileWatcher?: FSWatcher; private static configurationSectionCache = new Map< @@ -53,18 +69,10 @@ export class Configuration { [ConfigurationSection.uiServer, Configuration.buildUIServerSection()], ]); - private static warnDeprecatedConfigurationKeys = false; - - private static configurationChangeCallback?: () => Promise; - private constructor() { // This is intentional } - public static setConfigurationChangeCallback(cb: () => Promise): void { - Configuration.configurationChangeCallback = cb; - } - public static getConfigurationSection( sectionName: ConfigurationSection, ): T { @@ -77,7 +85,7 @@ export class Configuration { public static getStationTemplateUrls(): StationTemplateUrl[] | undefined { const checkDeprecatedConfigurationKeysOnce = once( Configuration.checkDeprecatedConfigurationKeys.bind(Configuration), - this, + Configuration, ); checkDeprecatedConfigurationKeysOnce(); return Configuration.getConfigurationData()?.stationTemplateUrls; @@ -103,7 +111,7 @@ export class Configuration { } public static workerPoolInUse(): boolean { - return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes( + return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes( Configuration.getConfigurationSection(ConfigurationSection.worker) .processType!, ); @@ -166,7 +174,7 @@ export class Configuration { } if (isCFEnvironment() === true) { delete uiServerConfiguration.options?.host; - uiServerConfiguration.options!.port = parseInt(process.env.PORT!); + uiServerConfiguration.options!.port = parseInt(env.PORT!); } return uiServerConfiguration; } @@ -289,14 +297,7 @@ export class Configuration { return workerConfiguration; } - private static logPrefix = (): string => { - return `${new Date().toLocaleString()} Simulator configuration |`; - }; - private static checkDeprecatedConfigurationKeys() { - if (Configuration.warnDeprecatedConfigurationKeys) { - return; - } // connection timeout Configuration.warnDeprecatedConfigurationKey( 'autoReconnectTimeout', @@ -331,7 +332,7 @@ export class Configuration { (stationTemplateUrl: StationTemplateUrl) => { if (!isUndefined(stationTemplateUrl?.['numberOfStation' as keyof StationTemplateUrl])) { console.error( - `${chalk.green(Configuration.logPrefix())} ${chalk.red( + `${chalk.green(configurationLogPrefix())} ${chalk.red( `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`, )}`, ); @@ -387,17 +388,17 @@ export class Configuration { `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`, ); Configuration.warnDeprecatedConfigurationKey( - 'workerPoolSize;', + 'workerPoolSize', undefined, `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, ); Configuration.warnDeprecatedConfigurationKey( - 'workerPoolMaxSize;', + 'workerPoolMaxSize', undefined, `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, ); Configuration.warnDeprecatedConfigurationKey( - 'workerPoolStrategy;', + 'workerPoolStrategy', undefined, `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`, ); @@ -406,6 +407,16 @@ export class Configuration { ConfigurationSection.worker, 'Not publicly exposed to end users', ); + if ( + Configuration.getConfigurationData()?.worker?.processType === + ('staticPool' as WorkerProcessType) + ) { + console.error( + `${chalk.green(configurationLogPrefix())} ${chalk.red( + `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`, + )}`, + ); + } // log section Configuration.warnDeprecatedConfigurationKey( 'logEnabled', @@ -466,12 +477,11 @@ export class Configuration { // uiServer section if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) { console.error( - `${chalk.green(Configuration.logPrefix())} ${chalk.red( + `${chalk.green(configurationLogPrefix())} ${chalk.red( `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`, )}`, ); } - Configuration.warnDeprecatedConfigurationKeys = true; } private static warnDeprecatedConfigurationKey( @@ -494,7 +504,7 @@ export class Configuration { ) ) { console.error( - `${chalk.green(Configuration.logPrefix())} ${chalk.red( + `${chalk.green(configurationLogPrefix())} ${chalk.red( `Deprecated configuration key '${key}' usage in section '${sectionName}'${ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : '' }`, @@ -504,7 +514,7 @@ export class Configuration { !isUndefined(Configuration.getConfigurationData()?.[key as keyof ConfigurationData]) ) { console.error( - `${chalk.green(Configuration.logPrefix())} ${chalk.red( + `${chalk.green(configurationLogPrefix())} ${chalk.red( `Deprecated configuration key '${key}' usage${ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : '' }`, @@ -527,7 +537,7 @@ export class Configuration { Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - Configuration.logPrefix(), + configurationLogPrefix(), ); } } @@ -537,13 +547,30 @@ export class Configuration { private static getConfigurationFileWatcher(): FSWatcher | undefined { try { return watch(Configuration.configurationFile, (event, filename): void => { - if (filename!.trim()!.length > 0 && event === 'change') { + if ( + !Configuration.configurationFileReloading && + filename!.trim()!.length > 0 && + event === 'change' + ) { + Configuration.configurationFileReloading = true; + const consoleWarnOnce = once(console.warn, this); + consoleWarnOnce( + `${chalk.green(configurationLogPrefix())} ${chalk.yellow( + `${FileType.Configuration} ${this.configurationFile} file have changed, reload`, + )}`, + ); delete Configuration.configurationData; Configuration.configurationSectionCache.clear(); if (!isUndefined(Configuration.configurationChangeCallback)) { - Configuration.configurationChangeCallback!().catch((error) => { - throw typeof error === 'string' ? new Error(error) : error; - }); + Configuration.configurationChangeCallback() + .catch((error) => { + throw typeof error === 'string' ? new Error(error) : error; + }) + .finally(() => { + Configuration.configurationFileReloading = false; + }); + } else { + Configuration.configurationFileReloading = false; } } }); @@ -552,7 +579,7 @@ export class Configuration { Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - Configuration.logPrefix(), + configurationLogPrefix(), ); } } @@ -561,27 +588,27 @@ export class Configuration { file: string, fileType: FileType, error: NodeJS.ErrnoException, - logPrefix: string, + logPfx: string, ): void { - const prefix = isNotEmptyString(logPrefix) ? `${logPrefix} ` : ''; + const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : ''; let logMsg: string; switch (error.code) { case 'ENOENT': - logMsg = `${fileType} file ${file} not found:`; + logMsg = `${fileType} file ${file} not found: `; break; case 'EEXIST': - logMsg = `${fileType} file ${file} already exists:`; + logMsg = `${fileType} file ${file} already exists: `; break; case 'EACCES': - logMsg = `${fileType} file ${file} access denied:`; + logMsg = `${fileType} file ${file} access denied: `; break; case 'EPERM': - logMsg = `${fileType} file ${file} permission denied:`; + logMsg = `${fileType} file ${file} permission denied: `; break; default: - logMsg = `${fileType} file ${file} error:`; + logMsg = `${fileType} file ${file} error: `; } - console.error(`${chalk.green(prefix)}${chalk.red(`${logMsg} `)}`, error); + console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error); throw error; }