refactor: use classic setter in configuration class
[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
d5bd1c00
JB
63 private constructor() {
64 // This is intentional
65 }
66
9f139e99 67 public static set configurationChangeCallback(cb: () => Promise<void>) {
e57acf6a
JB
68 Configuration.configurationChangeCallback = cb;
69 }
7dde0b73 70
e7c0fce0
JB
71 public static getConfigurationSection<T extends ConfigurationSectionType>(
72 sectionName: ConfigurationSection,
73 ): T {
81b9a105
JB
74 if (!Configuration.isConfigurationSectionCached(sectionName)) {
75 Configuration.cacheConfigurationSection(sectionName);
c1c97db8 76 }
5d049829
JB
77 return Configuration.configurationSectionCache.get(sectionName) as T;
78 }
79
5d049829 80 public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
5f742aac
JB
81 const checkDeprecatedConfigurationKeysOnce = once(
82 Configuration.checkDeprecatedConfigurationKeys.bind(Configuration),
0898a067 83 Configuration,
5f742aac
JB
84 );
85 checkDeprecatedConfigurationKeysOnce();
5d049829
JB
86 return Configuration.getConfigurationData()?.stationTemplateUrls;
87 }
88
5d049829 89 public static getSupervisionUrls(): string | string[] | undefined {
a37fc6dc
JB
90 if (
91 !isUndefined(
1c9de2b9 92 Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData],
a37fc6dc
JB
93 )
94 ) {
5d049829 95 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
a37fc6dc 96 'supervisionURLs' as keyof ConfigurationData
5d049829
JB
97 ] as string | string[];
98 }
99 return Configuration.getConfigurationData()?.supervisionUrls;
100 }
101
102 public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
5d049829
JB
103 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
104 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
105 : SupervisionUrlDistribution.ROUND_ROBIN;
106 }
107
c831d2bc
JB
108 public static workerPoolInUse(): boolean {
109 return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
eb979012
JB
110 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
111 .processType!,
c831d2bc
JB
112 );
113 }
114
115 public static workerDynamicPoolInUse(): boolean {
eb979012
JB
116 return (
117 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
118 .processType === WorkerProcessType.dynamicPool
119 );
c831d2bc
JB
120 }
121
a418c77b
JB
122 private static logPrefix = (): string => {
123 return logPrefix(' Simulator configuration |');
124 };
125
81b9a105
JB
126 private static isConfigurationSectionCached(sectionName: ConfigurationSection): boolean {
127 return Configuration.configurationSectionCache.has(sectionName);
128 }
129
130 private static cacheConfigurationSection(sectionName: ConfigurationSection): void {
131 switch (sectionName) {
132 case ConfigurationSection.log:
133 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection());
134 break;
135 case ConfigurationSection.performanceStorage:
136 Configuration.configurationSectionCache.set(
137 sectionName,
138 Configuration.buildPerformanceStorageSection(),
139 );
140 break;
141 case ConfigurationSection.worker:
142 Configuration.configurationSectionCache.set(
143 sectionName,
144 Configuration.buildWorkerSection(),
145 );
146 break;
147 case ConfigurationSection.uiServer:
148 Configuration.configurationSectionCache.set(
149 sectionName,
150 Configuration.buildUIServerSection(),
151 );
152 break;
153 default:
154 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
155 throw new Error(`Unknown configuration section '${sectionName}'`);
156 }
157 }
158
5d049829 159 private static buildUIServerSection(): UIServerConfiguration {
675fa8e3 160 let uiServerConfiguration: UIServerConfiguration = {
b803eefa 161 enabled: false,
1f7fa4de 162 type: ApplicationProtocol.WS,
c127bd64 163 options: {
adbddcb4
JB
164 host: Constants.DEFAULT_UI_SERVER_HOST,
165 port: Constants.DEFAULT_UI_SERVER_PORT,
c127bd64 166 },
6a49ad23 167 };
f74e97ac 168 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
598c886d
JB
169 uiServerConfiguration = merge<UIServerConfiguration>(
170 uiServerConfiguration,
f74e97ac 171 Configuration.getConfigurationData()!.uiServer!,
598c886d 172 );
6a49ad23 173 }
9bf0ef23 174 if (isCFEnvironment() === true) {
72092cfc 175 delete uiServerConfiguration.options?.host;
e1d9a0f4 176 uiServerConfiguration.options!.port = parseInt(process.env.PORT!);
b803eefa 177 }
5d049829 178 return uiServerConfiguration;
6a49ad23
JB
179 }
180
5d049829 181 private static buildPerformanceStorageSection(): StorageConfiguration {
6a49ad23
JB
182 let storageConfiguration: StorageConfiguration = {
183 enabled: false,
184 type: StorageType.JSON_FILE,
f0855167 185 uri: Configuration.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
6a49ad23 186 };
f74e97ac 187 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 188 storageConfiguration = {
1ba1e8fb 189 ...storageConfiguration,
f74e97ac
JB
190 ...Configuration.getConfigurationData()?.performanceStorage,
191 ...(Configuration.getConfigurationData()?.performanceStorage?.type ===
192 StorageType.JSON_FILE &&
193 Configuration.getConfigurationData()?.performanceStorage?.uri && {
e8044a69 194 uri: Configuration.buildPerformanceUriFilePath(
f74e97ac 195 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname,
e8044a69 196 ),
f682b2dc 197 }),
72f041bd 198 };
72f041bd 199 }
5d049829 200 return storageConfiguration;
7dde0b73
JB
201 }
202
5d049829 203 private static buildLogSection(): LogConfiguration {
3d48c1c1
JB
204 const defaultLogConfiguration: LogConfiguration = {
205 enabled: true,
206 file: 'logs/combined.log',
207 errorFile: 'logs/error.log',
208 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
209 level: 'info',
210 format: 'simple',
211 rotate: true,
212 };
213 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac
JB
214 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
215 enabled: Configuration.getConfigurationData()?.logEnabled,
3d48c1c1 216 }),
f74e97ac
JB
217 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
218 file: Configuration.getConfigurationData()?.logFile,
3d48c1c1 219 }),
f74e97ac
JB
220 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
221 errorFile: Configuration.getConfigurationData()?.logErrorFile,
3d48c1c1 222 }),
f74e97ac
JB
223 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
224 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval,
3d48c1c1 225 }),
f74e97ac
JB
226 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
227 level: Configuration.getConfigurationData()?.logLevel,
3d48c1c1 228 }),
f74e97ac
JB
229 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
230 console: Configuration.getConfigurationData()?.logConsole,
3d48c1c1 231 }),
f74e97ac
JB
232 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
233 format: Configuration.getConfigurationData()?.logFormat,
3d48c1c1 234 }),
f74e97ac
JB
235 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
236 rotate: Configuration.getConfigurationData()?.logRotate,
3d48c1c1 237 }),
f74e97ac
JB
238 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
239 maxFiles: Configuration.getConfigurationData()?.logMaxFiles,
3d48c1c1 240 }),
f74e97ac
JB
241 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
242 maxSize: Configuration.getConfigurationData()?.logMaxSize,
3d48c1c1
JB
243 }),
244 };
245 const logConfiguration: LogConfiguration = {
246 ...defaultLogConfiguration,
247 ...deprecatedLogConfiguration,
f74e97ac
JB
248 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
249 Configuration.getConfigurationData()?.log),
3d48c1c1 250 };
5d049829 251 return logConfiguration;
3d48c1c1
JB
252 }
253
5d049829 254 private static buildWorkerSection(): WorkerConfiguration {
3602e107
JB
255 const defaultWorkerConfiguration: WorkerConfiguration = {
256 processType: WorkerProcessType.workerSet,
257 startDelay: DEFAULT_WORKER_START_DELAY,
258 elementsPerWorker: 'auto',
259 elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
260 poolMinSize: DEFAULT_POOL_MIN_SIZE,
261 poolMaxSize: DEFAULT_POOL_MAX_SIZE,
262 };
263 const deprecatedWorkerConfiguration: WorkerConfiguration = {
264 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
265 processType: Configuration.getConfigurationData()?.workerProcess,
266 }),
267 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
268 startDelay: Configuration.getConfigurationData()?.workerStartDelay,
269 }),
270 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
271 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker,
272 }),
273 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
274 elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay,
275 }),
276 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
277 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize,
278 }),
279 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
280 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize,
281 }),
282 };
283 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
284 delete Configuration.getConfigurationData()?.workerPoolStrategy;
285 const workerConfiguration: WorkerConfiguration = {
286 ...defaultWorkerConfiguration,
287 ...deprecatedWorkerConfiguration,
288 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
289 Configuration.getConfigurationData()?.worker),
290 };
291 if (!Object.values(WorkerProcessType).includes(workerConfiguration.processType!)) {
292 throw new SyntaxError(
293 `Invalid worker process type '${workerConfiguration.processType}' defined in configuration`,
294 );
295 }
296 return workerConfiguration;
297 }
298
3602e107 299 private static checkDeprecatedConfigurationKeys() {
3602e107
JB
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 }
7dde0b73 474 }
eb3937cb 475
e7aeea18
JB
476 private static warnDeprecatedConfigurationKey(
477 key: string,
478 sectionName?: string,
5edd8ba0 479 logMsgToAppend = '',
e7aeea18 480 ) {
e7aeea18
JB
481 if (
482 sectionName &&
1c9de2b9
JB
483 !isUndefined(
484 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData],
485 ) &&
f74e97ac 486 !isUndefined(
a37fc6dc 487 (
1c9de2b9 488 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] as Record<
a37fc6dc
JB
489 string,
490 unknown
491 >
1c9de2b9 492 )?.[key],
f74e97ac 493 )
e7aeea18
JB
494 ) {
495 console.error(
c5e52a07
JB
496 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
497 `Deprecated configuration key '${key}' usage in section '${sectionName}'${
498 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
499 }`,
500 )}`,
e7aeea18 501 );
a37fc6dc 502 } else if (
1c9de2b9 503 !isUndefined(Configuration.getConfigurationData()?.[key as keyof ConfigurationData])
a37fc6dc 504 ) {
e7aeea18 505 console.error(
c5e52a07
JB
506 `${chalk.green(Configuration.logPrefix())} ${chalk.red(
507 `Deprecated configuration key '${key}' usage${
508 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
509 }`,
510 )}`,
e7aeea18 511 );
eb3937cb
JB
512 }
513 }
514
c7db8ecb 515 private static getConfigurationData(): ConfigurationData | undefined {
f74e97ac 516 if (!Configuration.configurationData) {
23132a44 517 try {
f74e97ac 518 Configuration.configurationData = JSON.parse(
5edd8ba0 519 readFileSync(Configuration.configurationFile, 'utf8'),
e7aeea18 520 ) as ConfigurationData;
c96b764e
JB
521 if (!Configuration.configurationFileWatcher) {
522 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
523 }
23132a44 524 } catch (error) {
69074173 525 Configuration.handleFileException(
a95873d8 526 Configuration.configurationFile,
7164966d
JB
527 FileType.Configuration,
528 error as NodeJS.ErrnoException,
5edd8ba0 529 Configuration.logPrefix(),
e7aeea18 530 );
23132a44 531 }
eb3937cb 532 }
f74e97ac 533 return Configuration.configurationData;
eb3937cb 534 }
963ee397 535
d972af76 536 private static getConfigurationFileWatcher(): FSWatcher | undefined {
23132a44 537 try {
d972af76 538 return watch(Configuration.configurationFile, (event, filename): void => {
e1d9a0f4 539 if (filename!.trim()!.length > 0 && event === 'change') {
c7db8ecb 540 delete Configuration.configurationData;
974efe6c 541 Configuration.configurationSectionCache.clear();
9bf0ef23 542 if (!isUndefined(Configuration.configurationChangeCallback)) {
9f139e99 543 Configuration.configurationChangeCallback().catch((error) => {
dcaf96dc
JB
544 throw typeof error === 'string' ? new Error(error) : error;
545 });
3ec10737 546 }
23132a44
JB
547 }
548 });
549 } catch (error) {
69074173 550 Configuration.handleFileException(
a95873d8 551 Configuration.configurationFile,
7164966d
JB
552 FileType.Configuration,
553 error as NodeJS.ErrnoException,
5edd8ba0 554 Configuration.logPrefix(),
e7aeea18 555 );
23132a44 556 }
ded13d97
JB
557 }
558
69074173
JB
559 private static handleFileException(
560 file: string,
561 fileType: FileType,
562 error: NodeJS.ErrnoException,
a418c77b 563 logPfx: string,
69074173 564 ): void {
a418c77b 565 const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : '';
69074173
JB
566 let logMsg: string;
567 switch (error.code) {
568 case 'ENOENT':
5587f482 569 logMsg = `${fileType} file ${file} not found: `;
69074173
JB
570 break;
571 case 'EEXIST':
5587f482 572 logMsg = `${fileType} file ${file} already exists: `;
69074173
JB
573 break;
574 case 'EACCES':
5587f482 575 logMsg = `${fileType} file ${file} access denied: `;
69074173 576 break;
7b5dbe91 577 case 'EPERM':
5587f482 578 logMsg = `${fileType} file ${file} permission denied: `;
7b5dbe91 579 break;
69074173 580 default:
5587f482 581 logMsg = `${fileType} file ${file} error: `;
69074173 582 }
5587f482 583 console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error);
7b5dbe91 584 throw error;
69074173
JB
585 }
586
1f5df42a 587 private static getDefaultPerformanceStorageUri(storageType: StorageType) {
d5603918
JB
588 switch (storageType) {
589 case StorageType.JSON_FILE:
e8044a69 590 return Configuration.buildPerformanceUriFilePath(
5edd8ba0 591 `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}`,
e8044a69 592 );
d5603918 593 case StorageType.SQLITE:
e8044a69 594 return Configuration.buildPerformanceUriFilePath(
5edd8ba0 595 `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`,
e8044a69 596 );
d5603918 597 default:
f39ae767 598 throw new Error(`Unsupported storage type '${storageType}'`);
d5603918
JB
599 }
600 }
e8044a69
JB
601
602 private static buildPerformanceUriFilePath(file: string) {
d972af76 603 return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}`;
e8044a69 604 }
7dde0b73 605}