UNPKG

confmgr

Version:
422 lines 17.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigManager = void 0; var process = __importStar(require("process")); var dotenv_1 = __importDefault(require("dotenv")); var path = __importStar(require("path")); var chalk_1 = __importDefault(require("chalk")); var yaml_1 = __importDefault(require("yaml")); var fs_1 = __importDefault(require("fs")); var SpecsFactory_1 = require("./SpecsFactory"); /** * Helper fonction to clone objects * @param object The object to clone */ function clone(object) { return JSON.parse(JSON.stringify(object)); } /** * The ConfigManager class is where everything happens. * * The ConfigManager allows you loading your config specs definition, * it also loads your actual config and lets you access it. */ var ConfigManager = /** @class */ (function () { /** * The constructor of ConfigManager is private and is called only * by GetInstance to ensure it works as singleton. * @param specs */ function ConfigManager(specs) { if (!specs) throw new Error('Missing specs in ctor'); this.specs = specs; this.refresh(); } /** * Load specs from a YAML file * @param file Path of the YAML file */ ConfigManager.loadSpecsFromYaml = function (file) { var configFile = fs_1.default.readFileSync(file, 'utf8'); var yaml = yaml_1.default.parse(configFile); if (Object.keys(yaml)[1]) throw new Error('Multiple prefixes is not supported yet. Get in touch if you see a need.'); var prefix = Object.keys(yaml)[0]; var factory = new SpecsFactory_1.SpecsFactory({ prefix: prefix }); Object.keys(yaml[prefix]).map(function (module) { Object.keys(yaml[prefix][module]).map(function (key) { var shortKey = key; // key.replace(`${prefix}_${module}_`, '') var description = yaml[prefix][module][shortKey].description; var opt = yaml[prefix][module][shortKey]; delete opt.description; var options = opt; factory.appendSpec(module, factory.getSpec(shortKey, description, options)); }); }); return factory.getSpecs(); }; /** * ConfigManager is a singleton. * @param specs The config specs the ConfigManager will rely on. It can be a ConfigSpecs object or the path of a file. * In that case, the file can be either a YAML file or a JSON file. */ ConfigManager.getInstance = function (specs) { if (!this.instance && !specs) { throw new Error('Missing specs'); } if (!this.instance && specs) { if (typeof specs === 'string') { this.instance = new ConfigManager(ConfigManager.loadSpecsFromYaml(specs)); } else { this.instance = new ConfigManager(specs); } this.instance.rebuild(); } return this.instance; }; /** * You should not use this function. It is there only for testing. */ ConfigManager.clearInstance = function () { this.instance = undefined; }; /** * This function converts a string to a boolean. It ignores the case and * support also values such as 0 and 1, yes and no. * @param s String to convert */ ConfigManager.stringToBoolean = function (s) { if (typeof s === 'boolean') return s; var res = null; switch (s.toLowerCase().trim()) { case 'true': case 'yes': case '1': res = true; break; case 'false': case 'no': case '0': case null: res = false; break; default: res = Boolean(s); } return res; }; ConfigManager.prototype.getConfig = function () { return this.config; }; /** * This retrieves the config and fills defaults. * Additionnaly, the config object you get is decorated with a few helper fonctions * such as Print, Validate, etc... to help you easily use your config */ ConfigManager.prototype.buildConfig = function () { var _this = this; // here we clone the config specs so we dont lose the specs var confClone = { values: clone(this.specs.config), Get: this.Get.bind(this), Print: this.Print.bind(this), Validate: this.Validate.bind(this), ValidateField: this.ValidateField.bind(this), GenEnv: this.GenEnv.bind(this), }; var specs = this.getSpecs(); Object.entries(confClone.values).map(function (_a) { var mod = _a[0], _confItems = _a[1]; Object.entries(confClone.values[mod]).map(function (_a) { var key = _a[0], _val = _a[1]; var longKey = "".concat(_this.specs.container.prefix, "_").concat(mod, "_").concat(key); confClone.values[mod][key] = process.env[longKey]; // Here we check if we need to apply some default values if (!confClone.values[mod][key] && specs[mod][key].options && specs[mod][key].options.default) { confClone.values[mod][key] = specs[mod][key].options.default; } // Here we check if a type is defined, and if so, we try to convert if (specs[mod][key].options && specs[mod][key].options.type) { switch (specs[mod][key].options.type) { case 'string': // nothing to do for strings... break; case 'number': confClone.values[mod][key] = Number(confClone.values[mod][key]); break; case 'boolean': confClone.values[mod][key] = ConfigManager.stringToBoolean(confClone.values[mod][key]); break; case 'array': confClone.values[mod][key] = typeof confClone.values[mod][key] === 'string' ? JSON.parse(confClone.values[mod][key]) : confClone.values[mod][key]; break; case 'object': confClone.values[mod][key] = typeof confClone.values[mod][key] === 'string' ? JSON.parse(confClone.values[mod][key]) : confClone.values[mod][key]; break; default: throw new Error("Type not supported: ".concat(specs[mod][key].options.type)); } } }); }); return confClone; }; ConfigManager.prototype.getSpecs = function () { return this.specs.config; }; /** * This function defines what '.env' file will be loaded. * By default, '.env' will be used. * However, if you pass NODE_ENV=abc, the loaded file will * be .env.abc */ ConfigManager.getEnvFile = function () { var profile = process.env.NODE_ENV || 'production'; var envfile = profile == 'production' ? path.resolve(process.cwd(), '.env') : path.resolve(process.cwd(), '.env.' + profile.toLowerCase()); return envfile; }; /** You likely will never have to use this function which * is mostly use for the tests. If you don't know, do NOT call * this function, it would take time and likely do nothing * interesting for you. */ ConfigManager.prototype.refresh = function () { var envfile = ConfigManager.getEnvFile(); dotenv_1.default.config({ path: envfile }); }; /** Does not touch the ENV but rebuild the config. * This is useful if you know that the ENV changed */ ConfigManager.prototype.rebuild = function () { this.config = this.buildConfig(); }; /** Calling this function will get an instance of the Config and attach it * to the global scope. */ ConfigManager.loadToGlobal = function () { global['Config'] = ConfigManager.getInstance().getConfig(); }; ConfigManager.isregExpWithAttributes = function (r) { return r.pattern !== undefined; }; /** * This is the actual function performing the validation of a given field according to the spcs * @param specs The specs */ ConfigManager.prototype.validaFieldsSpecs = function (module, specs) { var result = true; var config = this.getConfig().values; if (specs && specs.options) { var item = config[module][specs.name]; if (specs.options.regexp !== undefined) { var regexp_options = { pattern: undefined, attributes: undefined, }; if (!ConfigManager.isregExpWithAttributes(specs.options.regexp)) regexp_options.pattern = specs.options.regexp; else { regexp_options = specs.options.regexp; } var regex = RegExp(regexp_options.pattern, regexp_options.attributes); var testResult = regex.test(item); result = result && testResult; } result = result && (!specs.options.mandatory || (specs.options.mandatory && item !== undefined)); } return result; }; ConfigManager.prototype.getFieldSpecs = function (module, key) { var configSpecs = this.getSpecs(); var res = Object.entries(configSpecs[module]).find(function (_a) { var _key = _a[0], env = _a[1]; return env.name == key; }); return res && res[1] ? res[1] : null; }; /** * Validate a single field. * @param key Key of the field */ ConfigManager.prototype.ValidateField = function (module, key) { var fieldSpecs = this.getFieldSpecs(module, key); return this.validaFieldsSpecs(module, fieldSpecs); }; /** Validate the config and return wheather it is valid or not */ ConfigManager.prototype.Validate = function () { var _this = this; var result = true; var configSpecs = this.getSpecs(); Object.entries(configSpecs).map(function (_a) { var mod = _a[0], _data = _a[1]; Object.entries(configSpecs[mod]).map(function (_a) { var _key = _a[0], env = _a[1]; result = result && _this.validaFieldsSpecs(mod, env); }); }); return result; }; /** Check whether the module mod is part of ours specs */ ConfigManager.prototype.isModuleValid = function (mod) { var hit = Object.entries(this.specs.config).find(function (val) { return val[0] === mod; }); return hit !== undefined; }; /** Check wether the key is valid. We assume here that the module does exist */ ConfigManager.prototype.isKeyValid = function (mod, key) { var hit = Object.entries(this.specs.config[mod]).find(function (val) { return val[0] === key; }); return hit !== undefined; }; /** * This Getter is the safest way to access your configuration values as it will throw errors in case you try access an invalid module/key * @param mod * @param key */ ConfigManager.prototype.Get = function (mod, key) { var moduleExists = this.isModuleValid(mod); if (!moduleExists) throw new Error("Module '".concat(mod, "' does not exist in your specs")); var keyExists = this.isKeyValid(mod, key); if (!keyExists) throw new Error("Key '".concat(mod, "/").concat(key, "' does not exist in your specs")); var config = this.getConfig().values; return config[mod][key]; }; ConfigManager.prototype.printItemColor = function (mod, item, opt) { var config = this.getConfig().values; var container = "".concat(this.specs.container.prefix); var valid = this.validaFieldsSpecs(mod, item); var entry = ' '; entry += chalk_1.default[valid ? 'green' : 'red']("\u001C".concat(valid ? ' ✅' : ' ❌', " ").concat(item.name.replace(container + '_', ''), ": ")); var io = item.options; // the value itself /* eslint-disable */ entry += chalk_1.default[valid ? 'white' : 'red']("".concat(io && io.masked ? config[mod][item.name] ? '*****' : 'empty' : JSON.stringify(config[mod][item.name], null, 0))); /* eslint-enable */ if (!opt.compact) { entry += chalk_1.default.grey("\n ".concat(item.description, "\n")); } if (!opt.compact) { entry += chalk_1.default[valid ? 'white' : 'red']("".concat(io && io.regexp ? ' regexp: ' + io.regexp + '\n' : '', " ")); } opt.logger(entry); }; ConfigManager.prototype.printItemNoColor = function (mod, item, opt) { var config = this.getConfig().values; var container = "".concat(this.specs.container.prefix); var valid = this.validaFieldsSpecs(mod, item); var entry = ' '; entry += "\u001C".concat(valid ? ' ✅' : ' ❌', " ").concat(item.name.replace(container + '_', ''), ": "); var io = item.options; // the value itself /* eslint-disable */ entry += "".concat(io && io.masked ? config[mod][item.name] ? '*****' : 'empty' : JSON.stringify(config[mod][item.name], null, 0)); /* eslint-enable */ if (!opt.compact) { entry += "\n ".concat(item.description, "\n"); } if (!opt.compact) { entry += "".concat(io && io.regexp ? ' regexp: ' + io.regexp + '\n' : '', " "); } opt.logger(entry); }; /** * Display the current ENV using either the logger you provide or console.log by default. */ ConfigManager.prototype.Print = function (opt) { var _this = this; var container = "".concat(this.specs.container.prefix); if (!opt) opt = { color: true }; if (opt.color === undefined) opt.color = true; if (!opt.logger) opt.logger = console.log; if (opt.color) opt.logger(chalk_1.default.blue("\u001C".concat(container, ":"))); else opt.logger("\u001C".concat(container, ":")); Object.entries(this.specs.config).map(function (_a) { var mod = _a[0], moduleContent = _a[1]; if (opt.color) opt.logger(chalk_1.default.cyan("\u001C \uD83D\uDCE6 ".concat(mod, ":"))); else opt.logger("\u001C \uD83D\uDCE6 ".concat(mod, ":")); Object.entries(moduleContent).map(function (_a) { var _key = _a[0], env = _a[1]; opt.color ? _this.printItemColor(mod, env, opt) : _this.printItemNoColor(mod, env, opt); }); }); }; ConfigManager.prototype.GenEnv = function () { var container = "".concat(this.specs.container.prefix); var res = []; Object.entries(this.specs.config).map(function (_a) { var mod = _a[0], moduleContent = _a[1]; Object.entries(moduleContent).map(function (_a) { var _b; var key = _a[0], env = _a[1]; /* eslint-disable */ res.push("".concat(container, "_").concat(mod, "_").concat(key, "=").concat(((_b = env.options) === null || _b === void 0 ? void 0 : _b.default) ? JSON.stringify(env.options.default, null, 0) : '')); /* eslint-enable */ }); }); return res; }; return ConfigManager; }()); exports.ConfigManager = ConfigManager; //# sourceMappingURL=ConfigManager.js.map