From: Jérôme Benoit Date: Fri, 6 Mar 2026 20:50:55 +0000 (+0100) Subject: refactor(utils): audit fixes — dedup, data-driven config, lazy logger, DRY X-Git-Tag: ocpp-server@v3.0.0~5 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=77e325be17f6ae146091089adf2e9d1e3cadaee6;p=e-mobility-charging-stations-simulator.git refactor(utils): audit fixes — dedup, data-driven config, lazy logger, DRY --- diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index f3df70b3..ef8014f4 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -922,7 +922,6 @@ export class ChargingStation extends EventEmitter { this.templateFile, FileType.ChargingStationTemplate, this.logPrefix(), - undefined, (event, filename): void => { if (isNotEmptyString(filename) && event === 'change') { try { diff --git a/src/charging-station/IdTagsCache.ts b/src/charging-station/IdTagsCache.ts index 9c94872a..6aed7fb6 100644 --- a/src/charging-station/IdTagsCache.ts +++ b/src/charging-station/IdTagsCache.ts @@ -176,7 +176,6 @@ export class IdTagsCache { file, FileType.Authorization, this.logPrefix(file), - undefined, (event, filename) => { if (isNotEmptyString(filename) && event === 'change') { try { diff --git a/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts b/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts index 7f76a18e..44f3a2da 100644 --- a/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts +++ b/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts @@ -1,7 +1,7 @@ import type { AuthCache, CacheStats } from '../interfaces/OCPPAuthService.js' import type { AuthorizationResult } from '../types/AuthTypes.js' -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { AuthorizationStatus } from '../types/AuthTypes.js' /** diff --git a/src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.ts b/src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.ts index da36b309..8f4414df 100644 --- a/src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.ts +++ b/src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.ts @@ -3,7 +3,7 @@ import type { OCPPAuthService } from '../interfaces/OCPPAuthService.js' import { OCPPError } from '../../../../exception/OCPPError.js' import { ErrorType } from '../../../../types/index.js' -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { OCPPAuthServiceImpl } from './OCPPAuthServiceImpl.js' const moduleName = 'OCPPAuthServiceFactory' diff --git a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts index 8f3533f2..3a926e34 100644 --- a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts +++ b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts @@ -4,7 +4,7 @@ import type { OCPP20AuthAdapter } from '../adapters/OCPP20AuthAdapter.js' import { OCPPError } from '../../../../exception/OCPPError.js' import { ErrorType } from '../../../../types/index.js' import { OCPPVersion } from '../../../../types/ocpp/OCPPVersion.js' -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { type ChargingStation } from '../../../ChargingStation.js' import { AuthComponentFactory } from '../factories/AuthComponentFactory.js' import { diff --git a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts index f207acae..758f531c 100644 --- a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts @@ -8,8 +8,7 @@ import type { } from '../types/AuthTypes.js' import { OCPPVersion } from '../../../../types/index.js' -import { isNotEmptyString, sleep } from '../../../../utils/index.js' -import { logger } from '../../../../utils/Logger.js' +import { isNotEmptyString, logger, sleep } from '../../../../utils/index.js' import { AuthenticationMethod, AuthorizationStatus, IdentifierType } from '../types/AuthTypes.js' /** diff --git a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts index 200199b8..415c2127 100644 --- a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts @@ -5,7 +5,7 @@ import type { } from '../interfaces/OCPPAuthService.js' import type { AuthConfiguration, AuthorizationResult, AuthRequest } from '../types/AuthTypes.js' -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { AuthContext, AuthenticationError, diff --git a/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts index 7a31eaa4..b0f917b8 100644 --- a/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts @@ -7,7 +7,7 @@ import type { import type { AuthConfiguration, AuthorizationResult, AuthRequest } from '../types/AuthTypes.js' import { OCPPVersion } from '../../../../types/ocpp/OCPPVersion.js' -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { AuthenticationError, AuthenticationMethod, diff --git a/src/charging-station/ocpp/auth/utils/ConfigValidator.ts b/src/charging-station/ocpp/auth/utils/ConfigValidator.ts index e1bec914..fa6607ea 100644 --- a/src/charging-station/ocpp/auth/utils/ConfigValidator.ts +++ b/src/charging-station/ocpp/auth/utils/ConfigValidator.ts @@ -1,4 +1,4 @@ -import { logger } from '../../../../utils/Logger.js' +import { logger } from '../../../../utils/index.js' import { type AuthConfiguration, AuthenticationError, AuthErrorCode } from '../types/AuthTypes.js' /** diff --git a/src/utils/ChargingStationConfigurationUtils.ts b/src/utils/ChargingStationConfigurationUtils.ts index 38c48a32..19a62cdb 100644 --- a/src/utils/ChargingStationConfigurationUtils.ts +++ b/src/utils/ChargingStationConfigurationUtils.ts @@ -39,7 +39,6 @@ export const buildEvsesStatus = ( chargingStation: ChargingStation, outputFormat: OutputFormat = OutputFormat.configuration ): (EvseStatusConfiguration | EvseStatusWorkerType)[] => { - // eslint-disable-next-line array-callback-return return [...chargingStation.evses.values()].map(evseStatus => { const connectorsStatus = [...evseStatus.connectors.values()].map( ({ @@ -49,20 +48,22 @@ export const buildEvsesStatus = ( ...connectorStatus }) => connectorStatus ) - let status: EvseStatusConfiguration switch (outputFormat) { - case OutputFormat.configuration: - status = { + case OutputFormat.configuration: { + const status: EvseStatusConfiguration = { ...evseStatus, connectorsStatus, } delete (status as EvseStatusWorkerType).connectors return status + } case OutputFormat.worker: return { ...evseStatus, connectors: connectorsStatus, } + default: + throw new RangeError(`Unknown output format: ${outputFormat as string}`) } }) } diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index ea164099..b4eb5538 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -30,10 +30,10 @@ import { buildPerformanceUriFilePath, checkWorkerElementsPerWorker, getDefaultPerformanceStorageUri, - handleFileException, logPrefix, } from './ConfigurationUtils.js' import { Constants } from './Constants.js' +import { handleFileException } from './ErrorUtils.js' import { has, isCFEnvironment, mergeDeepRight, once } from './Utils.js' type ConfigurationSectionType = @@ -142,7 +142,8 @@ export class Configuration { Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - logPrefix() + logPrefix(), + { consoleOut: true } ) } } @@ -201,53 +202,31 @@ export class Configuration { } private static buildLogSection (): LogConfiguration { - const deprecatedLogConfiguration: LogConfiguration = { - ...(has('logEnabled', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - enabled: Configuration.getConfigurationData()?.logEnabled, - }), - ...(has('logFile', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - file: Configuration.getConfigurationData()?.logFile, - }), - ...(has('logErrorFile', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - errorFile: Configuration.getConfigurationData()?.logErrorFile, - }), - ...(has('logStatisticsInterval', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval, - }), - ...(has('logLevel', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - level: Configuration.getConfigurationData()?.logLevel, - }), - ...(has('logConsole', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - console: Configuration.getConfigurationData()?.logConsole, - }), - ...(has('logFormat', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - format: Configuration.getConfigurationData()?.logFormat, - }), - ...(has('logRotate', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - rotate: Configuration.getConfigurationData()?.logRotate, - }), - ...(has('logMaxFiles', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - maxFiles: Configuration.getConfigurationData()?.logMaxFiles, - }), - ...(has('logMaxSize', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - maxSize: Configuration.getConfigurationData()?.logMaxSize, - }), + const configData = Configuration.getConfigurationData() + const deprecatedLogKeyMap: [string, string][] = [ + ['logEnabled', 'enabled'], + ['logFile', 'file'], + ['logErrorFile', 'errorFile'], + ['logStatisticsInterval', 'statisticsInterval'], + ['logLevel', 'level'], + ['logConsole', 'console'], + ['logFormat', 'format'], + ['logRotate', 'rotate'], + ['logMaxFiles', 'maxFiles'], + ['logMaxSize', 'maxSize'], + ] + const deprecatedLogConfiguration: Record = {} + for (const [deprecatedKey, newKey] of deprecatedLogKeyMap) { + if (has(deprecatedKey, configData)) { + deprecatedLogConfiguration[newKey] = (configData as unknown as Record)[ + deprecatedKey + ] + } } const logConfiguration: LogConfiguration = { ...defaultLogConfiguration, - ...deprecatedLogConfiguration, - ...(has(ConfigurationSection.log, Configuration.getConfigurationData()) && - Configuration.getConfigurationData()?.log), + ...(deprecatedLogConfiguration as Partial), + ...(has(ConfigurationSection.log, configData) && configData?.log), } return logConfiguration } @@ -310,44 +289,34 @@ export class Configuration { } private static buildWorkerSection (): WorkerConfiguration { - const deprecatedWorkerConfiguration: WorkerConfiguration = { - ...(has('workerProcess', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - processType: Configuration.getConfigurationData()?.workerProcess, - }), - ...(has('workerStartDelay', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - startDelay: Configuration.getConfigurationData()?.workerStartDelay, - }), - ...(has('chargingStationsPerWorker', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker, - }), - ...(has('elementAddDelay', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - elementAddDelay: Configuration.getConfigurationData()?.elementAddDelay, - }), - ...(has('elementStartDelay', Configuration.getConfigurationData()?.worker) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - elementAddDelay: Configuration.getConfigurationData()?.worker?.elementStartDelay, - }), - ...(has('workerPoolMinSize', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize, - }), - ...(has('workerPoolMaxSize', Configuration.getConfigurationData()) && { - // eslint-disable-next-line @typescript-eslint/no-deprecated - poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize, - }), + const configData = Configuration.getConfigurationData() + const deprecatedWorkerKeyMap: [string, string][] = [ + ['workerProcess', 'processType'], + ['workerStartDelay', 'startDelay'], + ['chargingStationsPerWorker', 'elementsPerWorker'], + ['elementAddDelay', 'elementAddDelay'], + ['workerPoolMinSize', 'poolMinSize'], + ['workerPoolMaxSize', 'poolMaxSize'], + ] + const deprecatedWorkerConfiguration: Record = {} + for (const [deprecatedKey, newKey] of deprecatedWorkerKeyMap) { + if (has(deprecatedKey, configData)) { + deprecatedWorkerConfiguration[newKey] = (configData as unknown as Record)[ + deprecatedKey + ] + } + } + if (has('elementStartDelay', configData?.worker)) { + deprecatedWorkerConfiguration.elementAddDelay = ( + configData?.worker as unknown as Record + ).elementStartDelay } - has('workerPoolStrategy', Configuration.getConfigurationData()) && - // eslint-disable-next-line @typescript-eslint/no-deprecated - delete Configuration.getConfigurationData()?.workerPoolStrategy + has('workerPoolStrategy', configData) && + delete (configData as unknown as Record).workerPoolStrategy const workerConfiguration: WorkerConfiguration = { ...defaultWorkerConfiguration, - ...deprecatedWorkerConfiguration, - ...(has(ConfigurationSection.worker, Configuration.getConfigurationData()) && - Configuration.getConfigurationData()?.worker), + ...(deprecatedWorkerConfiguration as Partial), + ...(has(ConfigurationSection.worker, configData) && configData?.worker), } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion checkWorkerProcessType(workerConfiguration.processType!) @@ -382,29 +351,133 @@ export class Configuration { } private static checkDeprecatedConfigurationKeys (): void { - // connection timeout - Configuration.warnDeprecatedConfigurationKey( - 'autoReconnectTimeout', - undefined, - "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" - ) - Configuration.warnDeprecatedConfigurationKey( - 'connectionTimeout', - undefined, - "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" - ) - // connection retries - Configuration.warnDeprecatedConfigurationKey( - 'autoReconnectMaxRetries', - undefined, - 'Use it in charging station template instead' - ) - // station template url(s) - Configuration.warnDeprecatedConfigurationKey( - 'stationTemplateURLs', - undefined, - "Use 'stationTemplateUrls' instead" - ) + const deprecatedKeys: [string, ConfigurationSection | undefined, string][] = [ + // connection timeout + [ + 'autoReconnectTimeout', + undefined, + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead", + ], + [ + 'connectionTimeout', + undefined, + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead", + ], + // connection retries + ['autoReconnectMaxRetries', undefined, 'Use it in charging station template instead'], + // station template url(s) + ['stationTemplateURLs', undefined, "Use 'stationTemplateUrls' instead"], + // supervision url(s) + ['supervisionURLs', undefined, "Use 'supervisionUrls' instead"], + // supervision urls distribution + ['distributeStationToTenantEqually', undefined, "Use 'supervisionUrlDistribution' instead"], + ['distributeStationsToTenantsEqually', undefined, "Use 'supervisionUrlDistribution' instead"], + // worker section + [ + 'useWorkerPool', + undefined, + `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`, + ], + [ + 'workerProcess', + undefined, + `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`, + ], + [ + 'workerStartDelay', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`, + ], + [ + 'chargingStationsPerWorker', + undefined, + `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`, + ], + [ + 'elementAddDelay', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`, + ], + [ + 'workerPoolMinSize', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`, + ], + [ + 'workerPoolSize', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, + ], + [ + 'workerPoolMaxSize', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, + ], + [ + 'workerPoolStrategy', + undefined, + `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`, + ], + ['poolStrategy', ConfigurationSection.worker, 'Not publicly exposed to end users'], + ['elementStartDelay', ConfigurationSection.worker, "Use 'elementAddDelay' instead"], + // log section + [ + 'logEnabled', + undefined, + `Use '${ConfigurationSection.log}' section to define the logging enablement instead`, + ], + [ + 'logFile', + undefined, + `Use '${ConfigurationSection.log}' section to define the log file instead`, + ], + [ + 'logErrorFile', + undefined, + `Use '${ConfigurationSection.log}' section to define the log error file instead`, + ], + [ + 'logConsole', + undefined, + `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`, + ], + [ + 'logStatisticsInterval', + undefined, + `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`, + ], + [ + 'logLevel', + undefined, + `Use '${ConfigurationSection.log}' section to define the log level instead`, + ], + [ + 'logFormat', + undefined, + `Use '${ConfigurationSection.log}' section to define the log format instead`, + ], + [ + 'logRotate', + undefined, + `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`, + ], + [ + 'logMaxFiles', + undefined, + `Use '${ConfigurationSection.log}' section to define the log maximum files instead`, + ], + [ + 'logMaxSize', + undefined, + `Use '${ConfigurationSection.log}' section to define the log maximum size instead`, + ], + // performanceStorage section + ['URI', ConfigurationSection.performanceStorage, "Use 'uri' instead"], + ] + for (const [key, section, msg] of deprecatedKeys) { + Configuration.warnDeprecatedConfigurationKey(key, section, msg) + } + // station template url(s) remapping Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] != null && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -424,79 +497,7 @@ export class Configuration { } } ) - // supervision url(s) - Configuration.warnDeprecatedConfigurationKey( - 'supervisionURLs', - undefined, - "Use 'supervisionUrls' instead" - ) - // supervision urls distribution - Configuration.warnDeprecatedConfigurationKey( - 'distributeStationToTenantEqually', - undefined, - "Use 'supervisionUrlDistribution' instead" - ) - Configuration.warnDeprecatedConfigurationKey( - 'distributeStationsToTenantsEqually', - undefined, - "Use 'supervisionUrlDistribution' instead" - ) - // worker section - Configuration.warnDeprecatedConfigurationKey( - 'useWorkerPool', - undefined, - `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerProcess', - undefined, - `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerStartDelay', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker start delay instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'chargingStationsPerWorker', - undefined, - `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'elementAddDelay', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerPoolMinSize', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerPoolSize', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerPoolMaxSize', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'workerPoolStrategy', - undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'poolStrategy', - ConfigurationSection.worker, - 'Not publicly exposed to end users' - ) - Configuration.warnDeprecatedConfigurationKey( - 'elementStartDelay', - ConfigurationSection.worker, - "Use 'elementAddDelay' instead" - ) + // worker section: staticPool check if ( Configuration.getConfigurationData()?.worker?.processType === ('staticPool' as WorkerProcessType) @@ -507,63 +508,6 @@ export class Configuration { )}` ) } - // log section - Configuration.warnDeprecatedConfigurationKey( - 'logEnabled', - undefined, - `Use '${ConfigurationSection.log}' section to define the logging enablement instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logFile', - undefined, - `Use '${ConfigurationSection.log}' section to define the log file instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logErrorFile', - undefined, - `Use '${ConfigurationSection.log}' section to define the log error file instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logConsole', - undefined, - `Use '${ConfigurationSection.log}' section to define the console logging enablement instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logStatisticsInterval', - undefined, - `Use '${ConfigurationSection.log}' section to define the log statistics interval instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logLevel', - undefined, - `Use '${ConfigurationSection.log}' section to define the log level instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logFormat', - undefined, - `Use '${ConfigurationSection.log}' section to define the log format instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logRotate', - undefined, - `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logMaxFiles', - undefined, - `Use '${ConfigurationSection.log}' section to define the log maximum files instead` - ) - Configuration.warnDeprecatedConfigurationKey( - 'logMaxSize', - undefined, - `Use '${ConfigurationSection.log}' section to define the log maximum size instead` - ) - // performanceStorage section - Configuration.warnDeprecatedConfigurationKey( - 'URI', - ConfigurationSection.performanceStorage, - "Use 'uri' instead" - ) // uiServer section if (has('uiWebSocketServer', Configuration.getConfigurationData())) { console.error( @@ -617,7 +561,8 @@ export class Configuration { Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - logPrefix() + logPrefix(), + { consoleOut: true } ) } } diff --git a/src/utils/ConfigurationUtils.ts b/src/utils/ConfigurationUtils.ts index f1f5c0cc..9cd6a59c 100644 --- a/src/utils/ConfigurationUtils.ts +++ b/src/utils/ConfigurationUtils.ts @@ -1,10 +1,9 @@ -import chalk from 'chalk' import { dirname, join, resolve } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' -import { type ElementsPerWorkerType, type FileType, StorageType } from '../types/index.js' +import { type ElementsPerWorkerType, StorageType } from '../types/index.js' import { Constants } from './Constants.js' -import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js' +import { logPrefix as utilsLogPrefix } from './Utils.js' export const logPrefix = (): string => { return utilsLogPrefix(' Simulator configuration |') @@ -35,46 +34,6 @@ export const getDefaultPerformanceStorageUri = (storageType: StorageType): strin } } -export const handleFileException = ( - file: string, - fileType: FileType, - error: NodeJS.ErrnoException, - logPfx: string -): void => { - const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : '' - let logMsg: string - switch (error.code) { - case 'EACCES': - logMsg = `${fileType} file ${file} access denied: ` - break - case 'EEXIST': - logMsg = `${fileType} file ${file} already exists: ` - break - case 'EISDIR': - logMsg = `${fileType} file ${file} is a directory: ` - break - case 'ENOENT': - logMsg = `${fileType} file ${file} not found: ` - break - case 'ENOSPC': - logMsg = `${fileType} file ${file} no space left on device: ` - break - case 'ENOTDIR': - logMsg = `${fileType} file ${file} parent is not a directory: ` - break - case 'EPERM': - logMsg = `${fileType} file ${file} permission denied: ` - break - case 'EROFS': - logMsg = `${fileType} file ${file} read-only file system: ` - break - default: - logMsg = `${fileType} file ${file} error: ` - } - console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error) - throw error -} - export const checkWorkerElementsPerWorker = ( elementsPerWorker: ElementsPerWorkerType | undefined ): void => { diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index d2e62036..769f5da7 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -1,29 +1,16 @@ -import { type FSWatcher, readFileSync, watch, type WatchListener } from 'node:fs' +import { type FSWatcher, watch, type WatchListener } from 'node:fs' -import type { FileType, JsonType } from '../types/index.js' +import type { FileType } from '../types/index.js' import { handleFileException } from './ErrorUtils.js' import { logger } from './Logger.js' import { isNotEmptyString } from './Utils.js' -export const watchJsonFile = ( +export const watchJsonFile = ( file: string, fileType: FileType, logPrefix: string, - refreshedVariable?: T, - listener: WatchListener = (event, filename) => { - if (isNotEmptyString(filename) && event === 'change') { - try { - logger.debug(`${logPrefix} ${fileType} file ${file} has changed, reload`) - refreshedVariable != null && - (refreshedVariable = JSON.parse(readFileSync(file, 'utf8')) as T) - } catch (error) { - handleFileException(file, fileType, error as NodeJS.ErrnoException, logPrefix, { - throwError: false, - }) - } - } - } + listener: WatchListener ): FSWatcher | undefined => { if (isNotEmptyString(file)) { try { diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index e8b3af64..fd282638 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -1,83 +1,120 @@ import type { FormatWrap } from 'logform' -import { createLogger, format, type transport, transports as WinstonTransports } from 'winston' +import { + createLogger, + format, + type transport, + type Logger as WinstonLogger, + transports as WinstonTransports, +} from 'winston' import DailyRotateFile from 'winston-daily-rotate-file' import { ConfigurationSection, type LogConfiguration } from '../types/index.js' import { Configuration } from './Configuration.js' import { insertAt, isNotEmptyString } from './Utils.js' -const logConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.log -) -const transports: transport[] = [] -if (logConfiguration.rotate === true) { - const logMaxFiles = logConfiguration.maxFiles - const logMaxSize = logConfiguration.maxSize - if (isNotEmptyString(logConfiguration.errorFile)) { - transports.push( - new DailyRotateFile({ - filename: insertAt( - logConfiguration.errorFile, - '-%DATE%', - logConfiguration.errorFile.indexOf('.log') - ), - level: 'error', - ...(logMaxFiles != null && { maxFiles: logMaxFiles }), - ...(logMaxSize != null && { maxSize: logMaxSize }), - }) - ) - } - if (isNotEmptyString(logConfiguration.file)) { - transports.push( - new DailyRotateFile({ - filename: insertAt(logConfiguration.file, '-%DATE%', logConfiguration.file.indexOf('.log')), - ...(logMaxFiles != null && { maxFiles: logMaxFiles }), - ...(logMaxSize != null && { maxSize: logMaxSize }), - }) - ) +let loggerInstance: undefined | WinstonLogger + +const getLoggerInstance = (): WinstonLogger => { + if (loggerInstance !== undefined) { + return loggerInstance } -} else { - if (isNotEmptyString(logConfiguration.errorFile)) { - transports.push( - new WinstonTransports.File({ - filename: logConfiguration.errorFile, - level: 'error', - }) - ) + const logConfiguration = Configuration.getConfigurationSection( + ConfigurationSection.log + ) + const logTransports: transport[] = [] + if (logConfiguration.rotate === true) { + const logMaxFiles = logConfiguration.maxFiles + const logMaxSize = logConfiguration.maxSize + if (isNotEmptyString(logConfiguration.errorFile)) { + logTransports.push( + new DailyRotateFile({ + filename: insertAt( + logConfiguration.errorFile, + '-%DATE%', + logConfiguration.errorFile.indexOf('.log') + ), + level: 'error', + ...(logMaxFiles != null && { maxFiles: logMaxFiles }), + ...(logMaxSize != null && { maxSize: logMaxSize }), + }) + ) + } + if (isNotEmptyString(logConfiguration.file)) { + logTransports.push( + new DailyRotateFile({ + filename: insertAt( + logConfiguration.file, + '-%DATE%', + logConfiguration.file.indexOf('.log') + ), + ...(logMaxFiles != null && { maxFiles: logMaxFiles }), + ...(logMaxSize != null && { maxSize: logMaxSize }), + }) + ) + } + } else { + if (isNotEmptyString(logConfiguration.errorFile)) { + logTransports.push( + new WinstonTransports.File({ + filename: logConfiguration.errorFile, + level: 'error', + }) + ) + } + if (isNotEmptyString(logConfiguration.file)) { + logTransports.push( + new WinstonTransports.File({ + filename: logConfiguration.file, + }) + ) + } } - if (isNotEmptyString(logConfiguration.file)) { - transports.push( - new WinstonTransports.File({ - filename: logConfiguration.file, + const logFormat = format.combine( + format.splat(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)() + ) + loggerInstance = createLogger({ + format: logFormat, + level: logConfiguration.level, + silent: logConfiguration.enabled === false || logTransports.length === 0, + transports: logTransports, + }) + // + // If enabled, log to the `console` with the format: + // `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` + // + if (logConfiguration.console === true) { + loggerInstance.add( + new WinstonTransports.Console({ + format: logFormat, }) ) } + return loggerInstance } -const logFormat = format.combine( - format.splat(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)() -) - -const logger = createLogger({ - format: logFormat, - level: logConfiguration.level, - silent: logConfiguration.enabled === false || transports.length === 0, - transports, +export const logger = new Proxy({} as WinstonLogger, { + get (target, property, receiver): unknown { + if (Reflect.has(target, property)) { + return Reflect.get(target, property, receiver) as unknown + } + return Reflect.get(getLoggerInstance(), property, receiver) as unknown + }, + getOwnPropertyDescriptor (target, property) { + return ( + Reflect.getOwnPropertyDescriptor(target, property) ?? + Reflect.getOwnPropertyDescriptor(getLoggerInstance(), property) + ) + }, + getPrototypeOf () { + return Reflect.getPrototypeOf(getLoggerInstance()) + }, + has (target, property) { + return Reflect.has(target, property) || Reflect.has(getLoggerInstance(), property) + }, + set (target, property, value, receiver) { + return Reflect.set(target, property, value, receiver) + }, }) - -// -// If enabled, log to the `console` with the format: -// `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` -// -if (logConfiguration.console === true) { - logger.add( - new WinstonTransports.Console({ - format: logFormat, - }) - ) -} - -export { logger } diff --git a/src/utils/MessageChannelUtils.ts b/src/utils/MessageChannelUtils.ts index 41c380c7..f40795e7 100644 --- a/src/utils/MessageChannelUtils.ts +++ b/src/utils/MessageChannelUtils.ts @@ -16,49 +16,59 @@ import { OutputFormat, } from './ChargingStationConfigurationUtils.js' -export const buildAddedMessage = ( - chargingStation: ChargingStation +const buildChargingStationWorkerMessage = ( + chargingStation: ChargingStation, + event: ChargingStationWorkerMessageEvents ): ChargingStationWorkerMessage => { return { data: buildChargingStationDataPayload(chargingStation), - event: ChargingStationWorkerMessageEvents.added, + event, } } +export const buildAddedMessage = ( + chargingStation: ChargingStation +): ChargingStationWorkerMessage => { + return buildChargingStationWorkerMessage( + chargingStation, + ChargingStationWorkerMessageEvents.added + ) +} + export const buildDeletedMessage = ( chargingStation: ChargingStation ): ChargingStationWorkerMessage => { - return { - data: buildChargingStationDataPayload(chargingStation), - event: ChargingStationWorkerMessageEvents.deleted, - } + return buildChargingStationWorkerMessage( + chargingStation, + ChargingStationWorkerMessageEvents.deleted + ) } export const buildStartedMessage = ( chargingStation: ChargingStation ): ChargingStationWorkerMessage => { - return { - data: buildChargingStationDataPayload(chargingStation), - event: ChargingStationWorkerMessageEvents.started, - } + return buildChargingStationWorkerMessage( + chargingStation, + ChargingStationWorkerMessageEvents.started + ) } export const buildStoppedMessage = ( chargingStation: ChargingStation ): ChargingStationWorkerMessage => { - return { - data: buildChargingStationDataPayload(chargingStation), - event: ChargingStationWorkerMessageEvents.stopped, - } + return buildChargingStationWorkerMessage( + chargingStation, + ChargingStationWorkerMessageEvents.stopped + ) } export const buildUpdatedMessage = ( chargingStation: ChargingStation ): ChargingStationWorkerMessage => { - return { - data: buildChargingStationDataPayload(chargingStation), - event: ChargingStationWorkerMessageEvents.updated, - } + return buildChargingStationWorkerMessage( + chargingStation, + ChargingStationWorkerMessageEvents.updated + ) } export const buildPerformanceStatisticsMessage = ( diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 504ceb15..bc723324 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -343,6 +343,9 @@ export const clone = (object: T): T => { type AsyncFunctionType = (...args: A) => PromiseLike +// eslint-disable-next-line @typescript-eslint/no-empty-function +const AsyncFunctionConstructor = (async () => {}).constructor + /** * Detects whether the given value is an asynchronous function or not. * @param fn - Unknown value. @@ -350,8 +353,7 @@ type AsyncFunctionType = (...args: A) => PromiseLike * @internal */ export const isAsyncFunction = (fn: unknown): fn is AsyncFunctionType => { - // eslint-disable-next-line @typescript-eslint/no-empty-function - return fn?.constructor === (async () => {}).constructor + return fn?.constructor === AsyncFunctionConstructor } export const isCFEnvironment = (): boolean => { diff --git a/tests/utils/ConfigurationUtils.test.ts b/tests/utils/ConfigurationUtils.test.ts index 61d19314..983d87ea 100644 --- a/tests/utils/ConfigurationUtils.test.ts +++ b/tests/utils/ConfigurationUtils.test.ts @@ -10,9 +10,9 @@ import { buildPerformanceUriFilePath, checkWorkerElementsPerWorker, getDefaultPerformanceStorageUri, - handleFileException, logPrefix, } from '../../src/utils/ConfigurationUtils.js' +import { handleFileException } from '../../src/utils/ErrorUtils.js' import { standardCleanup } from '../helpers/TestLifecycleHelpers.js' await describe('ConfigurationUtils', async () => { @@ -52,7 +52,9 @@ await describe('ConfigurationUtils', async () => { const error = new Error() as NodeJS.ErrnoException error.code = 'ENOENT' expect(() => { - handleFileException('path/to/module.js', FileType.Authorization, error, 'log prefix |') + handleFileException('path/to/module.js', FileType.Authorization, error, 'log prefix |', { + consoleOut: true, + }) }).toThrow(error) expect(mockConsoleError.mock.calls.length).toBe(1) })