mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge pull request #6554 from w33ble/implement/pluginConfigPrefix
Implement plugin config prefix
This commit is contained in:
commit
4408069a9b
5 changed files with 180 additions and 23 deletions
|
@ -43,13 +43,36 @@ describe('lib/config/config', function () {
|
|||
|
||||
describe('constructor', function () {
|
||||
|
||||
it('should not allow any config if the schema is not passed', function (done) {
|
||||
it('should not allow any config if the schema is not passed', function () {
|
||||
var config = new Config();
|
||||
var run = function () {
|
||||
config.set('something.enable', true);
|
||||
};
|
||||
expect(run).to.throwException();
|
||||
done();
|
||||
});
|
||||
|
||||
it('should allow keys in the schema', function () {
|
||||
var config = new Config(schema);
|
||||
var run = function () {
|
||||
config.set('test.client.host', 'http://0.0.0.0');
|
||||
};
|
||||
expect(run).to.not.throwException();
|
||||
});
|
||||
|
||||
it('should not allow keys not in the schema', function () {
|
||||
var config = new Config(schema);
|
||||
var run = function () {
|
||||
config.set('paramNotDefinedInTheSchema', true);
|
||||
};
|
||||
expect(run).to.throwException();
|
||||
});
|
||||
|
||||
it('should not allow child keys not in the schema', function () {
|
||||
var config = new Config(schema);
|
||||
var run = function () {
|
||||
config.set('test.client.paramNotDefinedInTheSchema', true);
|
||||
};
|
||||
expect(run).to.throwException();
|
||||
});
|
||||
|
||||
it('should set defaults', function () {
|
||||
|
@ -198,6 +221,14 @@ describe('lib/config/config', function () {
|
|||
expect(config.get('myTest.test')).to.be(true);
|
||||
});
|
||||
|
||||
it('should allow you to extend the schema with a prefix', function () {
|
||||
var newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
|
||||
config.extendSchema('prefix.myTest', newSchema);
|
||||
expect(config.get('prefix')).to.eql({ myTest: { test: true }});
|
||||
expect(config.get('prefix.myTest')).to.eql({ test: true });
|
||||
expect(config.get('prefix.myTest.test')).to.be(true);
|
||||
});
|
||||
|
||||
it('should NOT allow you to extend the schema if somethign else is there', function () {
|
||||
var newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
|
||||
var run = function () {
|
||||
|
|
83
src/server/config/__tests__/unset.js
Normal file
83
src/server/config/__tests__/unset.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
import unset from '../unset';
|
||||
import expect from 'expect.js';
|
||||
|
||||
describe('unset(obj, key)', function () {
|
||||
describe('invalid input', function () {
|
||||
it('should do nothing if not given an object', function () {
|
||||
const obj = 'hello';
|
||||
unset(obj, 'e');
|
||||
expect(obj).to.equal('hello');
|
||||
});
|
||||
|
||||
it('should do nothing if not given a key', function () {
|
||||
const obj = { one: 1 };
|
||||
unset(obj);
|
||||
expect(obj).to.eql({ one: 1 });
|
||||
});
|
||||
|
||||
it('should do nothing if given an empty string as a key', function () {
|
||||
const obj = { one: 1 };
|
||||
unset(obj, '');
|
||||
expect(obj).to.eql({ one: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('shallow removal', function () {
|
||||
let obj;
|
||||
|
||||
beforeEach(function () {
|
||||
obj = { one: 1, two: 2, deep: { three: 3, four: 4 } };
|
||||
});
|
||||
|
||||
it('should remove the param using a string key', function () {
|
||||
unset(obj, 'two');
|
||||
expect(obj).to.eql({ one: 1, deep: { three: 3, four: 4 } });
|
||||
});
|
||||
|
||||
it('should remove the param using an array key', function () {
|
||||
unset(obj, ['two']);
|
||||
expect(obj).to.eql({ one: 1, deep: { three: 3, four: 4 } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('deep removal', function () {
|
||||
let obj;
|
||||
|
||||
beforeEach(function () {
|
||||
obj = { one: 1, two: 2, deep: { three: 3, four: 4 } };
|
||||
});
|
||||
|
||||
it('should remove the param using a string key', function () {
|
||||
unset(obj, 'deep.three');
|
||||
expect(obj).to.eql({ one: 1, two: 2, deep: { four: 4 } });
|
||||
});
|
||||
|
||||
it('should remove the param using an array key', function () {
|
||||
unset(obj, ['deep', 'three']);
|
||||
expect(obj).to.eql({ one: 1, two: 2, deep: { four: 4 } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursive removal', function () {
|
||||
it('should clear object if only value is removed', function () {
|
||||
const obj = { one: { two: { three: 3 } } };
|
||||
unset(obj, 'one.two.three');
|
||||
expect(obj).to.eql({});
|
||||
});
|
||||
|
||||
it('should clear object if no props are left', function () {
|
||||
const obj = { one: { two: { three: 3 } } };
|
||||
unset(obj, 'one.two');
|
||||
expect(obj).to.eql({});
|
||||
});
|
||||
|
||||
it('should remove deep property, then clear the object', function () {
|
||||
const obj = { one: { two: { three: 3, four: 4 } } };
|
||||
unset(obj, 'one.two.three');
|
||||
expect(obj).to.eql({ one: { two: { four: 4 } } });
|
||||
|
||||
unset(obj, 'one.two.four');
|
||||
expect(obj).to.eql({});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,13 +2,13 @@ import Promise from 'bluebird';
|
|||
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';
|
||||
import { zipObject } from 'lodash';
|
||||
|
||||
const schema = Symbol('Joi Schema');
|
||||
const schemaKeys = Symbol('Schema Extensions');
|
||||
const schemaExts = Symbol('Schema Extensions');
|
||||
const vals = Symbol('config values');
|
||||
const pendingSets = Symbol('Pending Settings');
|
||||
|
||||
|
@ -18,16 +18,15 @@ module.exports = class Config {
|
|||
}
|
||||
|
||||
constructor(initialSchema, initialSettings) {
|
||||
this[schemaKeys] = new Map();
|
||||
|
||||
this[schemaExts] = Object.create(null);
|
||||
this[vals] = Object.create(null);
|
||||
this[pendingSets] = new Map(_.pairs(clone(initialSettings || {})));
|
||||
this[pendingSets] = _.merge(Object.create(null), initialSettings || {});
|
||||
|
||||
if (initialSchema) this.extendSchema(initialSchema);
|
||||
}
|
||||
|
||||
getPendingSets() {
|
||||
return this[pendingSets];
|
||||
return new Map(_.pairs(this[pendingSets]));
|
||||
}
|
||||
|
||||
extendSchema(key, extension) {
|
||||
|
@ -41,27 +40,27 @@ module.exports = class Config {
|
|||
throw new Error(`Config schema already has key: ${key}`);
|
||||
}
|
||||
|
||||
this[schemaKeys].set(key, extension);
|
||||
_.set(this[schemaExts], key, extension);
|
||||
this[schema] = null;
|
||||
|
||||
let initialVals = this[pendingSets].get(key);
|
||||
let initialVals = _.get(this[pendingSets], key);
|
||||
if (initialVals) {
|
||||
this.set(key, initialVals);
|
||||
this[pendingSets].delete(key);
|
||||
unset(this[pendingSets], key);
|
||||
} else {
|
||||
this._commit(this[vals]);
|
||||
}
|
||||
}
|
||||
|
||||
removeSchema(key) {
|
||||
if (!this[schemaKeys].has(key)) {
|
||||
if (!_.has(this[schemaExts], key)) {
|
||||
throw new TypeError(`Unknown schema key: ${key}`);
|
||||
}
|
||||
|
||||
this[schema] = null;
|
||||
this[schemaKeys].delete(key);
|
||||
this[pendingSets].delete(key);
|
||||
delete this[vals][key];
|
||||
unset(this[schemaExts], key);
|
||||
unset(this[pendingSets], key);
|
||||
unset(this[vals], key);
|
||||
}
|
||||
|
||||
resetTo(obj) {
|
||||
|
@ -138,7 +137,7 @@ module.exports = class Config {
|
|||
// Catch the partial paths
|
||||
if (path.join('.') === key) return true;
|
||||
// Only go deep on inner objects with children
|
||||
if (schema._inner.children.length) {
|
||||
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
|
||||
|
@ -163,8 +162,22 @@ module.exports = class Config {
|
|||
|
||||
getSchema() {
|
||||
if (!this[schema]) {
|
||||
let objKeys = zipObject([...this[schemaKeys]]);
|
||||
this[schema] = Joi.object().keys(objKeys).default();
|
||||
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];
|
||||
|
|
26
src/server/config/unset.js
Normal file
26
src/server/config/unset.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import _ from 'lodash';
|
||||
import toPath from 'lodash/internal/toPath';
|
||||
|
||||
module.exports = function unset(object, rawPath) {
|
||||
if (!object) return;
|
||||
const path = toPath(rawPath);
|
||||
|
||||
switch (path.length) {
|
||||
case 0:
|
||||
return;
|
||||
|
||||
case 1:
|
||||
delete object[rawPath];
|
||||
break;
|
||||
|
||||
default:
|
||||
const leaf = path.pop();
|
||||
const parentPath = path.slice();
|
||||
const parent = _.get(object, parentPath);
|
||||
unset(parent, leaf);
|
||||
if (!_.size(parent)) {
|
||||
unset(object, parentPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash';
|
||||
import toPath from 'lodash/internal/toPath';
|
||||
import Joi from 'joi';
|
||||
import Bluebird, { attempt, fromNode } from 'bluebird';
|
||||
import { basename, resolve } from 'path';
|
||||
|
@ -37,6 +38,8 @@ const defaultConfigSchema = Joi.object({
|
|||
* @param {String} [opts.version=pkg.version] - the version of this plugin
|
||||
* @param {Function} [opts.init] - A function that will be called to initialize
|
||||
* this plugin at the appropriate time.
|
||||
* @param {Function} [opts.configPrefix=this.id] - The prefix to use for configuration
|
||||
* values in the main configuration service
|
||||
* @param {Function} [opts.config] - A function that produces a configuration
|
||||
* schema using Joi, which is passed as its
|
||||
* first argument.
|
||||
|
@ -57,6 +60,7 @@ module.exports = class Plugin {
|
|||
this.requiredIds = opts.require || [];
|
||||
this.version = opts.version || pkg.version;
|
||||
this.externalInit = opts.init || _.noop;
|
||||
this.configPrefix = opts.configPrefix || this.id;
|
||||
this.getConfigSchema = opts.config || _.noop;
|
||||
this.init = _.once(this.init);
|
||||
this[extendInitFns] = [];
|
||||
|
@ -86,18 +90,18 @@ module.exports = class Plugin {
|
|||
async readConfig() {
|
||||
let schema = await this.getConfigSchema(Joi);
|
||||
let { config } = this.kbnServer;
|
||||
config.extendSchema(this.id, schema || defaultConfigSchema);
|
||||
config.extendSchema(this.configPrefix, schema || defaultConfigSchema);
|
||||
|
||||
if (config.get([this.id, 'enabled'])) {
|
||||
if (config.get([...toPath(this.configPrefix), 'enabled'])) {
|
||||
return true;
|
||||
} else {
|
||||
config.removeSchema(this.id);
|
||||
config.removeSchema(this.configPrefix);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
let { id, version, kbnServer } = this;
|
||||
let { id, version, kbnServer, configPrefix } = this;
|
||||
let { config } = kbnServer;
|
||||
|
||||
// setup the hapi register function and get on with it
|
||||
|
@ -132,7 +136,7 @@ module.exports = class Plugin {
|
|||
await fromNode(cb => {
|
||||
kbnServer.server.register({
|
||||
register: register,
|
||||
options: config.has(id) ? config.get(id) : null
|
||||
options: config.has(configPrefix) ? config.get(configPrefix) : null
|
||||
}, cb);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue