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