test: add ErrorUtils test
[e-mobility-charging-stations-simulator.git] / src / utils / Configuration.ts
CommitLineData
d05b53c7 1import { existsSync, type FSWatcher, readFileSync, watch } from 'node:fs'
66a7748d
JB
2import { dirname, join } from 'node:path'
3import { env } from 'node:process'
4import { fileURLToPath } from 'node:url'
8114d10e 5
66a7748d 6import chalk from 'chalk'
38ae4ce2 7import { mergeDeepRight, once } from 'rambda'
8114d10e 8
83e00df1 9import {
268a74bb 10 ApplicationProtocol,
329eab0e 11 ApplicationProtocolVersion,
83e00df1 12 type ConfigurationData,
5d049829 13 ConfigurationSection,
268a74bb 14 FileType,
3d48c1c1 15 type LogConfiguration,
83e00df1
JB
16 type StationTemplateUrl,
17 type StorageConfiguration,
268a74bb 18 StorageType,
e7aeea18 19 SupervisionUrlDistribution,
83e00df1 20 type UIServerConfiguration,
66a7748d
JB
21 type WorkerConfiguration
22} from '../types/index.js'
769d3b10 23import {
da47bc29 24 DEFAULT_ELEMENT_ADD_DELAY,
769d3b10
JB
25 DEFAULT_POOL_MAX_SIZE,
26 DEFAULT_POOL_MIN_SIZE,
27 DEFAULT_WORKER_START_DELAY,
66a7748d
JB
28 WorkerProcessType
29} from '../worker/index.js'
4c3f6c20
JB
30import {
31 buildPerformanceUriFilePath,
32 checkWorkerElementsPerWorker,
33 checkWorkerProcessType,
34 getDefaultPerformanceStorageUri,
35 handleFileException,
36 logPrefix
37} from './ConfigurationUtils.js'
38import { Constants } from './Constants.js'
38ae4ce2 39import { hasOwnProp, isCFEnvironment } from './Utils.js'
7dde0b73 40
e7c0fce0
JB
41type ConfigurationSectionType =
42 | LogConfiguration
43 | StorageConfiguration
44 | WorkerConfiguration
66a7748d 45 | UIServerConfiguration
e7c0fce0 46
d05b53c7
JB
47const defaultUIServerConfiguration: UIServerConfiguration = {
48 enabled: false,
49 type: ApplicationProtocol.WS,
50 version: ApplicationProtocolVersion.VERSION_11,
51 options: {
52 host: Constants.DEFAULT_UI_SERVER_HOST,
53 port: Constants.DEFAULT_UI_SERVER_PORT
54 }
55}
56
57const defaultStorageConfiguration: StorageConfiguration = {
58 enabled: true,
59 type: StorageType.NONE
60}
61
62const defaultLogConfiguration: LogConfiguration = {
63 enabled: true,
64 file: 'logs/combined.log',
65 errorFile: 'logs/error.log',
66 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
67 level: 'info',
68 format: 'simple',
69 rotate: true
70}
71
72const defaultWorkerConfiguration: WorkerConfiguration = {
73 processType: WorkerProcessType.workerSet,
74 startDelay: DEFAULT_WORKER_START_DELAY,
75 elementsPerWorker: 'auto',
76 elementAddDelay: DEFAULT_ELEMENT_ADD_DELAY,
77 poolMinSize: DEFAULT_POOL_MIN_SIZE,
78 poolMaxSize: DEFAULT_POOL_MAX_SIZE
79}
80
66a7748d 81// eslint-disable-next-line @typescript-eslint/no-extraneous-class
268a74bb 82export class Configuration {
5199f9fd 83 public static configurationChangeCallback?: () => Promise<void>
6501eda9 84
d05b53c7 85 private static configurationFile: string | undefined
66a7748d
JB
86 private static configurationFileReloading = false
87 private static configurationData?: ConfigurationData
88 private static configurationFileWatcher?: FSWatcher
d05b53c7
JB
89 private static configurationSectionCache: Map<ConfigurationSection, ConfigurationSectionType>
90
91 static {
92 const configurationFile = join(dirname(fileURLToPath(import.meta.url)), 'assets', 'config.json')
93 if (existsSync(configurationFile)) {
94 Configuration.configurationFile = configurationFile
95 } else {
96 console.error(
97 `${chalk.green(logPrefix())} ${chalk.red(
98 `Configuration file '${configurationFile}' not found, using default configuration`
99 )}`
100 )
101 Configuration.configurationData = {
102 stationTemplateUrls: [],
103 supervisionUrls: 'ws://localhost:8180/steve/websocket/CentralSystemService',
104 supervisionUrlDistribution: SupervisionUrlDistribution.ROUND_ROBIN,
105 uiServer: defaultUIServerConfiguration,
106 performanceStorage: defaultStorageConfiguration,
107 log: defaultLogConfiguration,
108 worker: defaultWorkerConfiguration
109 }
110 }
111 Configuration.configurationSectionCache = new Map<
112 ConfigurationSection,
113 ConfigurationSectionType
114 >([
115 [ConfigurationSection.log, Configuration.buildLogSection()],
116 [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
117 [ConfigurationSection.worker, Configuration.buildWorkerSection()],
118 [ConfigurationSection.uiServer, Configuration.buildUIServerSection()]
119 ])
120 }
974efe6c 121
66a7748d 122 private constructor () {
d5bd1c00
JB
123 // This is intentional
124 }
125
e7c0fce0 126 public static getConfigurationSection<T extends ConfigurationSectionType>(
66a7748d 127 sectionName: ConfigurationSection
e7c0fce0 128 ): T {
81b9a105 129 if (!Configuration.isConfigurationSectionCached(sectionName)) {
66a7748d 130 Configuration.cacheConfigurationSection(sectionName)
c1c97db8 131 }
66a7748d 132 return Configuration.configurationSectionCache.get(sectionName) as T
5d049829
JB
133 }
134
66a7748d 135 public static getStationTemplateUrls (): StationTemplateUrl[] | undefined {
5f742aac 136 const checkDeprecatedConfigurationKeysOnce = once(
38ae4ce2 137 Configuration.checkDeprecatedConfigurationKeys.bind(Configuration)
66a7748d
JB
138 )
139 checkDeprecatedConfigurationKeysOnce()
140 return Configuration.getConfigurationData()?.stationTemplateUrls
5d049829
JB
141 }
142
66a7748d 143 public static getSupervisionUrls (): string | string[] | undefined {
a37fc6dc 144 if (
d760a0a6 145 Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData] != null
a37fc6dc 146 ) {
66a7748d 147 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5d049829 148 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
a37fc6dc 149 'supervisionURLs' as keyof ConfigurationData
66a7748d 150 ] as string | string[]
5d049829 151 }
66a7748d 152 return Configuration.getConfigurationData()?.supervisionUrls
5d049829
JB
153 }
154
66a7748d 155 public static getSupervisionUrlDistribution (): SupervisionUrlDistribution | undefined {
5d049829
JB
156 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
157 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
66a7748d 158 : SupervisionUrlDistribution.ROUND_ROBIN
5d049829
JB
159 }
160
66a7748d 161 public static workerPoolInUse (): boolean {
1d8f226b 162 return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes(
66a7748d 163 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
eb979012 164 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
66a7748d
JB
165 .processType!
166 )
c831d2bc
JB
167 }
168
66a7748d 169 public static workerDynamicPoolInUse (): boolean {
eb979012
JB
170 return (
171 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
172 .processType === WorkerProcessType.dynamicPool
66a7748d 173 )
c831d2bc
JB
174 }
175
66a7748d
JB
176 private static isConfigurationSectionCached (sectionName: ConfigurationSection): boolean {
177 return Configuration.configurationSectionCache.has(sectionName)
81b9a105
JB
178 }
179
66a7748d 180 private static cacheConfigurationSection (sectionName: ConfigurationSection): void {
81b9a105
JB
181 switch (sectionName) {
182 case ConfigurationSection.log:
66a7748d
JB
183 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection())
184 break
81b9a105
JB
185 case ConfigurationSection.performanceStorage:
186 Configuration.configurationSectionCache.set(
187 sectionName,
66a7748d
JB
188 Configuration.buildPerformanceStorageSection()
189 )
190 break
81b9a105 191 case ConfigurationSection.worker:
66a7748d
JB
192 Configuration.configurationSectionCache.set(sectionName, Configuration.buildWorkerSection())
193 break
81b9a105
JB
194 case ConfigurationSection.uiServer:
195 Configuration.configurationSectionCache.set(
196 sectionName,
66a7748d
JB
197 Configuration.buildUIServerSection()
198 )
199 break
81b9a105
JB
200 default:
201 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
66a7748d 202 throw new Error(`Unknown configuration section '${sectionName}'`)
81b9a105
JB
203 }
204 }
205
66a7748d 206 private static buildUIServerSection (): UIServerConfiguration {
d05b53c7 207 let uiServerConfiguration: UIServerConfiguration = defaultUIServerConfiguration
f74e97ac 208 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
b4c82e73 209 uiServerConfiguration = mergeDeepRight(
598c886d 210 uiServerConfiguration,
66a7748d
JB
211 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
212 Configuration.getConfigurationData()!.uiServer!
213 )
6a49ad23 214 }
66a7748d
JB
215 if (isCFEnvironment()) {
216 delete uiServerConfiguration.options?.host
217 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
48847bc0 218 uiServerConfiguration.options!.port = Number.parseInt(env.PORT!)
b803eefa 219 }
66a7748d 220 return uiServerConfiguration
6a49ad23
JB
221 }
222
66a7748d 223 private static buildPerformanceStorageSection (): StorageConfiguration {
789007bd
JB
224 let storageConfiguration: StorageConfiguration
225 switch (Configuration.getConfigurationData()?.performanceStorage?.type) {
226 case StorageType.SQLITE:
227 storageConfiguration = {
228 enabled: false,
229 type: StorageType.SQLITE,
230 uri: getDefaultPerformanceStorageUri(StorageType.SQLITE)
231 }
232 break
233 case StorageType.JSON_FILE:
789007bd
JB
234 storageConfiguration = {
235 enabled: false,
236 type: StorageType.JSON_FILE,
237 uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE)
238 }
239 break
a66bbcfe
JB
240 case StorageType.NONE:
241 default:
d05b53c7 242 storageConfiguration = defaultStorageConfiguration
a66bbcfe 243 break
66a7748d 244 }
f74e97ac 245 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 246 storageConfiguration = {
1ba1e8fb 247 ...storageConfiguration,
f74e97ac 248 ...Configuration.getConfigurationData()?.performanceStorage,
789007bd
JB
249 ...((Configuration.getConfigurationData()?.performanceStorage?.type ===
250 StorageType.JSON_FILE ||
251 Configuration.getConfigurationData()?.performanceStorage?.type === StorageType.SQLITE) &&
66a7748d
JB
252 Configuration.getConfigurationData()?.performanceStorage?.uri != null && {
253 uri: buildPerformanceUriFilePath(
254 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
255 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname
256 )
257 })
258 }
72f041bd 259 }
66a7748d 260 return storageConfiguration
7dde0b73
JB
261 }
262
66a7748d 263 private static buildLogSection (): LogConfiguration {
3d48c1c1 264 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac 265 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
66a7748d 266 enabled: Configuration.getConfigurationData()?.logEnabled
3d48c1c1 267 }),
f74e97ac 268 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
66a7748d 269 file: Configuration.getConfigurationData()?.logFile
3d48c1c1 270 }),
f74e97ac 271 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
66a7748d 272 errorFile: Configuration.getConfigurationData()?.logErrorFile
3d48c1c1 273 }),
f74e97ac 274 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
66a7748d 275 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval
3d48c1c1 276 }),
f74e97ac 277 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
66a7748d 278 level: Configuration.getConfigurationData()?.logLevel
3d48c1c1 279 }),
f74e97ac 280 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
66a7748d 281 console: Configuration.getConfigurationData()?.logConsole
3d48c1c1 282 }),
f74e97ac 283 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
66a7748d 284 format: Configuration.getConfigurationData()?.logFormat
3d48c1c1 285 }),
f74e97ac 286 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
66a7748d 287 rotate: Configuration.getConfigurationData()?.logRotate
3d48c1c1 288 }),
f74e97ac 289 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
66a7748d 290 maxFiles: Configuration.getConfigurationData()?.logMaxFiles
3d48c1c1 291 }),
f74e97ac 292 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
66a7748d
JB
293 maxSize: Configuration.getConfigurationData()?.logMaxSize
294 })
295 }
3d48c1c1
JB
296 const logConfiguration: LogConfiguration = {
297 ...defaultLogConfiguration,
298 ...deprecatedLogConfiguration,
f74e97ac 299 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
66a7748d
JB
300 Configuration.getConfigurationData()?.log)
301 }
302 return logConfiguration
3d48c1c1
JB
303 }
304
66a7748d 305 private static buildWorkerSection (): WorkerConfiguration {
3602e107
JB
306 const deprecatedWorkerConfiguration: WorkerConfiguration = {
307 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
66a7748d 308 processType: Configuration.getConfigurationData()?.workerProcess
3602e107
JB
309 }),
310 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
66a7748d 311 startDelay: Configuration.getConfigurationData()?.workerStartDelay
3602e107
JB
312 }),
313 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
66a7748d 314 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker
3602e107 315 }),
da47bc29
JB
316 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementAddDelay') && {
317 elementAddDelay: Configuration.getConfigurationData()?.elementAddDelay
318 }),
319 ...(hasOwnProp(Configuration.getConfigurationData()?.worker, 'elementStartDelay') && {
320 elementAddDelay: Configuration.getConfigurationData()?.worker?.elementStartDelay
3602e107
JB
321 }),
322 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
66a7748d 323 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize
3602e107
JB
324 }),
325 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
66a7748d
JB
326 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize
327 })
328 }
3602e107 329 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
66a7748d 330 delete Configuration.getConfigurationData()?.workerPoolStrategy
3602e107
JB
331 const workerConfiguration: WorkerConfiguration = {
332 ...defaultWorkerConfiguration,
333 ...deprecatedWorkerConfiguration,
334 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
66a7748d
JB
335 Configuration.getConfigurationData()?.worker)
336 }
337 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
338 checkWorkerProcessType(workerConfiguration.processType!)
339 checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker)
340 return workerConfiguration
3602e107
JB
341 }
342
66a7748d 343 private static checkDeprecatedConfigurationKeys (): void {
3602e107
JB
344 // connection timeout
345 Configuration.warnDeprecatedConfigurationKey(
346 'autoReconnectTimeout',
347 undefined,
66a7748d
JB
348 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
349 )
3602e107
JB
350 Configuration.warnDeprecatedConfigurationKey(
351 'connectionTimeout',
352 undefined,
66a7748d
JB
353 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
354 )
3602e107
JB
355 // connection retries
356 Configuration.warnDeprecatedConfigurationKey(
357 'autoReconnectMaxRetries',
358 undefined,
66a7748d
JB
359 'Use it in charging station template instead'
360 )
3602e107
JB
361 // station template url(s)
362 Configuration.warnDeprecatedConfigurationKey(
363 'stationTemplateURLs',
364 undefined,
66a7748d
JB
365 "Use 'stationTemplateUrls' instead"
366 )
d760a0a6
JB
367 Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] !=
368 null &&
66a7748d 369 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107 370 (Configuration.getConfigurationData()!.stationTemplateUrls =
66a7748d 371 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107
JB
372 Configuration.getConfigurationData()![
373 'stationTemplateURLs' as keyof ConfigurationData
66a7748d 374 ] as StationTemplateUrl[])
1c9de2b9 375 Configuration.getConfigurationData()?.stationTemplateUrls.forEach(
3602e107 376 (stationTemplateUrl: StationTemplateUrl) => {
5199f9fd 377 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
d760a0a6 378 if (stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl] != null) {
3602e107 379 console.error(
4354af5a 380 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
381 `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
382 )}`
383 )
3602e107 384 }
66a7748d
JB
385 }
386 )
3602e107
JB
387 // supervision url(s)
388 Configuration.warnDeprecatedConfigurationKey(
389 'supervisionURLs',
390 undefined,
66a7748d
JB
391 "Use 'supervisionUrls' instead"
392 )
3602e107
JB
393 // supervision urls distribution
394 Configuration.warnDeprecatedConfigurationKey(
395 'distributeStationToTenantEqually',
396 undefined,
66a7748d
JB
397 "Use 'supervisionUrlDistribution' instead"
398 )
3602e107
JB
399 Configuration.warnDeprecatedConfigurationKey(
400 'distributeStationsToTenantsEqually',
401 undefined,
66a7748d
JB
402 "Use 'supervisionUrlDistribution' instead"
403 )
3602e107 404 // worker section
e7aeea18 405 Configuration.warnDeprecatedConfigurationKey(
e80bc579 406 'useWorkerPool',
1895299d 407 undefined,
66a7748d
JB
408 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
409 )
cf2a5d9b
JB
410 Configuration.warnDeprecatedConfigurationKey(
411 'workerProcess',
1895299d 412 undefined,
66a7748d
JB
413 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
414 )
cf2a5d9b
JB
415 Configuration.warnDeprecatedConfigurationKey(
416 'workerStartDelay',
1895299d 417 undefined,
66a7748d
JB
418 `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`
419 )
cf2a5d9b
JB
420 Configuration.warnDeprecatedConfigurationKey(
421 'chargingStationsPerWorker',
1895299d 422 undefined,
66a7748d
JB
423 `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`
424 )
cf2a5d9b 425 Configuration.warnDeprecatedConfigurationKey(
da47bc29 426 'elementAddDelay',
1895299d 427 undefined,
da47bc29 428 `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`
66a7748d 429 )
cf2a5d9b
JB
430 Configuration.warnDeprecatedConfigurationKey(
431 'workerPoolMinSize',
1895299d 432 undefined,
66a7748d
JB
433 `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`
434 )
e7aeea18 435 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 436 'workerPoolSize',
1895299d 437 undefined,
66a7748d
JB
438 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
439 )
cf2a5d9b 440 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 441 'workerPoolMaxSize',
1895299d 442 undefined,
66a7748d
JB
443 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
444 )
cf2a5d9b 445 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 446 'workerPoolStrategy',
1895299d 447 undefined,
66a7748d
JB
448 `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`
449 )
eda9c451
JB
450 Configuration.warnDeprecatedConfigurationKey(
451 'poolStrategy',
974efe6c 452 ConfigurationSection.worker,
66a7748d
JB
453 'Not publicly exposed to end users'
454 )
da47bc29
JB
455 Configuration.warnDeprecatedConfigurationKey(
456 'elementStartDelay',
457 ConfigurationSection.worker,
458 "Use 'elementAddDelay' instead"
459 )
1d8f226b
JB
460 if (
461 Configuration.getConfigurationData()?.worker?.processType ===
462 ('staticPool' as WorkerProcessType)
463 ) {
464 console.error(
4354af5a 465 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
466 `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`
467 )}`
468 )
1d8f226b 469 }
3602e107
JB
470 // log section
471 Configuration.warnDeprecatedConfigurationKey(
472 'logEnabled',
473 undefined,
66a7748d
JB
474 `Use '${ConfigurationSection.log}' section to define the logging enablement instead`
475 )
3602e107
JB
476 Configuration.warnDeprecatedConfigurationKey(
477 'logFile',
478 undefined,
66a7748d
JB
479 `Use '${ConfigurationSection.log}' section to define the log file instead`
480 )
3602e107
JB
481 Configuration.warnDeprecatedConfigurationKey(
482 'logErrorFile',
483 undefined,
66a7748d
JB
484 `Use '${ConfigurationSection.log}' section to define the log error file instead`
485 )
3602e107
JB
486 Configuration.warnDeprecatedConfigurationKey(
487 'logConsole',
488 undefined,
66a7748d
JB
489 `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`
490 )
3602e107
JB
491 Configuration.warnDeprecatedConfigurationKey(
492 'logStatisticsInterval',
493 undefined,
66a7748d
JB
494 `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`
495 )
3602e107
JB
496 Configuration.warnDeprecatedConfigurationKey(
497 'logLevel',
498 undefined,
66a7748d
JB
499 `Use '${ConfigurationSection.log}' section to define the log level instead`
500 )
3602e107
JB
501 Configuration.warnDeprecatedConfigurationKey(
502 'logFormat',
503 undefined,
66a7748d
JB
504 `Use '${ConfigurationSection.log}' section to define the log format instead`
505 )
3602e107
JB
506 Configuration.warnDeprecatedConfigurationKey(
507 'logRotate',
508 undefined,
66a7748d
JB
509 `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`
510 )
3602e107
JB
511 Configuration.warnDeprecatedConfigurationKey(
512 'logMaxFiles',
513 undefined,
66a7748d
JB
514 `Use '${ConfigurationSection.log}' section to define the log maximum files instead`
515 )
3602e107
JB
516 Configuration.warnDeprecatedConfigurationKey(
517 'logMaxSize',
518 undefined,
66a7748d
JB
519 `Use '${ConfigurationSection.log}' section to define the log maximum size instead`
520 )
3602e107
JB
521 // performanceStorage section
522 Configuration.warnDeprecatedConfigurationKey(
523 'URI',
524 ConfigurationSection.performanceStorage,
66a7748d
JB
525 "Use 'uri' instead"
526 )
3602e107
JB
527 // uiServer section
528 if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
529 console.error(
4354af5a 530 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
531 `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
532 )}`
533 )
b5b2c3e8 534 }
7dde0b73 535 }
eb3937cb 536
66a7748d 537 private static warnDeprecatedConfigurationKey (
e7aeea18 538 key: string,
23c97c47 539 configurationSection?: ConfigurationSection,
66a7748d
JB
540 logMsgToAppend = ''
541 ): void {
e7aeea18 542 if (
23c97c47
JB
543 configurationSection != null &&
544 Configuration.getConfigurationData()?.[configurationSection as keyof ConfigurationData] !=
545 null &&
300418e9 546 (
23c97c47
JB
547 Configuration.getConfigurationData()?.[
548 configurationSection as keyof ConfigurationData
549 ] as Record<string, unknown>
d760a0a6 550 )[key] != null
e7aeea18
JB
551 ) {
552 console.error(
4354af5a 553 `${chalk.green(logPrefix())} ${chalk.red(
23c97c47 554 `Deprecated configuration key '${key}' usage in section '${configurationSection}'${
c5e52a07 555 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
556 }`
557 )}`
558 )
d760a0a6 559 } else if (Configuration.getConfigurationData()?.[key as keyof ConfigurationData] != null) {
e7aeea18 560 console.error(
4354af5a 561 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
562 `Deprecated configuration key '${key}' usage${
563 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
564 }`
565 )}`
566 )
eb3937cb
JB
567 }
568 }
569
8f8f87c4 570 public static getConfigurationData (): ConfigurationData | undefined {
d05b53c7
JB
571 if (
572 Configuration.configurationData == null &&
573 Configuration.configurationFile != null &&
574 Configuration.configurationFile.length > 0
575 ) {
23132a44 576 try {
f74e97ac 577 Configuration.configurationData = JSON.parse(
66a7748d
JB
578 readFileSync(Configuration.configurationFile, 'utf8')
579 ) as ConfigurationData
580 if (Configuration.configurationFileWatcher == null) {
581 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher()
1f8f6332 582 }
23132a44 583 } catch (error) {
4354af5a 584 handleFileException(
a95873d8 585 Configuration.configurationFile,
7164966d
JB
586 FileType.Configuration,
587 error as NodeJS.ErrnoException,
66a7748d
JB
588 logPrefix()
589 )
23132a44 590 }
eb3937cb 591 }
66a7748d 592 return Configuration.configurationData
eb3937cb 593 }
963ee397 594
66a7748d 595 private static getConfigurationFileWatcher (): FSWatcher | undefined {
d05b53c7
JB
596 if (Configuration.configurationFile == null || Configuration.configurationFile.length === 0) {
597 return
598 }
23132a44 599 try {
d972af76 600 return watch(Configuration.configurationFile, (event, filename): void => {
1b4ccee3
JB
601 if (
602 !Configuration.configurationFileReloading &&
66a7748d 603 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
aafba9d8 604 filename!.trim().length > 0 &&
1b4ccee3
JB
605 event === 'change'
606 ) {
66a7748d 607 Configuration.configurationFileReloading = true
38ae4ce2 608 const consoleWarnOnce = once(console.warn)
1b4ccee3 609 consoleWarnOnce(
4354af5a 610 `${chalk.green(logPrefix())} ${chalk.yellow(
66a7748d
JB
611 `${FileType.Configuration} ${this.configurationFile} file have changed, reload`
612 )}`
613 )
614 delete Configuration.configurationData
615 Configuration.configurationSectionCache.clear()
d760a0a6 616 if (Configuration.configurationChangeCallback != null) {
1b4ccee3 617 Configuration.configurationChangeCallback()
ea32ea05 618 .catch((error: unknown) => {
66a7748d 619 throw typeof error === 'string' ? new Error(error) : error
1b4ccee3
JB
620 })
621 .finally(() => {
66a7748d
JB
622 Configuration.configurationFileReloading = false
623 })
41d95b76 624 } else {
66a7748d 625 Configuration.configurationFileReloading = false
3ec10737 626 }
23132a44 627 }
66a7748d 628 })
23132a44 629 } catch (error) {
4354af5a 630 handleFileException(
a95873d8 631 Configuration.configurationFile,
7164966d
JB
632 FileType.Configuration,
633 error as NodeJS.ErrnoException,
66a7748d
JB
634 logPrefix()
635 )
23132a44 636 }
ded13d97 637 }
7dde0b73 638}