build(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / utils / Configuration.ts
CommitLineData
d972af76
JB
1import { type FSWatcher, readFileSync, watch } from 'node:fs';
2import { dirname, join, resolve } from 'node:path';
130783a7 3import { fileURLToPath } from 'node:url';
8114d10e
JB
4
5import chalk from 'chalk';
088ee3c1 6import merge from 'just-merge';
8114d10e 7
878e026c 8import { Constants } from './Constants';
9bf0ef23 9import { hasOwnProp, isCFEnvironment, isNotEmptyString, isUndefined } from './Utils';
83e00df1 10import {
268a74bb 11 ApplicationProtocol,
83e00df1 12 type ConfigurationData,
5d049829 13 ConfigurationSection,
268a74bb 14 FileType,
3d48c1c1 15 type LogConfiguration,
83e00df1
JB
16 type StationTemplateUrl,
17 type StorageConfiguration,
268a74bb 18 StorageType,
e7aeea18 19 SupervisionUrlDistribution,
83e00df1
JB
20 type UIServerConfiguration,
21 type WorkerConfiguration,
268a74bb 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
77 public static getAutoReconnectMaxRetries(): number | undefined {
5d049829
JB
78 if (hasOwnProp(Configuration.getConfigurationData(), 'autoReconnectMaxRetries')) {
79 return Configuration.getConfigurationData()?.autoReconnectMaxRetries;
80 }
81 }
82
83 public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
3602e107 84 Configuration.checkDeprecatedConfigurationKeys();
5d049829
JB
85 return Configuration.getConfigurationData()?.stationTemplateUrls;
86 }
87
5d049829 88 public static getSupervisionUrls(): string | string[] | undefined {
a37fc6dc
JB
89 if (
90 !isUndefined(
91 Configuration.getConfigurationData()!['supervisionURLs' as keyof ConfigurationData],
92 )
93 ) {
5d049829 94 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
a37fc6dc 95 'supervisionURLs' as keyof ConfigurationData
5d049829
JB
96 ] as string | string[];
97 }
98 return Configuration.getConfigurationData()?.supervisionUrls;
99 }
100
101 public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
5d049829
JB
102 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
103 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
104 : SupervisionUrlDistribution.ROUND_ROBIN;
105 }
106
c831d2bc
JB
107 public static workerPoolInUse(): boolean {
108 return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
eb979012
JB
109 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
110 .processType!,
c831d2bc
JB
111 );
112 }
113
114 public static workerDynamicPoolInUse(): boolean {
eb979012
JB
115 return (
116 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
117 .processType === WorkerProcessType.dynamicPool
118 );
c831d2bc
JB
119 }
120
81b9a105
JB
121 private static isConfigurationSectionCached(sectionName: ConfigurationSection): boolean {
122 return Configuration.configurationSectionCache.has(sectionName);
123 }
124
125 private static cacheConfigurationSection(sectionName: ConfigurationSection): void {
126 switch (sectionName) {
127 case ConfigurationSection.log:
128 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection());
129 break;
130 case ConfigurationSection.performanceStorage:
131 Configuration.configurationSectionCache.set(
132 sectionName,
133 Configuration.buildPerformanceStorageSection(),
134 );
135 break;
136 case ConfigurationSection.worker:
137 Configuration.configurationSectionCache.set(
138 sectionName,
139 Configuration.buildWorkerSection(),
140 );
141 break;
142 case ConfigurationSection.uiServer:
143 Configuration.configurationSectionCache.set(
144 sectionName,
145 Configuration.buildUIServerSection(),
146 );
147 break;
148 default:
149 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
150 throw new Error(`Unknown configuration section '${sectionName}'`);
151 }
152 }
153
5d049829 154 private static buildUIServerSection(): UIServerConfiguration {
675fa8e3 155 let uiServerConfiguration: UIServerConfiguration = {
b803eefa 156 enabled: false,
1f7fa4de 157 type: ApplicationProtocol.WS,
c127bd64 158 options: {
adbddcb4
JB
159 host: Constants.DEFAULT_UI_SERVER_HOST,
160 port: Constants.DEFAULT_UI_SERVER_PORT,
c127bd64 161 },
6a49ad23 162 };
f74e97ac 163 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
598c886d
JB
164 uiServerConfiguration = merge<UIServerConfiguration>(
165 uiServerConfiguration,
f74e97ac 166 Configuration.getConfigurationData()!.uiServer!,
598c886d 167 );
6a49ad23 168 }
9bf0ef23 169 if (isCFEnvironment() === true) {
72092cfc 170 delete uiServerConfiguration.options?.host;
e1d9a0f4 171 uiServerConfiguration.options!.port = parseInt(process.env.PORT!);
b803eefa 172 }
5d049829 173 return uiServerConfiguration;
6a49ad23
JB
174 }
175
5d049829 176 private static buildPerformanceStorageSection(): StorageConfiguration {
6a49ad23
JB
177 let storageConfiguration: StorageConfiguration = {
178 enabled: false,
179 type: StorageType.JSON_FILE,
f0855167 180 uri: Configuration.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
6a49ad23 181 };
f74e97ac 182 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 183 storageConfiguration = {
1ba1e8fb 184 ...storageConfiguration,
f74e97ac
JB
185 ...Configuration.getConfigurationData()?.performanceStorage,
186 ...(Configuration.getConfigurationData()?.performanceStorage?.type ===
187 StorageType.JSON_FILE &&
188 Configuration.getConfigurationData()?.performanceStorage?.uri && {
e8044a69 189 uri: Configuration.buildPerformanceUriFilePath(
f74e97ac 190 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname,
e8044a69 191 ),
f682b2dc 192 }),
72f041bd 193 };
72f041bd 194 }
5d049829 195 return storageConfiguration;
7dde0b73
JB
196 }
197
5d049829 198 private static buildLogSection(): LogConfiguration {
3d48c1c1
JB
199 const defaultLogConfiguration: LogConfiguration = {
200 enabled: true,
201 file: 'logs/combined.log',
202 errorFile: 'logs/error.log',
203 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
204 level: 'info',
205 format: 'simple',
206 rotate: true,
207 };
208 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac
JB
209 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
210 enabled: Configuration.getConfigurationData()?.logEnabled,
3d48c1c1 211 }),
f74e97ac
JB
212 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
213 file: Configuration.getConfigurationData()?.logFile,
3d48c1c1 214 }),
f74e97ac
JB
215 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
216 errorFile: Configuration.getConfigurationData()?.logErrorFile,
3d48c1c1 217 }),
f74e97ac
JB
218 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
219 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval,
3d48c1c1 220 }),
f74e97ac
JB
221 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
222 level: Configuration.getConfigurationData()?.logLevel,
3d48c1c1 223 }),
f74e97ac
JB
224 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
225 console: Configuration.getConfigurationData()?.logConsole,
3d48c1c1 226 }),
f74e97ac
JB
227 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
228 format: Configuration.getConfigurationData()?.logFormat,
3d48c1c1 229 }),
f74e97ac
JB
230 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
231 rotate: Configuration.getConfigurationData()?.logRotate,
3d48c1c1 232 }),
f74e97ac
JB
233 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
234 maxFiles: Configuration.getConfigurationData()?.logMaxFiles,
3d48c1c1 235 }),
f74e97ac
JB
236 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
237 maxSize: Configuration.getConfigurationData()?.logMaxSize,
3d48c1c1
JB
238 }),
239 };
240 const logConfiguration: LogConfiguration = {
241 ...defaultLogConfiguration,
242 ...deprecatedLogConfiguration,
f74e97ac
JB
243 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
244 Configuration.getConfigurationData()?.log),
3d48c1c1 245 };
5d049829 246 return logConfiguration;
3d48c1c1
JB
247 }
248
5d049829 249 private static buildWorkerSection(): WorkerConfiguration {
3602e107
JB
250 const defaultWorkerConfiguration: WorkerConfiguration = {
251 processType: WorkerProcessType.workerSet,
252 startDelay: DEFAULT_WORKER_START_DELAY,
253 elementsPerWorker: 'auto',
254 elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
255 poolMinSize: DEFAULT_POOL_MIN_SIZE,
256 poolMaxSize: DEFAULT_POOL_MAX_SIZE,
257 };
258 const deprecatedWorkerConfiguration: WorkerConfiguration = {
259 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
260 processType: Configuration.getConfigurationData()?.workerProcess,
261 }),
262 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
263 startDelay: Configuration.getConfigurationData()?.workerStartDelay,
264 }),
265 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
266 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker,
267 }),
268 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
269 elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay,
270 }),
271 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
272 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize,
273 }),
274 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
275 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize,
276 }),
277 };
278 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
279 delete Configuration.getConfigurationData()?.workerPoolStrategy;
280 const workerConfiguration: WorkerConfiguration = {
281 ...defaultWorkerConfiguration,
282 ...deprecatedWorkerConfiguration,
283 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
284 Configuration.getConfigurationData()?.worker),
285 };
286 if (!Object.values(WorkerProcessType).includes(workerConfiguration.processType!)) {
287 throw new SyntaxError(
288 `Invalid worker process type '${workerConfiguration.processType}' defined in configuration`,
289 );
290 }
291 return workerConfiguration;
292 }
293
294 private static logPrefix = (): string => {
295 return `${new Date().toLocaleString()} Simulator configuration |`;
296 };
297
298 private static checkDeprecatedConfigurationKeys() {
299 if (Configuration.warnDeprecatedConfigurationKeys) {
300 return;
301 }
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(
326 Configuration.getConfigurationData()!['stationTemplateURLs' as keyof ConfigurationData],
327 ) &&
328 (Configuration.getConfigurationData()!.stationTemplateUrls =
329 Configuration.getConfigurationData()![
330 'stationTemplateURLs' as keyof ConfigurationData
331 ] as StationTemplateUrl[]);
332 Configuration.getConfigurationData()!.stationTemplateUrls.forEach(
333 (stationTemplateUrl: StationTemplateUrl) => {
334 if (!isUndefined(stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl])) {
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 }
3602e107 476 Configuration.warnDeprecatedConfigurationKeys = true;
7dde0b73 477 }
eb3937cb 478
e7aeea18
JB
479 private static warnDeprecatedConfigurationKey(
480 key: string,
481 sectionName?: string,
5edd8ba0 482 logMsgToAppend = '',
e7aeea18 483 ) {
e7aeea18
JB
484 if (
485 sectionName &&
a37fc6dc 486 !isUndefined(Configuration.getConfigurationData()![sectionName as keyof ConfigurationData]) &&
f74e97ac 487 !isUndefined(
a37fc6dc
JB
488 (
489 Configuration.getConfigurationData()![sectionName as keyof ConfigurationData] as Record<
490 string,
491 unknown
492 >
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
JB
503 } else if (
504 !isUndefined(Configuration.getConfigurationData()![key as keyof ConfigurationData])
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}