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