open-collaboration-server
Version:
Open Collaboration Server implementation, part of the Open Collaboration Tools project
150 lines (126 loc) • 5.28 kB
text/typescript
// ******************************************************************************
// Copyright 2024 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.
// ******************************************************************************
import * as fs from 'node:fs';
import * as path from 'path';
import { inject, injectable, optional, postConstruct } from 'inversify';
import { parse as parseYaml } from 'yaml';
export interface Configuration {
/**
* Get a string value for the given configuration key.
* The key must be provided in kebab-case.
*/
getValue(key: string, type?: 'string'): string | undefined;
/**
* Get a number value for the given configuration key.
* The key must be provided in kebab-case.
*/
getValue(key: string, type: 'number'): number | undefined;
/**
* Get a boolean value for the given configuration key.
* The key must be provided in kebab-case.
*/
getValue(key: string, type: 'boolean'): boolean | undefined;
}
export const Configuration = Symbol('Configuration');
export const ConfigurationFile = Symbol('ConfigurationFile');
export type ConfigurationValue = string | number | boolean;
()
export class DefaultConfiguration implements Configuration {
(ConfigurationFile)() protected configurationFile: string | undefined;
protected configuration: Record<string, unknown> = {};
()
protected initialize() {
if (this.configurationFile) {
const ext = path.extname(this.configurationFile).toLowerCase();
const configContent = fs.readFileSync(this.configurationFile, 'utf8');
let config: unknown;
if (ext === '.yml' || ext === '.yaml') {
config = parseYaml(configContent);
} else {
config = JSON.parse(configContent);
}
if (typeof config === 'object' && config !== null) {
this.configuration = config as Record<string, unknown>;
}
}
}
getValue(key: string, type?: 'string'): string | undefined;
getValue(key: string, type: 'number'): number | undefined;
getValue(key: string, type: 'boolean'): boolean | undefined;
getValue(key: string, type?: 'string' | 'number' | 'boolean'): ConfigurationValue | undefined {
let value: ConfigurationValue | undefined = this.getFromEnv(key);
if (!value) {
value = this.getFromConfig(key);
}
return this.convertToType(value, type);
}
protected convertToType(value: ConfigurationValue | undefined, type?: 'string' | 'number' | 'boolean'): ConfigurationValue | undefined {
if (value === undefined) {
return undefined;
}
if (type === 'string' || type === undefined) {
return String(value);
} else if (type === 'number') {
if (typeof value === 'string') {
const parsed = parseInt(value, 10);
if (!isNaN(parsed)) {
return parsed;
}
} else if (typeof value === 'number') {
return value;
}
} else if (type === 'boolean') {
if (typeof value === 'string') {
const lower = value.toLowerCase();
switch (lower) {
case 'true':
return true;
case 'false':
return false;
}
} else if (typeof value === 'boolean') {
return value;
}
}
return undefined;
}
protected getFromEnv(key: string): string | undefined {
return process.env[toEnvKey(key)];
}
protected getFromConfig(key: string): ConfigurationValue | undefined {
return this.getFromConfigObject(this.configuration, toObjKey(key));
}
private getFromConfigObject(object: Record<string, unknown>, key: string): ConfigurationValue | undefined {
let value = object[key];
if (isConfigValue(value)) {
return value;
}
const segments = key.split('.');
for (let i = segments.length - 1; i >= 1; i--) {
const key = segments.slice(0, i).join('.');
value = object[key];
if (typeof value === 'object' && value !== null) {
return this.getFromConfigObject(value as Record<string, unknown>, segments.slice(i).join('.'));
}
}
return undefined;
}
}
/**
* Converts a kebab-case key to a SNAKE_CASE key.
*/
function toEnvKey(key: string): string {
return key.replace(/-/g, '_').toUpperCase();
}
/**
* Converts a kebab-case key to a dot.separated.key.
*/
function toObjKey(key: string): string {
return key.replace(/-/g, '.');
}
function isConfigValue(value: unknown): value is ConfigurationValue {
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
}