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