confmgr
Version:
Env. Configuration Manager
422 lines • 17.7 kB
JavaScript
;
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