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