UNPKG

blueconfig

Version:

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

389 lines (334 loc) 9.9 kB
const stringifyPath = require('objectpath').stringify const utils = require('./performer/utils/utils.js') const unroot = utils.unroot /** * @namespace ZCUSTOMERROR */ /** * Custom Blueconfig errors are used to identify: Blueconfig internal error, Schema error or config error. * * This class is Parent of all other Blueconfig errors, all errors are inherited of this one. * * @example * if (myError instanceof BLUECONFIG_ERROR) { * console.log('is blueconfig error'); * } * * @extends Error * * @Memberof ZCUSTOMERROR */ class BLUECONFIG_ERROR extends Error { /** * @param {String} message Error message */ constructor(message) { super(message) return this } } /** * List of errors (should be loop/parsed on LISTOFERRORS.errors like an array). Can be usefull with custom format. * * * ```text * Validate failed because wrong value(s): * - root: Custom format "children" tried to validate something and failed: * 1) germany: * - name: must be of type String: value was 1 * 2) italy: * - subregion: must be of type String: value was 2 * ``` * * @example * blueconfig.addFormat({ // Allow to do: config.*.name, by example : config.app1.name, config.app2.name * name: 'children', * validate: function(children, schema, fullname) { * Object.keys(children).forEach((keyname) => { * try { * const conf = blueconfig(schema.children).merge(children[keyname]).validate(); * this.set(keyname, conf.getProperties()); * } catch (err) { errors.push(err); } * }); * * if (errors.length !== 0) { throw new LISTOFERRORS(errors) } * } * }); * * @extends BLUECONFIG_ERROR * * * @Memberof ZCUSTOMERROR */ class LISTOFERRORS extends BLUECONFIG_ERROR { /** * @param {BLUECONFIG_ERROR[]} errors List of errors */ constructor(errors) { super('List of several errors.') /** * List of errors * * @var errors * @memberof LISTOFERRORS */ this.errors = errors return this } } // ========================================= // ============= BLUECONFIG ERROR ============= // ========================================= // new Error = Probably a blueconfig internal error // ========================================= // ============= INSIDE ERROR ============== // ============= SCHEMA ERROR ============== // ========================================= // This is probably a js/application problem. /** * Fired when schema is not valid. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class SCHEMA_INVALID extends BLUECONFIG_ERROR { /** * @param {String} fullname Return the full selector (e.g.: `base.path.name`) * @param {String} message Error message */ constructor(fullname, message) { super(`${fullname}: ${message}`) /** * Return the full selector (e.g.: `base.path.name`) * * @var fullname * @memberof SCHEMA_INVALID */ this.fullname = fullname this.type = 'SCHEMA_INVALID' this.doc = 'The schema is not valid, edit your schema to continue.' return this } } // ========================================= // ============= INSIDE ERROR ============== // =============== JS ERROR ================ (with custom getter, format or parser) // ========================================= or wrong path with get/set/default/reset/getOrigin function. // This is probably a js/application problem. /** * Fired when adding custom getter/format/parser failed. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class CUSTOMISE_FAILED extends BLUECONFIG_ERROR { /** * @param {String} message Error message */ constructor(message) { super(message) this.type = 'CUSTOMISE_FAILED' this.doc = 'You try to add a getter/format/parser but you failed, fix your javascript code to continue.' return this } } /** * Fired when the usage of Blueconfig functions are wrong. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class INCORRECT_USAGE extends BLUECONFIG_ERROR { /** * @param {String} message Error message */ constructor(message) { super(message) this.type = 'INCORRECT_USAGE' this.doc = 'Wrong usage of blueconfig function, maybe wrong parameter, fix your javascript code to continue.' return this } } /** * Fired when we try to access to missed value (e.g.: `base.path.undefined.undefined.name` where `undefined` doesn't exist) * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class PATH_INVALID extends BLUECONFIG_ERROR { /** * @param {String} fullname Return the full selector of value missed (e.g.: `base.path.name`) * @param {Array} path Return the nearest full selector before missed key (e.g.: `['base', 'path']`) * @param {String} name Return the property name missing or invalid value * @param {*} value Return the value (should be an object) */ constructor(fullname, path, name, value) { const fullpath = unroot(stringifyPath([...path, name])) const why = (() => { const type = typeof value if (type !== 'object') { return `"${unroot(stringifyPath(path))}" is a ${type}` } else if (value === null) { return `"${unroot(stringifyPath(path))}" is null` } else { return `"${fullpath}" is not defined` } })() super(`${fullname}: cannot find "${fullpath}" property because ${why}.`) /** * Return the full selector of value missed (e.g.: `base.path.name`) * * @var fullname * @memberof PATH_INVALID */ this.fullname = fullname /** * Return the nearest full selector before missed key (e.g.: `base.path`) * * @var path * @memberof PATH_INVALID */ this.path = path /** * Return the property name missing or invalid value * * @var name * @memberof PATH_INVALID */ this.name = name /** * Return the value (should be an object) * * @var value * @memberof PATH_INVALID */ this.value = value /** * Error explanation * * @var why * @memberof PATH_INVALID */ this.why = why this.type = 'PATH_INVALID' this.doc = 'To fix this error you should try to use an existing property path (take a look on the schema), edit your javascript file to continue.' return this } } // ========================================= // ============== USER ERROR =============== // === (values don't respect the schema) === // ========================================= // This is probably a config problem. /** * Validate failed because wrong value. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class VALUE_INVALID extends BLUECONFIG_ERROR { /** * @param {String} message Error message */ constructor(message) { super(message) this.type = 'VALUE_INVALID' this.doc = 'You should try to change your config to respect the schema to continue.' return this } } /** * Validate failed because wrong value. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class VALIDATE_FAILED extends BLUECONFIG_ERROR { /** * @param {String} explains List of explained error why validate failed */ constructor(explains) { super('Validate failed because wrong value(s):\n' + explains) /** * List of explained error why validate failed (defined with the first argument of the constructor) * @name why * @memberof VALIDATE_FAILED */ this.why = explains this.type = 'VALIDATE_FAILED' this.doc = 'You should try to change your config to respect the schema to continue.' return this } } /** * Value has wrong format. * * When an Error is caught in format function during the validation, FORMAT_INVALID is fired instead. * * @extends BLUECONFIG_ERROR * * @Memberof ZCUSTOMERROR */ class FORMAT_INVALID extends BLUECONFIG_ERROR { /** * @param {String} fullname Return the full selector (e.g.: `base.path.name`) * @param {String} message Error message * @param {Object} getter Getter * @param {String} getter.name Getter name * @param {String} getter.value Getter value (=Getter keyname) * @param {String} getter Returned value by `Getter(keyname)` */ constructor(fullname, message, getter, value) { super(message) /** * Return the full selector (e.g.: `base.path.name`) * * @var fullname * @memberof FORMAT_INVALID */ this.fullname = fullname /** * Return the nearest full selector before missed key (e.g.: `base.path.name`) * * @var getter * @typedef {Object} * @property {string} name Getter name * @property {string} value Getter keyname * @memberof FORMAT_INVALID */ this.getter = { name: getter.name, keyname: getter.keyname } /** * Returned value by `this.getter` * * @var value * @memberof FORMAT_INVALID */ this.value = value this.type = 'FORMAT_INVALID' this.doc = 'You should try to change the property value to respect the schema to continue.' return this } } module.exports = { BLUECONFIG_ERROR, LISTOFERRORS, // 1 SCHEMA_INVALID, // 2 CUSTOMISE_FAILED, INCORRECT_USAGE, PATH_INVALID, // 2 VALUE_INVALID, VALIDATE_FAILED, FORMAT_INVALID }