From c06527c073b81c4f979f7b29272f88d6c474a44c Mon Sep 17 00:00:00 2001 From: Diego Magdaleno Date: Sat, 22 May 2021 16:40:04 -0500 Subject: [PATCH 1/2] Config: New config and store classes to replace the mongo method --- package-lock.json | Bin 33324 -> 38728 bytes package.json | 3 + src/util/Config.ts | 141 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 111 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb58226c68f3ae4d943b1c098337d365e362e18e..711d6bb01c0ce453a15a1cd0fe053fa596805f9a 100644 GIT binary patch delta 3301 zcmd5;NsrrP7*;ydWu^-)GgDEurJZz>Fpjrm5?8b+i8EQ9-HsE9Qpa{;dlK)?2Bktm zs(M5pKL7~{aX^JpIDn9lxbZW}g$p}E;s6JnOfzk#MNp}u;zRW6eeEyLyFJhM@jH9I zxwGffTfP{t_WYO6`maR-p#bWCd2zve{G#r_hNTtU6efBMjmpHrval!f{vR+Q7H} z?9CQ$JFqB81}vE_-^AS8 zF~ul5j$vO$kgZ@q(7T9j=$frtHX<2%Q?jJdhN6LAp;Ke{;PHJ2W^I`i+P4OL1imtJ zdG(%|@EW*IoMW6sc;wVaTq-RMdqOx5$z?)U!t3c~3(2Qdwn)m|s6z~CZe*3vFpjhg zHJKaW$w7XQ959^_8z$(4Ssy8Sf!0|$NoPU=*xY;esA9urx>{HD zHpV85Ogy5$Kup`LRLkXffm3p-RKi3IaYc(s$|gNv+R;uwn{S{(3wKRk z3ng=I1&;#s$a(P5E2obk*`h{0 zEkP$7lS*U~dZa%olrJ*r9Jsyo#dACM4Qyg302D^N<#}`$J&2EU!x(sdaApcQPe+g< zsS%vY$?1lMb#n5?Ivu=&t$XYU+DmI$dIubGPdYeZxMMpHN={n9yAzjWCDbnVtOeRBA3sO+vJ~%^AmEMZ0cMv5Cr@( zy0k@?9)~p;RqNESx~NBL9ZXTM zij-0$Nk&ttR#D2~Et<=&jZixx30a$LS#dX)>1krD4elq-dNqq@7@J|dh^`gTEJ6Z&Mn?p{2Oo! BG~ECI delta 89 zcmV-f0H*)Qt^%xt0YZ5*&4vpYR^1+(@; v`3kcWP%a6xwOVWivp8Wt0h3{5>ys~LsConfig.data; - }, - setAll: function set(val: any) { - return db.collection("config").updateOne({}, { $set: val }, { upsert: true }); - }, -}; - -export const DefaultOptions = { - api: {}, - gateway: {}, - voice: {}, -}; - -export interface DefaultOptions extends Document { - api?: any; - gateway?: any; - voice?: any; +interface Options { + path: string; + schemaValidator: ValidateFunction; + schema: JSONSchemaType; } -export const ConfigSchema = new Schema({ - api: Object, - gateway: Object, - voice: Object, -}); +type Deserialize = (text: string) => T; + +function getConfigPath(name: string, configFileName: string, extension: string): string { + const configEnvPath = envPaths(name, { suffix: "" }).config; + const configPath = path.resolve(configEnvPath, `${configFileName}${extension}`) + return configPath +} + +class Store = Record> implements Iterable<[keyof T, T[keyof T]]>{ + readonly path: string; + readonly validator: ValidateFunction; + readonly schema: string; + + constructor(path: string, validator: ValidateFunction, schema: JSONSchemaType) { + this.validator = validator; + if (fs.existsSync(path)) { + this.path = path + } else { + this._ensureDirectory() + } + } + + private readonly _deserialize: Deserialize = value => JSON.parse(value); + + private _ensureDirectory(): void { + fs.mkdirSync(path.dirname(this.path), { recursive: true }) + } + + protected _validate(data: T | unknown): void { + const valid = this.validator(data); + if (valid || !this.validator.errors) { + return; + } + + const errors = this.validator.errors.map(({ instancePath, message = '' }) => `\`${instancePath.slice(1)}\` ${message}`); + throw new Error("The configuration schema was violated!: " + errors.join('; ')) + + } + + *[Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]> { + for (const [key, value] of Object.entries(this.store)) { + yield [key, value] + } + } + + public get store(): T { + try { + const data = fs.readFileSync(this.path).toString(); + const deserializedData = this._deserialize(data); + this._validate(deserializedData); + return Object.assign(Object.create(null), deserializedData); + } catch (error) { + if (error == 'ENOENT') { + this._ensureDirectory(); + throw new Error("Critical, config store does not exist, the base directory has been created, copy the necessary config files to the directory"); + } + + throw error; + } + + } +} + +class Config = Record> extends Store implements Iterable<[keyof T, T[keyof T]]> { + constructor(options: Readonly>>) { + super(options.path!, options.schemaValidator!, options.schema!); + + this._validate(this.store); + + } + + public get(key: Key): T[Key]; + public get(key: Key, defaultValue: Required[Key]): Required[Key]; + public get(key: Exclude, defaultValue?: Value): Value; + public get(key: string, defaultValue?: unknown): unknown { + return this._get(key, defaultValue); + } + + public getAll(): unknown { + return this.store as unknown; + } + + private _has(key: Key | string): boolean { + return dotProp.has(this.store, key as string); + } + + private _get(key: Key): T[Key] | undefined; + private _get(key: Key, defaultValue: Default): T[Key] | Default; + private _get(key: Key | string, defaultValue?: Default): Default | undefined { + if (!this._has(key)) { + throw new Error("Tried to acess a non existant property in the config"); + } + + return dotProp.get(this.store, key as string, defaultValue as T[Key]); + } + +} + +export default Config; -export const ConfigModel = model("Config", ConfigSchema, "config"); From 41317582fb084b240f0bbb5e978ee7f976dd5fdc Mon Sep 17 00:00:00 2001 From: Diego Magdaleno Date: Sat, 22 May 2021 16:41:48 -0500 Subject: [PATCH 2/2] Config: export getConfigPathForFile --- src/util/Config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/Config.ts b/src/util/Config.ts index c3f7b848..7d69fb45 100644 --- a/src/util/Config.ts +++ b/src/util/Config.ts @@ -13,7 +13,7 @@ interface Options { type Deserialize = (text: string) => T; -function getConfigPath(name: string, configFileName: string, extension: string): string { +export function getConfigPathForFile(name: string, configFileName: string, extension: string): string { const configEnvPath = envPaths(name, { suffix: "" }).config; const configPath = path.resolve(configEnvPath, `${configFileName}${extension}`) return configPath