build(deps): apply updates
[e-mobility-charging-stations-simulator.git] / src / utils / Configuration.ts
CommitLineData
d972af76
JB
1import { type FSWatcher, readFileSync, watch } from 'node:fs';
2import { dirname, join, resolve } from 'node:path';
130783a7 3import { fileURLToPath } from 'node:url';
8114d10e
JB
4
5import chalk from 'chalk';
088ee3c1 6import merge from 'just-merge';
8114d10e 7
878e026c 8import { Constants } from './Constants';
9bf0ef23 9import { hasOwnProp, isCFEnvironment, isNotEmptyString, isUndefined } from './Utils';
83e00df1 10import {
268a74bb 11 ApplicationProtocol,
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
JB
20 type UIServerConfiguration,
21 type WorkerConfiguration,
268a74bb
JB
22} from '../types';
23import { WorkerConstants, WorkerProcessType } from '../worker';
7dde0b73 24
e7c0fce0
JB
25type ConfigurationSectionType =
26 | LogConfiguration
27 | StorageConfiguration
28 | WorkerConfiguration
29 | UIServerConfiguration;
30
268a74bb 31export class Configuration {
d972af76
JB
32 private static configurationFile = join(
33 dirname(fileURLToPath(import.meta.url)),
e7aeea18 34 'assets',
5edd8ba0 35 'config.json',
e7aeea18 36 );
10068088 37
c7db8ecb
JB
38 private static configurationFileWatcher?: FSWatcher;
39 private static configurationData?: ConfigurationData;
974efe6c
JB
40 private static configurationSectionCache = new Map<
41 ConfigurationSection,
e7c0fce0 42 ConfigurationSectionType
5d049829
JB
43 >([
44 [ConfigurationSection.log, Configuration.buildLogSection()],
45 [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
46 [ConfigurationSection.worker, Configuration.buildWorkerSection()],
47 [ConfigurationSection.uiServer, Configuration.buildUIServerSection()],
48 ]);
974efe6c 49
0e8e6cbe 50 private static configurationChangeCallback?: () => Promise<void>;
e57acf6a 51
d5bd1c00
JB
52 private constructor() {
53 // This is intentional
54 }
55
aa7d6d95 56 public static setConfigurationChangeCallback(cb: () => Promise<void>): void {
e57acf6a
JB
57 Configuration.configurationChangeCallback = cb;
58 }
7dde0b73 59
e7c0fce0
JB
60 public static getConfigurationSection<T extends ConfigurationSectionType>(
61 sectionName: ConfigurationSection,
62 ): T {
c1c97db8
JB
63 if (!Configuration.configurationSectionCache.has(sectionName)) {
64 switch (sectionName) {
65 case ConfigurationSection.log:
66 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection());
67 break;
68 case ConfigurationSection.performanceStorage:
69 Configuration.configurationSectionCache.set(
70 sectionName,
71 Configuration.buildPerformanceStorageSection(),
72 );
73 break;
74 case ConfigurationSection.worker:
75 Configuration.configurationSectionCache.set(
76 sectionName,
77 Configuration.buildWorkerSection(),
78 );
79 break;
80 case ConfigurationSection.uiServer:
81 Configuration.configurationSectionCache.set(
82 sectionName,
83 Configuration.buildUIServerSection(),
84 );
85 break;
86 default:
87 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
88 throw new Error(`Unknown configuration section '${sectionName}'`);
89 }
90 }
5d049829
JB
91 return Configuration.configurationSectionCache.get(sectionName) as T;
92 }
93
94 public static getAutoReconnectMaxRetries(): number | undefined {
95 Configuration.warnDeprecatedConfigurationKey(
96 'autoReconnectTimeout',
97 undefined,
98 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
99 );
100 Configuration.warnDeprecatedConfigurationKey(
101 'connectionTimeout',
102 undefined,
103 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
104 );
105 Configuration.warnDeprecatedConfigurationKey(
106 'autoReconnectMaxRetries',
107 undefined,
108 'Use it in charging station template instead',
109 );
110 if (hasOwnProp(Configuration.getConfigurationData(), 'autoReconnectMaxRetries')) {
111 return Configuration.getConfigurationData()?.autoReconnectMaxRetries;
112 }
113 }
114
115 public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
116 Configuration.warnDeprecatedConfigurationKey(
117 'stationTemplateURLs',
118 undefined,
119 "Use 'stationTemplateUrls' instead",
120 );
121 // eslint-disable-next-line @typescript-eslint/dot-notation
a37fc6dc
JB
122 !isUndefined(
123 Configuration.getConfigurationData()!['stationTemplateURLs' as keyof ConfigurationData],
124 ) &&
5d049829
JB
125 (Configuration.getConfigurationData()!.stationTemplateUrls =
126 Configuration.getConfigurationData()![
127 // eslint-disable-next-line @typescript-eslint/dot-notation
a37fc6dc 128 'stationTemplateURLs' as keyof ConfigurationData
5d049829
JB
129 ] as StationTemplateUrl[]);
130 Configuration.getConfigurationData()!.stationTemplateUrls.forEach(
131 (stationTemplateUrl: StationTemplateUrl) => {
132 // eslint-disable-next-line @typescript-eslint/dot-notation
a37fc6dc 133 if (!isUndefined(stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl])) {
5d049829
JB
134 console.error(
135 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
136 `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`,
137 )}`,
138 );
139 }
140 },
141 );
142 return Configuration.getConfigurationData()?.stationTemplateUrls;
143 }
144
5d049829
JB
145 public static getSupervisionUrls(): string | string[] | undefined {
146 Configuration.warnDeprecatedConfigurationKey(
147 'supervisionURLs',
148 undefined,
149 "Use 'supervisionUrls' instead",
150 );
151 // eslint-disable-next-line @typescript-eslint/dot-notation
a37fc6dc
JB
152 if (
153 !isUndefined(
154 Configuration.getConfigurationData()!['supervisionURLs' as keyof ConfigurationData],
155 )
156 ) {
5d049829
JB
157 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
158 // eslint-disable-next-line @typescript-eslint/dot-notation
a37fc6dc 159 'supervisionURLs' as keyof ConfigurationData
5d049829
JB
160 ] as string | string[];
161 }
162 return Configuration.getConfigurationData()?.supervisionUrls;
163 }
164
165 public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
166 Configuration.warnDeprecatedConfigurationKey(
167 'distributeStationToTenantEqually',
168 undefined,
169 "Use 'supervisionUrlDistribution' instead",
170 );
171 Configuration.warnDeprecatedConfigurationKey(
172 'distributeStationsToTenantsEqually',
173 undefined,
174 "Use 'supervisionUrlDistribution' instead",
175 );
176 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
177 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
178 : SupervisionUrlDistribution.ROUND_ROBIN;
179 }
180
c831d2bc
JB
181 public static workerPoolInUse(): boolean {
182 return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
183 Configuration.buildWorkerSection().processType!,
184 );
185 }
186
187 public static workerDynamicPoolInUse(): boolean {
188 return Configuration.buildWorkerSection().processType === WorkerProcessType.dynamicPool;
189 }
190
5d049829 191 private static buildUIServerSection(): UIServerConfiguration {
f74e97ac 192 if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
66271092 193 console.error(
c5e52a07 194 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
974efe6c 195 `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`,
5edd8ba0 196 )}`,
66271092
JB
197 );
198 }
675fa8e3 199 let uiServerConfiguration: UIServerConfiguration = {
b803eefa 200 enabled: false,
1f7fa4de 201 type: ApplicationProtocol.WS,
c127bd64 202 options: {
adbddcb4
JB
203 host: Constants.DEFAULT_UI_SERVER_HOST,
204 port: Constants.DEFAULT_UI_SERVER_PORT,
c127bd64 205 },
6a49ad23 206 };
f74e97ac 207 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
598c886d
JB
208 uiServerConfiguration = merge<UIServerConfiguration>(
209 uiServerConfiguration,
f74e97ac 210 Configuration.getConfigurationData()!.uiServer!,
598c886d 211 );
6a49ad23 212 }
9bf0ef23 213 if (isCFEnvironment() === true) {
72092cfc 214 delete uiServerConfiguration.options?.host;
e1d9a0f4 215 uiServerConfiguration.options!.port = parseInt(process.env.PORT!);
b803eefa 216 }
5d049829 217 return uiServerConfiguration;
6a49ad23
JB
218 }
219
5d049829 220 private static buildPerformanceStorageSection(): StorageConfiguration {
974efe6c
JB
221 Configuration.warnDeprecatedConfigurationKey(
222 'URI',
223 ConfigurationSection.performanceStorage,
224 "Use 'uri' instead",
225 );
6a49ad23
JB
226 let storageConfiguration: StorageConfiguration = {
227 enabled: false,
228 type: StorageType.JSON_FILE,
f0855167 229 uri: Configuration.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
6a49ad23 230 };
f74e97ac 231 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 232 storageConfiguration = {
1ba1e8fb 233 ...storageConfiguration,
f74e97ac
JB
234 ...Configuration.getConfigurationData()?.performanceStorage,
235 ...(Configuration.getConfigurationData()?.performanceStorage?.type ===
236 StorageType.JSON_FILE &&
237 Configuration.getConfigurationData()?.performanceStorage?.uri && {
e8044a69 238 uri: Configuration.buildPerformanceUriFilePath(
f74e97ac 239 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname,
e8044a69 240 ),
f682b2dc 241 }),
72f041bd 242 };
72f041bd 243 }
5d049829 244 return storageConfiguration;
7dde0b73
JB
245 }
246
5d049829 247 private static buildLogSection(): LogConfiguration {
3d48c1c1
JB
248 Configuration.warnDeprecatedConfigurationKey(
249 'logEnabled',
250 undefined,
974efe6c 251 `Use '${ConfigurationSection.log}' section to define the logging enablement instead`,
3d48c1c1
JB
252 );
253 Configuration.warnDeprecatedConfigurationKey(
254 'logFile',
255 undefined,
974efe6c 256 `Use '${ConfigurationSection.log}' section to define the log file instead`,
3d48c1c1
JB
257 );
258 Configuration.warnDeprecatedConfigurationKey(
259 'logErrorFile',
260 undefined,
974efe6c 261 `Use '${ConfigurationSection.log}' section to define the log error file instead`,
3d48c1c1
JB
262 );
263 Configuration.warnDeprecatedConfigurationKey(
264 'logConsole',
265 undefined,
974efe6c 266 `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`,
3d48c1c1
JB
267 );
268 Configuration.warnDeprecatedConfigurationKey(
269 'logStatisticsInterval',
270 undefined,
974efe6c 271 `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`,
3d48c1c1
JB
272 );
273 Configuration.warnDeprecatedConfigurationKey(
274 'logLevel',
275 undefined,
974efe6c 276 `Use '${ConfigurationSection.log}' section to define the log level instead`,
3d48c1c1
JB
277 );
278 Configuration.warnDeprecatedConfigurationKey(
279 'logFormat',
280 undefined,
974efe6c 281 `Use '${ConfigurationSection.log}' section to define the log format instead`,
3d48c1c1
JB
282 );
283 Configuration.warnDeprecatedConfigurationKey(
284 'logRotate',
285 undefined,
974efe6c 286 `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`,
3d48c1c1
JB
287 );
288 Configuration.warnDeprecatedConfigurationKey(
289 'logMaxFiles',
290 undefined,
974efe6c 291 `Use '${ConfigurationSection.log}' section to define the log maximum files instead`,
3d48c1c1
JB
292 );
293 Configuration.warnDeprecatedConfigurationKey(
294 'logMaxSize',
295 undefined,
974efe6c 296 `Use '${ConfigurationSection.log}' section to define the log maximum size instead`,
3d48c1c1
JB
297 );
298 const defaultLogConfiguration: LogConfiguration = {
299 enabled: true,
300 file: 'logs/combined.log',
301 errorFile: 'logs/error.log',
302 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
303 level: 'info',
304 format: 'simple',
305 rotate: true,
306 };
307 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac
JB
308 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
309 enabled: Configuration.getConfigurationData()?.logEnabled,
3d48c1c1 310 }),
f74e97ac
JB
311 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
312 file: Configuration.getConfigurationData()?.logFile,
3d48c1c1 313 }),
f74e97ac
JB
314 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
315 errorFile: Configuration.getConfigurationData()?.logErrorFile,
3d48c1c1 316 }),
f74e97ac
JB
317 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
318 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval,
3d48c1c1 319 }),
f74e97ac
JB
320 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
321 level: Configuration.getConfigurationData()?.logLevel,
3d48c1c1 322 }),
f74e97ac
JB
323 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
324 console: Configuration.getConfigurationData()?.logConsole,
3d48c1c1 325 }),
f74e97ac
JB
326 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
327 format: Configuration.getConfigurationData()?.logFormat,
3d48c1c1 328 }),
f74e97ac
JB
329 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
330 rotate: Configuration.getConfigurationData()?.logRotate,
3d48c1c1 331 }),
f74e97ac
JB
332 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
333 maxFiles: Configuration.getConfigurationData()?.logMaxFiles,
3d48c1c1 334 }),
f74e97ac
JB
335 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
336 maxSize: Configuration.getConfigurationData()?.logMaxSize,
3d48c1c1
JB
337 }),
338 };
339 const logConfiguration: LogConfiguration = {
340 ...defaultLogConfiguration,
341 ...deprecatedLogConfiguration,
f74e97ac
JB
342 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
343 Configuration.getConfigurationData()?.log),
3d48c1c1 344 };
5d049829 345 return logConfiguration;
3d48c1c1
JB
346 }
347
5d049829 348 private static buildWorkerSection(): WorkerConfiguration {
e7aeea18 349 Configuration.warnDeprecatedConfigurationKey(
e80bc579 350 'useWorkerPool',
1895299d 351 undefined,
974efe6c 352 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
cf2a5d9b
JB
353 );
354 Configuration.warnDeprecatedConfigurationKey(
355 'workerProcess',
1895299d 356 undefined,
974efe6c 357 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
cf2a5d9b
JB
358 );
359 Configuration.warnDeprecatedConfigurationKey(
360 'workerStartDelay',
1895299d 361 undefined,
974efe6c 362 `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`,
cf2a5d9b
JB
363 );
364 Configuration.warnDeprecatedConfigurationKey(
365 'chargingStationsPerWorker',
1895299d 366 undefined,
974efe6c 367 `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`,
cf2a5d9b
JB
368 );
369 Configuration.warnDeprecatedConfigurationKey(
370 'elementStartDelay',
1895299d 371 undefined,
974efe6c 372 `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead`,
cf2a5d9b
JB
373 );
374 Configuration.warnDeprecatedConfigurationKey(
375 'workerPoolMinSize',
1895299d 376 undefined,
974efe6c 377 `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`,
e7aeea18 378 );
e7aeea18
JB
379 Configuration.warnDeprecatedConfigurationKey(
380 'workerPoolSize;',
1895299d 381 undefined,
974efe6c 382 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
e7aeea18 383 );
cf2a5d9b
JB
384 Configuration.warnDeprecatedConfigurationKey(
385 'workerPoolMaxSize;',
1895299d 386 undefined,
974efe6c 387 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
cf2a5d9b
JB
388 );
389 Configuration.warnDeprecatedConfigurationKey(
390 'workerPoolStrategy;',
1895299d 391 undefined,
974efe6c 392 `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`,
cf2a5d9b 393 );
3d48c1c1
JB
394 const defaultWorkerConfiguration: WorkerConfiguration = {
395 processType: WorkerProcessType.workerSet,
396 startDelay: WorkerConstants.DEFAULT_WORKER_START_DELAY,
8603c1ca 397 elementsPerWorker: 'auto',
3d48c1c1
JB
398 elementStartDelay: WorkerConstants.DEFAULT_ELEMENT_START_DELAY,
399 poolMinSize: WorkerConstants.DEFAULT_POOL_MIN_SIZE,
400 poolMaxSize: WorkerConstants.DEFAULT_POOL_MAX_SIZE,
3d48c1c1 401 };
f74e97ac
JB
402 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
403 delete Configuration.getConfigurationData()?.workerPoolStrategy;
3d48c1c1 404 const deprecatedWorkerConfiguration: WorkerConfiguration = {
f74e97ac
JB
405 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
406 processType: Configuration.getConfigurationData()?.workerProcess,
3d48c1c1 407 }),
f74e97ac
JB
408 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
409 startDelay: Configuration.getConfigurationData()?.workerStartDelay,
3d48c1c1 410 }),
f74e97ac
JB
411 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
412 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker,
3d48c1c1 413 }),
f74e97ac
JB
414 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
415 elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay,
3d48c1c1 416 }),
f74e97ac
JB
417 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
418 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize,
3d48c1c1 419 }),
f74e97ac
JB
420 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
421 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize,
3d48c1c1 422 }),
3d48c1c1 423 };
eda9c451
JB
424 Configuration.warnDeprecatedConfigurationKey(
425 'poolStrategy',
974efe6c 426 ConfigurationSection.worker,
eda9c451
JB
427 'Not publicly exposed to end users',
428 );
3d48c1c1
JB
429 const workerConfiguration: WorkerConfiguration = {
430 ...defaultWorkerConfiguration,
431 ...deprecatedWorkerConfiguration,
f74e97ac
JB
432 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
433 Configuration.getConfigurationData()?.worker),
cf2a5d9b 434 };
b5b2c3e8
JB
435 if (!Object.values(WorkerProcessType).includes(workerConfiguration.processType!)) {
436 throw new SyntaxError(
437 `Invalid worker process type '${workerConfiguration.processType}' defined in configuration`,
438 );
439 }
5d049829 440 return workerConfiguration;
7dde0b73 441 }
eb3937cb 442
8b7072dc 443 private static logPrefix = (): string => {
14ecae6a 444 return `${new Date().toLocaleString()} Simulator configuration |`;
8b7072dc 445 };
23132a44 446
e7aeea18
JB
447 private static warnDeprecatedConfigurationKey(
448 key: string,
449 sectionName?: string,
5edd8ba0 450 logMsgToAppend = '',
e7aeea18 451 ) {
e7aeea18
JB
452 if (
453 sectionName &&
a37fc6dc 454 !isUndefined(Configuration.getConfigurationData()![sectionName as keyof ConfigurationData]) &&
f74e97ac 455 !isUndefined(
a37fc6dc
JB
456 (
457 Configuration.getConfigurationData()![sectionName as keyof ConfigurationData] as Record<
458 string,
459 unknown
460 >
461 )[key],
f74e97ac 462 )
e7aeea18
JB
463 ) {
464 console.error(
c5e52a07
JB
465 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
466 `Deprecated configuration key '${key}' usage in section '${sectionName}'${
467 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
468 }`,
469 )}`,
e7aeea18 470 );
a37fc6dc
JB
471 } else if (
472 !isUndefined(Configuration.getConfigurationData()![key as keyof ConfigurationData])
473 ) {
e7aeea18 474 console.error(
c5e52a07
JB
475 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
476 `Deprecated configuration key '${key}' usage${
477 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
478 }`,
479 )}`,
e7aeea18 480 );
eb3937cb
JB
481 }
482 }
483
c7db8ecb 484 private static getConfigurationData(): ConfigurationData | undefined {
f74e97ac 485 if (!Configuration.configurationData) {
23132a44 486 try {
f74e97ac 487 Configuration.configurationData = JSON.parse(
5edd8ba0 488 readFileSync(Configuration.configurationFile, 'utf8'),
e7aeea18 489 ) as ConfigurationData;
c96b764e
JB
490 if (!Configuration.configurationFileWatcher) {
491 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
492 }
23132a44 493 } catch (error) {
69074173 494 Configuration.handleFileException(
a95873d8 495 Configuration.configurationFile,
7164966d
JB
496 FileType.Configuration,
497 error as NodeJS.ErrnoException,
5edd8ba0 498 Configuration.logPrefix(),
e7aeea18 499 );
23132a44 500 }
eb3937cb 501 }
f74e97ac 502 return Configuration.configurationData;
eb3937cb 503 }
963ee397 504
d972af76 505 private static getConfigurationFileWatcher(): FSWatcher | undefined {
23132a44 506 try {
d972af76 507 return watch(Configuration.configurationFile, (event, filename): void => {
e1d9a0f4 508 if (filename!.trim()!.length > 0 && event === 'change') {
c7db8ecb 509 delete Configuration.configurationData;
974efe6c 510 Configuration.configurationSectionCache.clear();
9bf0ef23 511 if (!isUndefined(Configuration.configurationChangeCallback)) {
0e8e6cbe 512 Configuration.configurationChangeCallback!().catch((error) => {
dcaf96dc
JB
513 throw typeof error === 'string' ? new Error(error) : error;
514 });
3ec10737 515 }
23132a44
JB
516 }
517 });
518 } catch (error) {
69074173 519 Configuration.handleFileException(
a95873d8 520 Configuration.configurationFile,
7164966d
JB
521 FileType.Configuration,
522 error as NodeJS.ErrnoException,
5edd8ba0 523 Configuration.logPrefix(),
e7aeea18 524 );
23132a44 525 }
ded13d97
JB
526 }
527
69074173
JB
528 private static handleFileException(
529 file: string,
530 fileType: FileType,
531 error: NodeJS.ErrnoException,
5edd8ba0 532 logPrefix: string,
69074173 533 ): void {
9bf0ef23 534 const prefix = isNotEmptyString(logPrefix) ? `${logPrefix} ` : '';
69074173
JB
535 let logMsg: string;
536 switch (error.code) {
537 case 'ENOENT':
538 logMsg = `${fileType} file ${file} not found:`;
539 break;
540 case 'EEXIST':
541 logMsg = `${fileType} file ${file} already exists:`;
542 break;
543 case 'EACCES':
544 logMsg = `${fileType} file ${file} access denied:`;
545 break;
7b5dbe91
JB
546 case 'EPERM':
547 logMsg = `${fileType} file ${file} permission denied:`;
548 break;
69074173
JB
549 default:
550 logMsg = `${fileType} file ${file} error:`;
551 }
7b5dbe91
JB
552 console.error(`${chalk.green(prefix)}${chalk.red(`${logMsg} `)}`, error);
553 throw error;
69074173
JB
554 }
555
1f5df42a 556 private static getDefaultPerformanceStorageUri(storageType: StorageType) {
d5603918
JB
557 switch (storageType) {
558 case StorageType.JSON_FILE:
e8044a69 559 return Configuration.buildPerformanceUriFilePath(
5edd8ba0 560 `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}`,
e8044a69 561 );
d5603918 562 case StorageType.SQLITE:
e8044a69 563 return Configuration.buildPerformanceUriFilePath(
5edd8ba0 564 `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`,
e8044a69 565 );
d5603918
JB
566 default:
567 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);
568 }
569 }
e8044a69
JB
570
571 private static buildPerformanceUriFilePath(file: string) {
d972af76 572 return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}`;
e8044a69 573 }
7dde0b73 574}