diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..e0cbf4b --- /dev/null +++ b/src/index.js @@ -0,0 +1,118 @@ +import * as Types from './types'; +import ConfigField from './config-field'; +import envParser from './env-parser'; + +class TwConf { + static Types = Types; + + /** + * @type {Map.} + */ + config = new Map(); + + /** + * @type {Map.} + */ + env = new Map(); + + /** + * @method get + * @public + * @param {string} key + * @return {*} + */ + get(key) { + return this.config.get(key); + } + + get flatObject() { + const result = {}; + + this.config.forEach((value, key) => { + result[key.replace(/([._-])([a-z\d])/g, str => str[1].toUpperCase())] = value; + }); + + return Object.freeze(result); + } + + toString() { + return JSON.stringify(this.config); + } + + /** + * Create TwConf object + * @param {(Map.|Array.[]|object)} confSkeleton + * @param {object=} options + * @param {boolean} [options.allowUnspecified=true] + * @param {boolean} [options.flatOnly=false] + * @param {boolean} [options.validationOnDemand=false] + */ + constructor(confSkeleton, options = { + allowUnspecified: true, + flatOnly: false, + validationOnDemand: false, + }) { + this.options = options; + + /** + * @type {Map.} + */ + this.skeleton = null; + + if (confSkeleton instanceof Map) { + this.skeleton = confSkeleton; + } + + if (confSkeleton instanceof Array) { + this.skeleton = new Map(confSkeleton); + } + + if (typeof confSkeleton === 'object') { + this.skeleton = new Map(Object.keys(confSkeleton).map(key => [key, confSkeleton[key]])); + } + + this.skeleton.forEach((value, key) => { + if (!(value instanceof ConfigField)) { + if (typeof value !== 'object') { + const err = new TypeError('Value of ConfigRule must be a object'); + err.field = key; + throw err; + } + + this.skeleton.set(key, new ConfigField(value)); + } + }); + + if (!this.options.validationOnDemand) { + this.validate(); + } + } + + /** + * @method validate + * @public + */ + validate() { + this.env = envParser(this.options); + + const errors = []; + + this.skeleton.forEach((configField, key) => { + try { + this.config.set(key, configField.validate(this.env.get(key))); + } catch (err) { + err.field = key; + errors.push(err); + } + }); + + if (errors.length) { + const newError = new Error('Validation error'); + newError.errors = errors; + throw newError; + } + } +} + +export default TwConf; +module.exports = TwConf; diff --git a/src/index.test.js b/src/index.test.js new file mode 100644 index 0000000..6bf67cb --- /dev/null +++ b/src/index.test.js @@ -0,0 +1,39 @@ +/* eslint-env mocha */ +/* globals describe, it, before, after, beforeEach, afterEach */ + +import { expect } from 'chai'; +import TwConf from './index'; +import { StringType } from './types'; + +describe('twconfig main tests', () => { + beforeEach(() => { + process.env = { + 'DATABASE.MONGODB.HOSTNAME': 'localhost', + 'DATABASE.MONGODB.PORT': '27316', + 'REDIS:HASH-ALGO': 'testName', + }; + }); + + it('simple smoke test', () => { + expect(() => { +// eslint-disable-next-line no-new + const conf = new TwConf({ + 'database.mongodb.hostname': { + comment: 'hostname of mongodb', + simple: 'ams3.digitalocean.com', + type: new StringType(), + // required: true, + preTransforms: [ + value => String(value), + ], + validators: [ + value => value !== 'mongodb', + ], + default: 'localhost', + }, + }); + + expect(conf.get('database.mongodb.hostname')).to.be.equal('localhost'); + }).not.to.throw(); + }); +});