UNPKG

blueconfig

Version:

Merge and validate your config depending of your schema, forked from node-convict.

302 lines (266 loc) 10.2 kB
const ConfigObjectModel = require('./model/com.js') const ParserInterface = require('./performer/parser.js') const GetterInterface = require('./performer/getter.js') const RulerInterface = require('./performer/ruler.js') const cvtError = require('./error.js') const CUSTOMISE_FAILED = cvtError.CUSTOMISE_FAILED /** * Cutomize Blueconfig before make a ConfigObjectModel (= COM, Like DOM but not for Document, for Config) * * @example * const Blueconfig = require('blueconfig/lib/core.js') * const blueconfig = new Blueconfig() * console.log(blueconfig.getGettersOrder()) // === [value', 'force'] * * @class */ const BlueconfigCore = function() { this.initPerformer() } module.exports = BlueconfigCore /** * Create instance of worker (Parser, Getter, Ruler) * * @return {this} */ BlueconfigCore.prototype.initPerformer = function() { this.Parser = new ParserInterface() this.Getter = new GetterInterface() this.Ruler = new RulerInterface() return this } /** * Gets array with getter name in the current order of priority * * @example * const Blueconfig = require('blueconfig/lib/core.js') * const blueconfig = new Blueconfig() * blueconfig.init(rawSchema, options) * * @param {string|object} rawSchema Schema object or a path to a schema JSON file * @param {object} [options] Options. [See ConfigObjectModel constructor doc](./ConfigObjectModel.html) * * @return {string[]} Returns current getter order */ BlueconfigCore.prototype.init = function(rawSchema, options) { return new ConfigObjectModel(rawSchema, options, { Parser: this.Parser, Getter: this.Getter, Ruler: this.Ruler }) } /** * Gets array with getter name in the current order of priority * * @example * blueconfig.getGettersOrder() // ['default', 'value', 'env', 'arg', 'force'] * * @return {string[]} Returns current getter order */ BlueconfigCore.prototype.getGettersOrder = function() { return [...this.Getter.storage.order] } /** * Sorts getter priority, this function uses ascending order. It is recommanded to uses this function before init COM (with `blueconfig()`) * or you should call `<COM>.refreshGetters()`. * * @see ConfigObjectModel.refreshGetters * * @example * blueconfig.getGettersOrder() * // ['default', 'value', 'env', 'arg', 'force'] * * // two ways to do: * blueconfig.sortGetters(['default', 'value', 'arg', 'env', 'force']) * blueconfig.sortGetters(['default', 'value', 'arg', 'env']) // force is optional and must be the last one * * blueconfig.getGettersOrder() * // ['default', 'value', 'arg', 'env', 'force'] * * @param {string[]} newOrder Value of the property to validate * * @return {this} */ BlueconfigCore.prototype.sortGetters = function(newOrder) { const sortFilter = this.Getter.sortGetters(this.Getter.storage.order, newOrder) this.Getter.storage.order.sort(sortFilter) return this } /** * Adds a new custom getter. Getter function get value depending of the property name. In schema, the property name is a keyname of the schema. * * @example * convict.addGetter('aws', function(aws, schema, stopPropagation) { * return aws.get(aws) * }, false, true) * * @param {string|object} name String for Getter name, `Object/Object[]` or which contains arguments: * @param {string} name.name Getter name * @param {function} name.getter *See below* * @param {boolean} [name.usedOnlyOnce=false] *See below* * @param {boolean} [name.rewrite=false] *See below* * @param {ConfigObjectModel.getterCallback} getter Getter function get external value depending of the name name. * @param {boolean} [usedOnlyOnce=false] `false` by default. If true, The value can't be reused by another `keyname=value` * @param {boolean} [rewrite=false] Allow rewrite an existant format * * @return {this} */ BlueconfigCore.prototype.addGetter = function(name, getter, usedOnlyOnce, rewrite) { if (typeof name === 'object') { getter = name.getter usedOnlyOnce = name.usedOnlyOnce rewrite = name.rewrite name = name.name || name.property } this.Getter.add(name, getter, usedOnlyOnce, rewrite) return this } /** * Adds several getters * * @example * // Example with the default env & arg getters: * Blueconfig.addGetters({ * env: { * getter: (value, schema) => schema._cvtCoerce(this.getEnv()[value]) * }, * arg: { * getter: function(value, schema, stopPropagation) { * const argv = parseArgs(this.getArgs(), { configuration: { 'dot-notation': false } }) * return schema._cvtCoerce(argv[value]) * }, * usedOnlyOnce: true * } * }) * * @param {object|object[]} getters Object containing list of Getters/Object * @param {object} getters.{name} `name` is the getter name * @param {function} getters.{name}.getter *See Blueconfig.addGetter* * @param {boolean} [getters.{name}.usedOnlyOnce=false] *See Blueconfig.addGetter* * @param {boolean} [getters.{name}.rewrite=false] *See Blueconfig.addGetter* * * @return {this} */ BlueconfigCore.prototype.addGetters = function(getters) { if (Array.isArray(getters)) { getters.forEach((child) => { this.addGetter(child) }) return this } Object.keys(getters).forEach((name) => { const child = getters[name] this.addGetter(name, child.getter, child.usedOnlyOnce, child.rewrite) }) return this } /** * Adds a new custom format. Validate function and coerce function will be used to validate COM property with `format` type. * * @example * Blueconfig.addFormat({ * name: 'int', * coerce: (value) => (typeof value !== 'undefined') ? parseInt(value, 10) : value, * validate: function(value) { * if (Number.isInteger(value)) { * throw new Error('must be an integer') * } * } * }) * * * @param {string|object} name String for Format name, `Object/Object[]` or which contains arguments: * @param {string} name.name Format name * @param {function} name.validate *See below* * @param {function} name.coerce *See below* * @param {boolean} [name.rewrite=false] *See below* * @param {SchemaNode.validateCallback} validate Validate function, should throw if the value is wrong `Error` or [`LISTOFERRORS` (see example)](./ZCUSTOMERROR.LISTOFERRORS.html) * @param {SchemaNode.coerce} coerce Coerce function to convert a value to a specified function (can be omitted) * @param {boolean} [rewrite=false] Allow rewrite an existant format * * @return {this} */ BlueconfigCore.prototype.addFormat = function(name, validate, coerce, rewrite) { if (typeof name === 'object') { validate = name.validate coerce = name.coerce rewrite = name.rewrite name = name.name } this.Ruler.add(name, validate, coerce, rewrite) return this } /** * Adds several formats * * @example * // Add several formats with: [Object, Object, Object, Object] * const vFormat = require('blueconfig-format-with-validator') * blueconfig.addFormats({ // add: email, ipaddress, url, token * email: vFormat.email, * ipaddress: vFormat.ipaddress, * url: vFormat.url, * token: { * validate: function(value) { * if (!isToken(value)) { * throw new Error(':(') * } * } * } * }) * * @see Blueconfig.addFormat * * @param {object|object[]} formats Object containing list of Object * @param {object} formats.{name} {name} in `formats.{name}` is the format name * @param {function} formats.{name}.validate *See Blueconfig.addFormat* * @param {function} formats.{name}.coerce *See Blueconfig.addFormat* * @param {boolean} [formats.{name}.rewrite=false] *See Blueconfig.addFormat* * * @return {this} */ BlueconfigCore.prototype.addFormats = function(formats) { if (Array.isArray(formats)) { formats.forEach((child) => { this.addFormat(child) }) return this } Object.keys(formats).forEach((name) => { this.addFormat(name, formats[name].validate, formats[name].coerce, formats[name].rewrite) }) return this } /** * Adds new custom file parsers. JSON.parse will be used by default for unknown extension (default extension -> `*` => JSON). * * Blueconfig is able to parse files with custom file types during `merge`. For this specify the * corresponding parsers with the associated file extensions. * * If no supported extension is detected, `merge` will fallback to using the default json parser `JSON.parse`. * * @example * blueconfig.addParser([ * { extension: ['yml', 'yaml'], parse: yaml.safeLoad }, * { extension: ['yaml', 'yml'], parse: require('yaml').safeLoad }, * // will allow comment in json file * { extension: 'json', parse: require('json5').parse } // replace `JSON` by `json5` * ]) * config.merge('config.yml') * * @param {object[]} parsers Parser * @param {string} parsers.extension Parser extension * @param {function} parsers.parse Parser function * * @return {this} */ BlueconfigCore.prototype.addParser = function(parsers) { if (!Array.isArray(parsers)) parsers = [parsers] parsers.forEach((parser) => { if (!parser) throw new CUSTOMISE_FAILED('Invalid parser') if (!parser.extension) throw new CUSTOMISE_FAILED('Missing parser.extension') if (!parser.parse) throw new CUSTOMISE_FAILED('Missing parser.parse function') this.Parser.add(parser.extension, parser.parse) }) return this }