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