Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export function registerPost(server) {
}
},
handler: async function (req, reply) {
const config = await server.uiSettings().getAll();
const uiSettings = server.uiSettings();
const metaFields = await uiSettings.get('metaFields');
const boundCallWithRequest = _.partial(server.plugins.elasticsearch.callWithRequest, req);
const requestDocument = _.cloneDeep(req.payload);
const indexPattern = keysToCamelCaseShallow(requestDocument.index_pattern);
Expand All @@ -74,8 +75,6 @@ export function registerPost(server) {
delete indexPattern.id;

const mappings = createMappingsFromPatternFields(indexPattern.fields);

const metaFields = _.get(config, 'metaFields.userValue', config.metaFields.value);
const indexPatternMetaFields = _.map(metaFields, name => ({name}));

indexPattern.fields = initDefaultFieldProps(indexPattern.fields.concat(indexPatternMetaFields));
Expand Down
308 changes: 308 additions & 0 deletions src/ui/settings/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
import { isEqual } from 'lodash';
import sinon from 'sinon';
import expect from 'expect.js';
import init from '..';
import defaultsProvider from '../defaults';

describe('ui settings', function () {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all the anonymous functions can be replaced with fat arrow functions in this file, to get more ES6ish.

describe('overview', function () {
it('has expected api surface', function () {
const { uiSettings } = instantiate();
expect(typeof uiSettings.get).to.equal('function');
expect(typeof uiSettings.getAll).to.equal('function');
expect(typeof uiSettings.getDefaults).to.equal('function');
expect(typeof uiSettings.getRaw).to.equal('function');
expect(typeof uiSettings.getUserProvided).to.equal('function');
expect(typeof uiSettings.remove).to.equal('function');
expect(typeof uiSettings.removeMany).to.equal('function');
expect(typeof uiSettings.set).to.equal('function');
expect(typeof uiSettings.setMany).to.equal('function');
});
});

describe('#setMany()', function () {
it('returns a promise', () => {
const { uiSettings } = instantiate();
const result = uiSettings.setMany({ a: 'b' });
expect(result).to.be.a(Promise);
});

it('updates a single value in one operation', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.setMany({ one: 'value' });
expectElasticsearchUpdateQuery(server, configGet, {
one: 'value'
});
});

it('updates several values in one operation', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.setMany({ one: 'value', another: 'val' });
expectElasticsearchUpdateQuery(server, configGet, {
one: 'value', another: 'val'
});
});
});

describe('#set()', function () {
it('returns a promise', () => {
const { uiSettings } = instantiate();
const result = uiSettings.set('a', 'b');
expect(result).to.be.a(Promise);
});

it('updates single values by (key, value)', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.set('one', 'value');
expectElasticsearchUpdateQuery(server, configGet, {
one: 'value'
});
});
});

describe('#remove()', function () {
it('returns a promise', () => {
const { uiSettings } = instantiate();
const result = uiSettings.remove('one');
expect(result).to.be.a(Promise);
});

it('removes single values by key', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.remove('one');
expectElasticsearchUpdateQuery(server, configGet, {
one: null
});
});
});

describe('#removeMany()', function () {
it('returns a promise', () => {
const { uiSettings } = instantiate();
const result = uiSettings.removeMany(['one']);
expect(result).to.be.a(Promise);
});

it('removes a single value', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.removeMany(['one']);
expectElasticsearchUpdateQuery(server, configGet, {
one: null
});
});

it('updates several values in one operation', function () {
const { server, uiSettings, configGet } = instantiate();
const result = uiSettings.removeMany(['one', 'two', 'three']);
expectElasticsearchUpdateQuery(server, configGet, {
one: null, two: null, three: null
});
});
});

describe('#getDefaults()', function () {
it('is promised the default values', async function () {
const { server, uiSettings, configGet } = instantiate();
const defaults = await uiSettings.getDefaults();
expect(isEqual(defaults, defaultsProvider())).to.equal(true);
});
});

describe('#getUserProvided()', function () {
it('pulls user configuration from ES', async function () {
const getResult = { user: 'customized' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getUserProvided();
expectElasticsearchGetQuery(server, configGet);
});

it('returns user configuration', async function () {
const getResult = { user: 'customized' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getUserProvided();
expect(isEqual(result, {
user: { userValue: 'customized' }
})).to.equal(true);
});

it('ignores null user configuration (because default values)', async function () {
const getResult = { user: 'customized', usingDefault: null, something: 'else' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getUserProvided();
expect(isEqual(result, {
user: { userValue: 'customized' }, something: { userValue: 'else' }
})).to.equal(true);
});
});

describe('#getRaw()', function () {
it('pulls user configuration from ES', async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getRaw();
expectElasticsearchGetQuery(server, configGet);
});

it(`without user configuration it's equal to the defaults`, async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getRaw();
expect(isEqual(result, defaultsProvider())).to.equal(true);
});

it(`user configuration gets merged with defaults`, async function () {
const getResult = { foo: 'bar' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getRaw();
const merged = defaultsProvider();
merged.foo = { userValue: 'bar' };
expect(isEqual(result, merged)).to.equal(true);
});

it(`user configuration gets merged into defaults`, async function () {
const getResult = { dateFormat: 'YYYY-MM-DD' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getRaw();
const merged = defaultsProvider();
merged.dateFormat.userValue = 'YYYY-MM-DD';
expect(isEqual(result, merged)).to.equal(true);
});
});

describe('#getAll()', function () {
it('pulls user configuration from ES', async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getAll();
expectElasticsearchGetQuery(server, configGet);
});

it(`returns key value pairs`, async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getAll();
const defaults = defaultsProvider();
const expectation = {};
Object.keys(defaults).forEach(key => {
expectation[key] = defaults[key].value;
});
expect(isEqual(result, expectation)).to.equal(true);
});

it(`returns key value pairs including user configuration`, async function () {
const getResult = { something: 'user-provided' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getAll();
const defaults = defaultsProvider();
const expectation = {};
Object.keys(defaults).forEach(key => {
expectation[key] = defaults[key].value;
});
expectation.something = 'user-provided';
expect(isEqual(result, expectation)).to.equal(true);
});

it(`returns key value pairs including user configuration for existing settings`, async function () {
const getResult = { dateFormat: 'YYYY-MM-DD' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.getAll();
const defaults = defaultsProvider();
const expectation = {};
Object.keys(defaults).forEach(key => {
expectation[key] = defaults[key].value;
});
expectation.dateFormat = 'YYYY-MM-DD';
expect(isEqual(result, expectation)).to.equal(true);
});
});

describe('#get()', function () {
it('pulls user configuration from ES', async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.get();
expectElasticsearchGetQuery(server, configGet);
});

it(`returns the promised value for a key`, async function () {
const getResult = {};
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.get('dateFormat');
const defaults = defaultsProvider();
expect(result).to.equal(defaults.dateFormat.value);
});

it(`returns the user-configured value for a custom key`, async function () {
const getResult = { custom: 'value' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.get('custom');
expect(result).to.equal('value');
});

it(`returns the user-configured value for a modified key`, async function () {
const getResult = { dateFormat: 'YYYY-MM-DD' };
const { server, uiSettings, configGet } = instantiate({ getResult });
const result = await uiSettings.get('dateFormat');
expect(result).to.equal('YYYY-MM-DD');
});
});
});

function expectElasticsearchGetQuery(server, configGet) {
expect(server.plugins.elasticsearch.client.get.callCount).to.equal(1);
expect(isEqual(server.plugins.elasticsearch.client.get.firstCall.args, [{
index: configGet('kibana.index'),
id: configGet('pkg.version'),
type: 'config'
}])).to.equal(true);
}

function expectElasticsearchUpdateQuery(server, configGet, doc) {
expect(server.plugins.elasticsearch.client.update.callCount).to.equal(1);
expect(isEqual(server.plugins.elasticsearch.client.update.firstCall.args, [{
index: configGet('kibana.index'),
id: configGet('pkg.version'),
type: 'config',
body: { doc }
}])).to.equal(true);
}

function instantiate({ getResult } = {}) {
const esStatus = {
state: 'green',
on: sinon.spy()
};
const settingsStatus = {
state: 'green',
red: sinon.spy(),
yellow: sinon.spy(),
green: sinon.spy()
};
const kbnServer = {
status: {
create: sinon.stub().withArgs('ui settings').returns(settingsStatus),
getForPluginId: sinon.stub().withArgs('elasticsearch').returns(esStatus)
},
ready: sinon.stub().returns(Promise.resolve())
};
const server = {
decorate: (_, key, value) => server[key] = value,
plugins: {
elasticsearch: {
client: {
get: sinon.stub().returns(Promise.resolve({ _source: getResult })),
update: sinon.stub().returns(Promise.resolve())
}
}
}
};
const configGet = sinon.stub();
configGet.withArgs('kibana.index').returns('.kibana');
configGet.withArgs('pkg.version').returns('1.2.3-test');
const config = {
get: configGet
};
const setupSettings = init(kbnServer, server, config);
const uiSettings = server.uiSettings();
return { server, uiSettings, configGet };
}
43 changes: 40 additions & 3 deletions src/ui/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,47 @@ import defaultsProvider from './defaults';
export default function setupSettings(kbnServer, server, config) {
const status = kbnServer.status.create('ui settings');
const uiSettings = {
// returns a Promise for the value of the requested setting
get,
// returns a Promise for a hash of setting key/value pairs
getAll,
getDefaults,
getUserProvided,
// .set(key, value), returns a Promise for persisting the new value to ES
set,
// takes a key/value hash, returns a Promise for persisting the new values to ES
setMany,
remove
// returns a Promise for removing the provided key from user-specific settings
remove,
// takes an array, returns a Promise for removing every provided key from user-specific settings
removeMany,

// returns a Promise for the default settings, follows metadata format (see ./defaults)
getDefaults,
// returns a Promise for user-specific settings stored in ES, follows metadata format
getUserProvided,
// returns a Promise merging results of getDefaults & getUserProvided, follows metadata format
getRaw
};

server.decorate('server', 'uiSettings', () => uiSettings);
kbnServer.ready().then(mirrorEsStatus);

function get(key) {
return getAll().then(all => all[key]);
}

function getAll() {
return getRaw()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably have some tests for these higher level methods.

.then(raw => Object.keys(raw)
.reduce((all, key) => {
const item = raw[key];
const hasUserValue = 'userValue' in item;
all[key] = hasUserValue ? item.userValue : item.value;
return all;
}, {})
);
}

function getRaw() {
return Promise
.all([getDefaults(), getUserProvided()])
.then(([defaults, user]) => defaultsDeep(user, defaults));
Expand Down Expand Up @@ -59,6 +88,14 @@ export default function setupSettings(kbnServer, server, config) {
return set(key, null);
}

function removeMany(keys) {
const changes = {};
keys.forEach(key => {
changes[key] = null;
});
return setMany(changes);
}

function mirrorEsStatus() {
const esStatus = kbnServer.status.getForPluginId('elasticsearch');

Expand Down
Loading