UNPKG

kibana-123

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

185 lines (152 loc) 4.95 kB
import Joi from 'joi'; import _ from 'lodash'; import override from './override'; import unset from './unset'; import createDefaultSchema from './schema'; import pkg from '../../utils/package_json'; import clone from './deep_clone_with_buffers'; const schema = Symbol('Joi Schema'); const schemaExts = Symbol('Schema Extensions'); const vals = Symbol('config values'); const pendingSets = Symbol('Pending Settings'); module.exports = class Config { static withDefaultSchema(settings = {}) { return new Config(createDefaultSchema(), settings); } constructor(initialSchema, initialSettings) { this[schemaExts] = Object.create(null); this[vals] = Object.create(null); this[pendingSets] = _.merge(Object.create(null), initialSettings || {}); if (initialSchema) this.extendSchema(initialSchema); } getPendingSets() { return new Map(_.pairs(this[pendingSets])); } extendSchema(key, extension) { if (key && key.isJoi) { return _.each(key._inner.children, (child) => { this.extendSchema(child.key, child.schema); }); } if (this.has(key)) { throw new Error(`Config schema already has key: ${key}`); } _.set(this[schemaExts], key, extension); this[schema] = null; let initialVals = _.get(this[pendingSets], key); if (initialVals) { this.set(key, initialVals); unset(this[pendingSets], key); } else { this._commit(this[vals]); } } removeSchema(key) { if (!_.has(this[schemaExts], key)) { throw new TypeError(`Unknown schema key: ${key}`); } this[schema] = null; unset(this[schemaExts], key); unset(this[pendingSets], key); unset(this[vals], key); } resetTo(obj) { this._commit(obj); } set(key, value) { // clone and modify the config let config = clone(this[vals]); if (_.isPlainObject(key)) { config = override(config, key); } else { _.set(config, key, value); } // attempt to validate the config value this._commit(config); } _commit(newVals) { // resolve the current environment let env = newVals.env; delete newVals.env; if (_.isObject(env)) env = env.name; if (!env) env = process.env.NODE_ENV || 'production'; let dev = env === 'development'; let prod = env === 'production'; // pass the environment as context so that it can be refed in config let context = { env: env, prod: prod, dev: dev, notProd: !prod, notDev: !dev, version: _.get(pkg, 'version'), buildNum: dev ? Math.pow(2, 53) - 1 : _.get(pkg, 'build.number', NaN), buildSha: dev ? 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' : _.get(pkg, 'build.sha', '') }; if (!context.dev && !context.prod) { throw new TypeError( `Unexpected environment "${env}", expected one of "development" or "production"` ); } let results = Joi.validate(newVals, this.getSchema(), { context }); if (results.error) { throw results.error; } this[vals] = results.value; } get(key) { if (!key) { return clone(this[vals]); } let value = _.get(this[vals], key); if (value === undefined) { if (!this.has(key)) { throw new Error('Unknown config key: ' + key); } } return clone(value); } has(key) { function has(key, schema, path) { path = path || []; // Catch the partial paths if (path.join('.') === key) return true; // Only go deep on inner objects with children if (_.size(schema._inner.children)) { for (let i = 0; i < schema._inner.children.length; i++) { let child = schema._inner.children[i]; // If the child is an object recurse through it's children and return // true if there's a match if (child.schema._type === 'object') { if (has(key, child.schema, path.concat([child.key]))) return true; // if the child matches, return true } else if (path.concat([child.key]).join('.') === key) { return true; } } } } if (_.isArray(key)) { // TODO: add .has() support for array keys key = key.join('.'); } return !!has(key, this.getSchema()); } getSchema() { if (!this[schema]) { this[schema] = (function convertToSchema(children) { let schema = Joi.object().keys({}).default(); for (const key of Object.keys(children)) { const child = children[key]; const childSchema = _.isPlainObject(child) ? convertToSchema(child) : child; if (!childSchema || !childSchema.isJoi) { throw new TypeError('Unable to convert configuration definition value to Joi schema: ' + childSchema); } schema = schema.keys({ [key]: childSchema }); } return schema; }(this[schemaExts])); } return this[schema]; } };