mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[uiSettings] make service request based (#12243)
* [server/uiSettings] make uiSettings service request based * [server/uiSettings] disambiguate UiSettings/Service * [server/uiSettings] link to PR in removal error * [server/uiSettings] await _read before hydrating * [server/uiSettings] focus tests, remove server integration * [server/uiSettings] add tests for readInterceptor() arg * [server/uiSettings] add server integration tests * [server/uiExports] fix replaceInjectedVars tests * [server/uiSettings] convert all methods to use async/await * [uiSettings/serviceFactory] fix doc block * [uiSettings/service] fix doc block * [uiSettings/tests/callClusterStub] stop tracking state needlessly * [uiSettings/tests] remove invalid tests and pointless promise helpers * [uiSettings/forRequest] fix typo * [uiSettings/tests] remove mixture of arrow and function expressions * [uiSettings/tests/callClusterStub] leverage sinon.calledWithExactly * [uiSettings/mixin/tests] add exception for eslint import/no-duplicates * [uiSettings/mixin/tests] wrap single args in parens
This commit is contained in:
parent
dd072c8596
commit
65d6b5d309
19 changed files with 879 additions and 639 deletions
|
@ -6,11 +6,12 @@ export default function registerDelete(server) {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
handler: function (req, reply) {
|
handler: function (req, reply) {
|
||||||
const { key } = req.params;
|
const { key } = req.params;
|
||||||
const uiSettings = server.uiSettings();
|
const uiSettings = req.getUiSettingsService();
|
||||||
|
|
||||||
uiSettings
|
uiSettings
|
||||||
.remove(req, key)
|
.remove(key)
|
||||||
.then(() => uiSettings
|
.then(() => uiSettings
|
||||||
.getUserProvided(req)
|
.getUserProvided()
|
||||||
.then(settings => reply({ settings }).type('application/json'))
|
.then(settings => reply({ settings }).type('application/json'))
|
||||||
)
|
)
|
||||||
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
||||||
|
|
|
@ -5,9 +5,9 @@ export default function registerGet(server) {
|
||||||
path: '/api/kibana/settings',
|
path: '/api/kibana/settings',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
handler: function (req, reply) {
|
handler: function (req, reply) {
|
||||||
server
|
req
|
||||||
.uiSettings()
|
.getUiSettingsService()
|
||||||
.getUserProvided(req)
|
.getUserProvided()
|
||||||
.then(settings => reply({ settings }).type('application/json'))
|
.then(settings => reply({ settings }).type('application/json'))
|
||||||
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,12 @@ export default function registerSet(server) {
|
||||||
handler: function (req, reply) {
|
handler: function (req, reply) {
|
||||||
const { key } = req.params;
|
const { key } = req.params;
|
||||||
const { value } = req.payload;
|
const { value } = req.payload;
|
||||||
const uiSettings = server.uiSettings();
|
const uiSettings = req.getUiSettingsService();
|
||||||
|
|
||||||
uiSettings
|
uiSettings
|
||||||
.set(req, key, value)
|
.set(key, value)
|
||||||
.then(() => uiSettings
|
.then(() => uiSettings
|
||||||
.getUserProvided(req)
|
.getUserProvided()
|
||||||
.then(settings => reply({ settings }).type('application/json'))
|
.then(settings => reply({ settings }).type('application/json'))
|
||||||
)
|
)
|
||||||
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
||||||
|
|
|
@ -6,11 +6,12 @@ export default function registerSet(server) {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
handler: function (req, reply) {
|
handler: function (req, reply) {
|
||||||
const { changes } = req.payload;
|
const { changes } = req.payload;
|
||||||
const uiSettings = server.uiSettings();
|
const uiSettings = req.getUiSettingsService();
|
||||||
|
|
||||||
uiSettings
|
uiSettings
|
||||||
.setMany(req, changes)
|
.setMany(changes)
|
||||||
.then(() => uiSettings
|
.then(() => uiSettings
|
||||||
.getUserProvided(req)
|
.getUserProvided()
|
||||||
.then(settings => reply({ settings }).type('application/json'))
|
.then(settings => reply({ settings }).type('application/json'))
|
||||||
)
|
)
|
||||||
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
.catch(err => reply(Boom.wrap(err, err.statusCode)));
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default function (server) {
|
||||||
path: '/api/timelion/run',
|
path: '/api/timelion/run',
|
||||||
handler: async (request, reply) => {
|
handler: async (request, reply) => {
|
||||||
try {
|
try {
|
||||||
const uiSettings = await server.uiSettings().getAll(request);
|
const uiSettings = await request.getUiSettingsService().getAll();
|
||||||
|
|
||||||
const tlConfig = require('../handlers/lib/tl_config.js')({
|
const tlConfig = require('../handlers/lib/tl_config.js')({
|
||||||
server,
|
server,
|
||||||
|
|
|
@ -3,7 +3,7 @@ export default function (server) {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/api/timelion/validate/es',
|
path: '/api/timelion/validate/es',
|
||||||
handler: function (request, reply) {
|
handler: function (request, reply) {
|
||||||
return server.uiSettings().getAll(request).then((uiSettings) => {
|
return request.getUiSettingsService().getAll().then((uiSettings) => {
|
||||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
||||||
|
|
||||||
const timefield = uiSettings['timelion:es.timefield'];
|
const timefield = uiSettings['timelion:es.timefield'];
|
||||||
|
|
|
@ -120,8 +120,8 @@ export default async function (kbnServer, server, config) {
|
||||||
const url = await shortUrlLookup.getUrl(request.params.urlId, request);
|
const url = await shortUrlLookup.getUrl(request.params.urlId, request);
|
||||||
shortUrlAssertValid(url);
|
shortUrlAssertValid(url);
|
||||||
|
|
||||||
const uiSettings = server.uiSettings();
|
const uiSettings = request.getUiSettingsService();
|
||||||
const stateStoreInSessionStorage = await uiSettings.get(request, 'state:storeInSessionStorage');
|
const stateStoreInSessionStorage = await uiSettings.get('state:storeInSessionStorage');
|
||||||
if (!stateStoreInSessionStorage) {
|
if (!stateStoreInSessionStorage) {
|
||||||
reply().redirect(config.get('server.basePath') + url);
|
reply().redirect(config.get('server.basePath') + url);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe('UiExports', function () {
|
||||||
|
|
||||||
await kbnServer.ready();
|
await kbnServer.ready();
|
||||||
kbnServer.status.get('ui settings').state = 'green';
|
kbnServer.status.get('ui settings').state = 'green';
|
||||||
kbnServer.server.decorate('server', 'uiSettings', () => {
|
kbnServer.server.decorate('request', 'getUiSettingsService', () => {
|
||||||
return { getDefaults: noop, getUserProvided: noop };
|
return { getDefaults: noop, getUserProvided: noop };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -66,7 +66,7 @@ export default async (kbnServer, server, config) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getKibanaPayload({ app, request, includeUserProvidedConfig, injectedVarsOverrides }) {
|
async function getKibanaPayload({ app, request, includeUserProvidedConfig, injectedVarsOverrides }) {
|
||||||
const uiSettings = server.uiSettings();
|
const uiSettings = request.getUiSettingsService();
|
||||||
const translations = await uiI18n.getTranslationsForRequest(request);
|
const translations = await uiI18n.getTranslationsForRequest(request);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -82,7 +82,7 @@ export default async (kbnServer, server, config) => {
|
||||||
translations: translations,
|
translations: translations,
|
||||||
uiSettings: await props({
|
uiSettings: await props({
|
||||||
defaults: uiSettings.getDefaults(),
|
defaults: uiSettings.getDefaults(),
|
||||||
user: includeUserProvidedConfig && uiSettings.getUserProvided(request)
|
user: includeUserProvidedConfig && uiSettings.getUserProvided()
|
||||||
}),
|
}),
|
||||||
vars: await reduceAsync(
|
vars: await reduceAsync(
|
||||||
uiExports.injectedVarsReplacers,
|
uiExports.injectedVarsReplacers,
|
||||||
|
|
|
@ -1,497 +0,0 @@
|
||||||
import { isEqual } from 'lodash';
|
|
||||||
import sinon from 'sinon';
|
|
||||||
import expect from 'expect.js';
|
|
||||||
import { uiSettingsMixin } from '../ui_settings_mixin';
|
|
||||||
import { getDefaultSettings } from '../defaults';
|
|
||||||
import { errors as esErrors } from 'elasticsearch';
|
|
||||||
|
|
||||||
async function expectRejection(promise, errorMessageContain) {
|
|
||||||
if (!promise || typeof promise.then !== 'function') {
|
|
||||||
throw new Error('Expected function to return a promise');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await promise;
|
|
||||||
} catch (err) {
|
|
||||||
expect(err.message).to.contain(errorMessageContain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('ui settings', function () {
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if the first error is not a request', async () => {
|
|
||||||
const { uiSettings } = instantiate();
|
|
||||||
await expectRejection(uiSettings.get(null), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get(false), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get('key'), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get(/regex/), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get(new Date()), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get({}), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get({ path:'' }), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get({ path:'', headers:null }), 'hapi.Request');
|
|
||||||
await expectRejection(uiSettings.get({ headers:{} }), 'hapi.Request');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#setMany()', function () {
|
|
||||||
it('returns a promise', () => {
|
|
||||||
const { uiSettings, req } = instantiate();
|
|
||||||
const result = uiSettings.setMany(req, { a: 'b' });
|
|
||||||
expect(result).to.be.a(Promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates a single value in one operation', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.setMany(req, { one: 'value' });
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: 'value'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates several values in one operation', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.setMany(req, { one: 'value', another: 'val' });
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: 'value', another: 'val'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#set()', function () {
|
|
||||||
it('returns a promise', () => {
|
|
||||||
const { uiSettings, req } = instantiate();
|
|
||||||
const result = uiSettings.set(req, 'a', 'b');
|
|
||||||
expect(result).to.be.a(Promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates single values by (key, value)', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.set(req, 'one', 'value');
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: 'value'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#remove()', function () {
|
|
||||||
it('returns a promise', () => {
|
|
||||||
const { uiSettings, req } = instantiate();
|
|
||||||
const result = uiSettings.remove(req, 'one');
|
|
||||||
expect(result).to.be.a(Promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes single values by key', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.remove(req, 'one');
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: null
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#removeMany()', function () {
|
|
||||||
it('returns a promise', () => {
|
|
||||||
const { uiSettings, req } = instantiate();
|
|
||||||
const result = uiSettings.removeMany(req, ['one']);
|
|
||||||
expect(result).to.be.a(Promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes a single value', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.removeMany(req, ['one']);
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: null
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates several values in one operation', function () {
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate();
|
|
||||||
uiSettings.removeMany(req, ['one', 'two', 'three']);
|
|
||||||
expectElasticsearchUpdateQuery(server, req, configGet, {
|
|
||||||
one: null, two: null, three: null
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getDefaults()', function () {
|
|
||||||
it('is promised the default values', async function () {
|
|
||||||
const {
|
|
||||||
uiSettings
|
|
||||||
} = instantiate();
|
|
||||||
const defaults = await uiSettings.getDefaults();
|
|
||||||
expect(isEqual(defaults, getDefaultSettings())).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('defaults for formatters', async function () {
|
|
||||||
|
|
||||||
const defaults = getDefaultSettings();
|
|
||||||
const mapping = JSON.parse(defaults['format:defaultTypeMap'].value);
|
|
||||||
const expected = {
|
|
||||||
ip: { id: 'ip', params: {} },
|
|
||||||
date: { id: 'date', params: {} },
|
|
||||||
number: { id: 'number', params: {} },
|
|
||||||
boolean: { id: 'boolean', params: {} },
|
|
||||||
_source: { id: '_source', params: {} },
|
|
||||||
_default_: { id: 'string', params: {} }
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(mapping).forEach(function (dataType) {
|
|
||||||
it(`should configure ${dataType}`, function () {
|
|
||||||
expect(expected.hasOwnProperty(dataType)).to.equal(true);
|
|
||||||
expect(mapping[dataType].id).to.equal(expected[dataType].id);
|
|
||||||
expect(JSON.stringify(mapping[dataType].params)).to.equal(JSON.stringify(expected[dataType].params));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getUserProvided()', function () {
|
|
||||||
it('pulls user configuration from ES', async function () {
|
|
||||||
const getResult = { user: 'customized' };
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate({ getResult });
|
|
||||||
await uiSettings.getUserProvided(req);
|
|
||||||
expectElasticsearchGetQuery(server, req, configGet);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns user configuration', async function () {
|
|
||||||
const getResult = { user: 'customized' };
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getUserProvided(req);
|
|
||||||
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 {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getUserProvided(req);
|
|
||||||
expect(isEqual(result, {
|
|
||||||
user: { userValue: 'customized' }, something: { userValue: 'else' }
|
|
||||||
})).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty object when status is not green', async function () {
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
settingsStatusOverrides: { state: 'yellow' }
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await uiSettings.getUserProvided(req)).to.eql({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty object on 404 responses', async function () {
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
async callWithRequest() {
|
|
||||||
throw new esErrors[404]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await uiSettings.getUserProvided(req)).to.eql({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty object on 403 responses', async function () {
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
async callWithRequest() {
|
|
||||||
throw new esErrors[403]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await uiSettings.getUserProvided(req)).to.eql({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty object on NoConnections responses', async function () {
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
async callWithRequest() {
|
|
||||||
throw new esErrors.NoConnections();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await uiSettings.getUserProvided(req)).to.eql({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws 401 errors', async function () {
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
async callWithRequest() {
|
|
||||||
throw new esErrors[401]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await uiSettings.getUserProvided(req);
|
|
||||||
throw new Error('expect getUserProvided() to throw');
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be.a(esErrors[401]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throw when callWithRequest fails in some unexpected way', async function () {
|
|
||||||
const expectedUnexpectedError = new Error('unexpected');
|
|
||||||
|
|
||||||
const { uiSettings, req } = instantiate({
|
|
||||||
async callWithRequest() {
|
|
||||||
throw expectedUnexpectedError;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await uiSettings.getUserProvided(req);
|
|
||||||
throw new Error('expect getUserProvided() to throw');
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be(expectedUnexpectedError);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getRaw()', function () {
|
|
||||||
it('pulls user configuration from ES', async function () {
|
|
||||||
const getResult = {};
|
|
||||||
const { server, uiSettings, configGet, req } = instantiate({ getResult });
|
|
||||||
await uiSettings.getRaw(req);
|
|
||||||
expectElasticsearchGetQuery(server, req, configGet);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`without user configuration it's equal to the defaults`, async function () {
|
|
||||||
const getResult = {};
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getRaw(req);
|
|
||||||
expect(isEqual(result, getDefaultSettings())).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`user configuration gets merged with defaults`, async function () {
|
|
||||||
const getResult = { foo: 'bar' };
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getRaw(req);
|
|
||||||
const merged = getDefaultSettings();
|
|
||||||
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 {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getRaw(req);
|
|
||||||
const merged = getDefaultSettings();
|
|
||||||
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, req } = instantiate({ getResult });
|
|
||||||
await uiSettings.getAll(req);
|
|
||||||
expectElasticsearchGetQuery(server, req, configGet);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`returns key value pairs`, async function () {
|
|
||||||
const getResult = {};
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getAll(req);
|
|
||||||
const defaults = getDefaultSettings();
|
|
||||||
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 {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getAll(req);
|
|
||||||
const defaults = getDefaultSettings();
|
|
||||||
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 {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.getAll(req);
|
|
||||||
const defaults = getDefaultSettings();
|
|
||||||
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, req } = instantiate({ getResult });
|
|
||||||
await uiSettings.get(req);
|
|
||||||
expectElasticsearchGetQuery(server, req, configGet);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`returns the promised value for a key`, async function () {
|
|
||||||
const getResult = {};
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.get(req, 'dateFormat');
|
|
||||||
const defaults = getDefaultSettings();
|
|
||||||
expect(result).to.equal(defaults.dateFormat.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`returns the user-configured value for a custom key`, async function () {
|
|
||||||
const getResult = { custom: 'value' };
|
|
||||||
const {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.get(req, '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 {
|
|
||||||
uiSettings,
|
|
||||||
req
|
|
||||||
} = instantiate({ getResult });
|
|
||||||
const result = await uiSettings.get(req, 'dateFormat');
|
|
||||||
expect(result).to.equal('YYYY-MM-DD');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function expectElasticsearchGetQuery(server, req, configGet) {
|
|
||||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
|
|
||||||
sinon.assert.calledOnce(callWithRequest);
|
|
||||||
const [reqPassed, method, params] = callWithRequest.args[0];
|
|
||||||
expect(reqPassed).to.be(req);
|
|
||||||
expect(method).to.be('get');
|
|
||||||
expect(params).to.eql({
|
|
||||||
index: configGet('kibana.index'),
|
|
||||||
id: configGet('pkg.version'),
|
|
||||||
type: 'config'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectElasticsearchUpdateQuery(server, req, configGet, doc) {
|
|
||||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
|
|
||||||
sinon.assert.calledOnce(callWithRequest);
|
|
||||||
const [reqPassed, method, params] = callWithRequest.args[0];
|
|
||||||
expect(reqPassed).to.be(req);
|
|
||||||
expect(method).to.be('update');
|
|
||||||
expect(params).to.eql({
|
|
||||||
index: configGet('kibana.index'),
|
|
||||||
id: configGet('pkg.version'),
|
|
||||||
type: 'config',
|
|
||||||
body: { doc }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function instantiate({ getResult, callWithRequest, settingsStatusOverrides } = {}) {
|
|
||||||
const esStatus = {
|
|
||||||
state: 'green',
|
|
||||||
on: sinon.spy()
|
|
||||||
};
|
|
||||||
const settingsStatus = {
|
|
||||||
state: 'green',
|
|
||||||
red: sinon.spy(),
|
|
||||||
yellow: sinon.spy(),
|
|
||||||
green: sinon.spy(),
|
|
||||||
...settingsStatusOverrides
|
|
||||||
};
|
|
||||||
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 req = { __stubHapiRequest: true, path: '', headers: {} };
|
|
||||||
|
|
||||||
const adminCluster = {
|
|
||||||
errors: esErrors,
|
|
||||||
callWithInternalUser: sinon.stub(),
|
|
||||||
callWithRequest: sinon.spy((withReq, method, params) => {
|
|
||||||
if (callWithRequest) {
|
|
||||||
return callWithRequest(withReq, method, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(withReq).to.be(req);
|
|
||||||
switch (method) {
|
|
||||||
case 'get':
|
|
||||||
return Promise.resolve({ _source: getResult });
|
|
||||||
case 'update':
|
|
||||||
return Promise.resolve();
|
|
||||||
default:
|
|
||||||
throw new Error(`callWithRequest() is using unexpected method "${method}"`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
adminCluster.callWithInternalUser.withArgs('get', sinon.match.any).returns(Promise.resolve({ _source: getResult }));
|
|
||||||
adminCluster.callWithInternalUser.withArgs('update', sinon.match.any).returns(Promise.resolve());
|
|
||||||
|
|
||||||
const configGet = sinon.stub();
|
|
||||||
configGet.withArgs('kibana.index').returns('.kibana');
|
|
||||||
configGet.withArgs('pkg.version').returns('1.2.3-test');
|
|
||||||
configGet.withArgs('uiSettings.enabled').returns(true);
|
|
||||||
const config = {
|
|
||||||
get: configGet
|
|
||||||
};
|
|
||||||
|
|
||||||
const server = {
|
|
||||||
config: () => config,
|
|
||||||
decorate: (_, key, value) => server[key] = value,
|
|
||||||
plugins: {
|
|
||||||
elasticsearch: {
|
|
||||||
getCluster: sinon.stub().withArgs('admin').returns(adminCluster)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
uiSettingsMixin(kbnServer, server, config);
|
|
||||||
const uiSettings = server.uiSettings();
|
|
||||||
return { server, uiSettings, configGet, req };
|
|
||||||
}
|
|
41
src/ui/ui_settings/__tests__/lib/call_cluster_stub.js
Normal file
41
src/ui/ui_settings/__tests__/lib/call_cluster_stub.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import expect from 'expect.js';
|
||||||
|
|
||||||
|
export function createCallClusterStub(index, type, id, esDocSource) {
|
||||||
|
const callCluster = sinon.spy(async (method, params) => {
|
||||||
|
expect(params)
|
||||||
|
.to.have.property('index', index)
|
||||||
|
.and.to.have.property('type', type)
|
||||||
|
.and.to.have.property('id', id);
|
||||||
|
|
||||||
|
switch (method) {
|
||||||
|
case 'get':
|
||||||
|
return { _source: { ...esDocSource } };
|
||||||
|
|
||||||
|
case 'update':
|
||||||
|
expect(params).to.have.property('body');
|
||||||
|
expect(params.body).to.have.property('doc');
|
||||||
|
return {};
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`unexpected es method ${method}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
callCluster.assertGetQuery = () => {
|
||||||
|
sinon.assert.calledOnce(callCluster);
|
||||||
|
sinon.assert.calledWith(callCluster, 'get');
|
||||||
|
};
|
||||||
|
|
||||||
|
callCluster.assertUpdateQuery = doc => {
|
||||||
|
sinon.assert.calledOnce(callCluster);
|
||||||
|
sinon.assert.calledWithExactly(callCluster, 'update', {
|
||||||
|
index,
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
body: { doc }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return callCluster;
|
||||||
|
}
|
1
src/ui/ui_settings/__tests__/lib/index.js
Normal file
1
src/ui/ui_settings/__tests__/lib/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { createCallClusterStub } from './call_cluster_stub';
|
197
src/ui/ui_settings/__tests__/ui_settings_mixin_integration.js
Normal file
197
src/ui/ui_settings/__tests__/ui_settings_mixin_integration.js
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import expect from 'expect.js';
|
||||||
|
import Chance from 'chance';
|
||||||
|
|
||||||
|
import ServerStatus from '../../../server/status/server_status';
|
||||||
|
import Config from '../../../server/config/config';
|
||||||
|
|
||||||
|
/* eslint-disable import/no-duplicates */
|
||||||
|
import * as uiSettingsServiceFactoryNS from '../ui_settings_service_factory';
|
||||||
|
import { uiSettingsServiceFactory } from '../ui_settings_service_factory';
|
||||||
|
import * as getUiSettingsServiceForRequestNS from '../ui_settings_service_for_request';
|
||||||
|
import { getUiSettingsServiceForRequest } from '../ui_settings_service_for_request';
|
||||||
|
/* eslint-enable import/no-duplicates */
|
||||||
|
|
||||||
|
import { uiSettingsMixin } from '../ui_settings_mixin';
|
||||||
|
|
||||||
|
const chance = new Chance();
|
||||||
|
|
||||||
|
describe('uiSettingsMixin()', () => {
|
||||||
|
const sandbox = sinon.sandbox.create();
|
||||||
|
|
||||||
|
function setup(options = {}) {
|
||||||
|
const {
|
||||||
|
enabled = true
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const config = Config.withDefaultSchema({
|
||||||
|
uiSettings: { enabled }
|
||||||
|
});
|
||||||
|
|
||||||
|
// maps of decorations passed to `server.decorate()`
|
||||||
|
const decorations = {
|
||||||
|
server: {},
|
||||||
|
request: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// mock hapi server
|
||||||
|
const server = {
|
||||||
|
log: sinon.stub(),
|
||||||
|
config: () => config,
|
||||||
|
decorate: sinon.spy((type, name, value) => {
|
||||||
|
decorations[type][name] = value;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// "promise" returned from kbnServer.ready()
|
||||||
|
const readyPromise = {
|
||||||
|
then: sinon.stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const kbnServer = {
|
||||||
|
server,
|
||||||
|
config,
|
||||||
|
status: new ServerStatus(server),
|
||||||
|
ready: sinon.stub().returns(readyPromise),
|
||||||
|
};
|
||||||
|
|
||||||
|
uiSettingsMixin(kbnServer, server, config);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kbnServer,
|
||||||
|
server,
|
||||||
|
decorations,
|
||||||
|
readyPromise,
|
||||||
|
status: kbnServer.status.get('ui settings'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => sandbox.restore());
|
||||||
|
|
||||||
|
describe('status', () => {
|
||||||
|
it('creates a "ui settings" status', () => {
|
||||||
|
const { status } = setup();
|
||||||
|
expect(status).to.have.property('state', 'uninitialized');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('disabled', () => {
|
||||||
|
it('disables if uiSettings.enabled config is false', () => {
|
||||||
|
const { status } = setup({ enabled: false });
|
||||||
|
expect(status).to.have.property('state', 'disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not register a handler for kbnServer.ready()', () => {
|
||||||
|
const { readyPromise } = setup({ enabled: false });
|
||||||
|
sinon.assert.notCalled(readyPromise.then);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enabled', () => {
|
||||||
|
it('registers a handler for kbnServer.ready()', () => {
|
||||||
|
const { readyPromise } = setup();
|
||||||
|
sinon.assert.calledOnce(readyPromise.then);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mirrors the elasticsearch plugin status once kibanaServer.ready() resolves', () => {
|
||||||
|
const { kbnServer, readyPromise, status } = setup();
|
||||||
|
const esStatus = kbnServer.status.createForPlugin({
|
||||||
|
id: 'elasticsearch',
|
||||||
|
version: 'kibana',
|
||||||
|
});
|
||||||
|
|
||||||
|
esStatus.green();
|
||||||
|
expect(status).to.have.property('state', 'uninitialized');
|
||||||
|
const readyPromiseHandler = readyPromise.then.firstCall.args[0];
|
||||||
|
readyPromiseHandler();
|
||||||
|
expect(status).to.have.property('state', 'green');
|
||||||
|
|
||||||
|
|
||||||
|
const states = chance.shuffle(['red', 'green', 'yellow']);
|
||||||
|
states.forEach((state) => {
|
||||||
|
esStatus[state]();
|
||||||
|
expect(esStatus).to.have.property('state', state);
|
||||||
|
expect(status).to.have.property('state', state);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('server.uiSettingsServiceFactory()', () => {
|
||||||
|
it('decorates server with "uiSettingsServiceFactory"', () => {
|
||||||
|
const { decorations } = setup();
|
||||||
|
expect(decorations.server).to.have.property('uiSettingsServiceFactory').a('function');
|
||||||
|
|
||||||
|
sandbox.stub(uiSettingsServiceFactoryNS, 'uiSettingsServiceFactory');
|
||||||
|
sinon.assert.notCalled(uiSettingsServiceFactory);
|
||||||
|
decorations.server.uiSettingsServiceFactory();
|
||||||
|
sinon.assert.calledOnce(uiSettingsServiceFactory);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes `server` and `options` argument to factory', () => {
|
||||||
|
const { decorations, server } = setup();
|
||||||
|
expect(decorations.server).to.have.property('uiSettingsServiceFactory').a('function');
|
||||||
|
|
||||||
|
sandbox.stub(uiSettingsServiceFactoryNS, 'uiSettingsServiceFactory');
|
||||||
|
sinon.assert.notCalled(uiSettingsServiceFactory);
|
||||||
|
const football = {};
|
||||||
|
decorations.server.uiSettingsServiceFactory(football);
|
||||||
|
sinon.assert.calledWith(uiSettingsServiceFactory, server, football);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('request.getUiSettingsService()', () => {
|
||||||
|
it('exposes "getUiSettingsService" on requests', () => {
|
||||||
|
const { decorations } = setup();
|
||||||
|
expect(decorations.request).to.have.property('getUiSettingsService').a('function');
|
||||||
|
|
||||||
|
sandbox.stub(getUiSettingsServiceForRequestNS, 'getUiSettingsServiceForRequest');
|
||||||
|
sinon.assert.notCalled(getUiSettingsServiceForRequest);
|
||||||
|
decorations.request.getUiSettingsService();
|
||||||
|
sinon.assert.calledOnce(getUiSettingsServiceForRequest);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes request to getUiSettingsServiceForRequest', () => {
|
||||||
|
const { server, decorations } = setup();
|
||||||
|
expect(decorations.request).to.have.property('getUiSettingsService').a('function');
|
||||||
|
|
||||||
|
sandbox.stub(getUiSettingsServiceForRequestNS, 'getUiSettingsServiceForRequest');
|
||||||
|
sinon.assert.notCalled(getUiSettingsServiceForRequest);
|
||||||
|
const request = {};
|
||||||
|
decorations.request.getUiSettingsService.call(request);
|
||||||
|
sinon.assert.calledWith(getUiSettingsServiceForRequest, server, request);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defines read interceptor that intercepts when status is not green', () => {
|
||||||
|
const { status, decorations } = setup();
|
||||||
|
expect(decorations.request).to.have.property('getUiSettingsService').a('function');
|
||||||
|
|
||||||
|
sandbox.stub(getUiSettingsServiceForRequestNS, 'getUiSettingsServiceForRequest');
|
||||||
|
decorations.request.getUiSettingsService();
|
||||||
|
|
||||||
|
const readInterceptor = getUiSettingsServiceForRequest.firstCall.args[2];
|
||||||
|
expect(readInterceptor).to.be.a('function');
|
||||||
|
|
||||||
|
status.green();
|
||||||
|
expect(readInterceptor()).to.be(undefined);
|
||||||
|
|
||||||
|
status.yellow();
|
||||||
|
expect(readInterceptor()).to.eql({});
|
||||||
|
|
||||||
|
status.red();
|
||||||
|
expect(readInterceptor()).to.eql({});
|
||||||
|
|
||||||
|
status.green();
|
||||||
|
expect(readInterceptor()).to.eql(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('server.uiSettings()', () => {
|
||||||
|
it('throws an error, links to pr', () => {
|
||||||
|
const { decorations } = setup();
|
||||||
|
expect(decorations.server).to.have.property('uiSettings').a('function');
|
||||||
|
expect(() => {
|
||||||
|
decorations.server.uiSettings();
|
||||||
|
}).to.throwError('http://github.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
393
src/ui/ui_settings/__tests__/ui_settings_service.js
Normal file
393
src/ui/ui_settings/__tests__/ui_settings_service.js
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import expect from 'expect.js';
|
||||||
|
import { errors as esErrors } from 'elasticsearch';
|
||||||
|
|
||||||
|
import { getDefaultSettings } from '../defaults';
|
||||||
|
import { UiSettingsService } from '../ui_settings_service';
|
||||||
|
|
||||||
|
import { createCallClusterStub } from './lib';
|
||||||
|
|
||||||
|
const INDEX = '.kibana';
|
||||||
|
const TYPE = 'config';
|
||||||
|
const ID = 'kibana-version';
|
||||||
|
|
||||||
|
function setup(options = {}) {
|
||||||
|
const {
|
||||||
|
readInterceptor,
|
||||||
|
esDocSource = {},
|
||||||
|
callCluster = createCallClusterStub(INDEX, TYPE, ID, esDocSource)
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const uiSettings = new UiSettingsService({
|
||||||
|
index: INDEX,
|
||||||
|
type: TYPE,
|
||||||
|
id: ID,
|
||||||
|
readInterceptor,
|
||||||
|
callCluster,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
uiSettings,
|
||||||
|
assertGetQuery: callCluster.assertGetQuery,
|
||||||
|
assertUpdateQuery: callCluster.assertUpdateQuery,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ui settings', () => {
|
||||||
|
describe('overview', () => {
|
||||||
|
it('has expected api surface', () => {
|
||||||
|
const { uiSettings } = setup();
|
||||||
|
expect(uiSettings).to.have.property('get').a('function');
|
||||||
|
expect(uiSettings).to.have.property('getAll').a('function');
|
||||||
|
expect(uiSettings).to.have.property('getDefaults').a('function');
|
||||||
|
expect(uiSettings).to.have.property('getRaw').a('function');
|
||||||
|
expect(uiSettings).to.have.property('getUserProvided').a('function');
|
||||||
|
expect(uiSettings).to.have.property('remove').a('function');
|
||||||
|
expect(uiSettings).to.have.property('removeMany').a('function');
|
||||||
|
expect(uiSettings).to.have.property('set').a('function');
|
||||||
|
expect(uiSettings).to.have.property('setMany').a('function');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setMany()', () => {
|
||||||
|
it('returns a promise', () => {
|
||||||
|
const { uiSettings } = setup();
|
||||||
|
expect(uiSettings.setMany({ a: 'b' })).to.be.a(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates a single value in one operation', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.setMany({ one: 'value' });
|
||||||
|
assertUpdateQuery({ one: 'value' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates several values in one operation', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.setMany({ one: 'value', another: 'val' });
|
||||||
|
assertUpdateQuery({ one: 'value', another: 'val' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#set()', () => {
|
||||||
|
it('returns a promise', () => {
|
||||||
|
const { uiSettings } = setup();
|
||||||
|
expect(uiSettings.set('a', 'b')).to.be.a(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates single values by (key, value)', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.set('one', 'value');
|
||||||
|
assertUpdateQuery({ one: 'value' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#remove()', () => {
|
||||||
|
it('returns a promise', () => {
|
||||||
|
const { uiSettings } = setup();
|
||||||
|
expect(uiSettings.remove('one')).to.be.a(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes single values by key', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.remove('one');
|
||||||
|
assertUpdateQuery({ one: null });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#removeMany()', () => {
|
||||||
|
it('returns a promise', () => {
|
||||||
|
const { uiSettings } = setup();
|
||||||
|
expect(uiSettings.removeMany(['one'])).to.be.a(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes a single value', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.removeMany(['one']);
|
||||||
|
assertUpdateQuery({ one: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates several values in one operation', async () => {
|
||||||
|
const { uiSettings, assertUpdateQuery } = setup();
|
||||||
|
await uiSettings.removeMany(['one', 'two', 'three']);
|
||||||
|
assertUpdateQuery({ one: null, two: null, three: null });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getDefaults()', () => {
|
||||||
|
it('is promised the default values', async () => {
|
||||||
|
const {
|
||||||
|
uiSettings
|
||||||
|
} = setup();
|
||||||
|
const defaults = await uiSettings.getDefaults();
|
||||||
|
expect(isEqual(defaults, getDefaultSettings())).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('defaults for formatters', async () => {
|
||||||
|
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
const mapping = JSON.parse(defaults['format:defaultTypeMap'].value);
|
||||||
|
const expected = {
|
||||||
|
ip: { id: 'ip', params: {} },
|
||||||
|
date: { id: 'date', params: {} },
|
||||||
|
number: { id: 'number', params: {} },
|
||||||
|
boolean: { id: 'boolean', params: {} },
|
||||||
|
_source: { id: '_source', params: {} },
|
||||||
|
_default_: { id: 'string', params: {} }
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(mapping).forEach((dataType) => {
|
||||||
|
it(`should configure ${dataType}`, () => {
|
||||||
|
expect(expected.hasOwnProperty(dataType)).to.equal(true);
|
||||||
|
expect(mapping[dataType].id).to.equal(expected[dataType].id);
|
||||||
|
expect(JSON.stringify(mapping[dataType].params)).to.equal(JSON.stringify(expected[dataType].params));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getUserProvided()', () => {
|
||||||
|
it('pulls user configuration from ES', async () => {
|
||||||
|
const { uiSettings, assertGetQuery } = setup();
|
||||||
|
await uiSettings.getUserProvided();
|
||||||
|
assertGetQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns user configuration', async () => {
|
||||||
|
const esDocSource = { user: 'customized' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getUserProvided();
|
||||||
|
expect(isEqual(result, {
|
||||||
|
user: { userValue: 'customized' }
|
||||||
|
})).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores null user configuration (because default values)', async () => {
|
||||||
|
const esDocSource = { user: 'customized', usingDefault: null, something: 'else' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getUserProvided();
|
||||||
|
expect(isEqual(result, {
|
||||||
|
user: { userValue: 'customized' }, something: { userValue: 'else' }
|
||||||
|
})).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty object on 404 responses', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
async callCluster() {
|
||||||
|
throw new esErrors[404]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await uiSettings.getUserProvided()).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty object on 403 responses', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
async callCluster() {
|
||||||
|
throw new esErrors[403]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await uiSettings.getUserProvided()).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty object on NoConnections responses', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
async callCluster() {
|
||||||
|
throw new esErrors.NoConnections();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await uiSettings.getUserProvided()).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws 401 errors', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
async callCluster() {
|
||||||
|
throw new esErrors[401]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await uiSettings.getUserProvided();
|
||||||
|
throw new Error('expect getUserProvided() to throw');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.a(esErrors[401]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw when callCluster fails in some unexpected way', async () => {
|
||||||
|
const expectedUnexpectedError = new Error('unexpected');
|
||||||
|
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
async callCluster() {
|
||||||
|
throw expectedUnexpectedError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await uiSettings.getUserProvided();
|
||||||
|
throw new Error('expect getUserProvided() to throw');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be(expectedUnexpectedError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getRaw()', () => {
|
||||||
|
it('pulls user configuration from ES', async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings, assertGetQuery } = setup({ esDocSource });
|
||||||
|
await uiSettings.getRaw();
|
||||||
|
assertGetQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`without user configuration it's equal to the defaults`, async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getRaw();
|
||||||
|
expect(isEqual(result, getDefaultSettings())).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`user configuration gets merged with defaults`, async () => {
|
||||||
|
const esDocSource = { foo: 'bar' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getRaw();
|
||||||
|
const merged = getDefaultSettings();
|
||||||
|
merged.foo = { userValue: 'bar' };
|
||||||
|
expect(isEqual(result, merged)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`user configuration gets merged into defaults`, async () => {
|
||||||
|
const esDocSource = { dateFormat: 'YYYY-MM-DD' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getRaw();
|
||||||
|
const merged = getDefaultSettings();
|
||||||
|
merged.dateFormat.userValue = 'YYYY-MM-DD';
|
||||||
|
expect(isEqual(result, merged)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getAll()', () => {
|
||||||
|
it('pulls user configuration from ES', async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings, assertGetQuery } = setup({ esDocSource });
|
||||||
|
await uiSettings.getAll();
|
||||||
|
assertGetQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns key value pairs`, async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getAll();
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
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 () => {
|
||||||
|
const esDocSource = { something: 'user-provided' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getAll();
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
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 () => {
|
||||||
|
const esDocSource = { dateFormat: 'YYYY-MM-DD' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.getAll();
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
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()', () => {
|
||||||
|
it('pulls user configuration from ES', async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings, assertGetQuery } = setup({ esDocSource });
|
||||||
|
await uiSettings.get();
|
||||||
|
assertGetQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns the promised value for a key`, async () => {
|
||||||
|
const esDocSource = {};
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.get('dateFormat');
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
expect(result).to.equal(defaults.dateFormat.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns the user-configured value for a custom key`, async () => {
|
||||||
|
const esDocSource = { custom: 'value' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.get('custom');
|
||||||
|
expect(result).to.equal('value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns the user-configured value for a modified key`, async () => {
|
||||||
|
const esDocSource = { dateFormat: 'YYYY-MM-DD' };
|
||||||
|
const { uiSettings } = setup({ esDocSource });
|
||||||
|
const result = await uiSettings.get('dateFormat');
|
||||||
|
expect(result).to.equal('YYYY-MM-DD');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('readInterceptor() argument', () => {
|
||||||
|
describe('#getUserProvided()', () => {
|
||||||
|
it('returns a promise when interceptValue doesn\'t', () => {
|
||||||
|
const { uiSettings } = setup({ readInterceptor: () => ({}) });
|
||||||
|
expect(uiSettings.getUserProvided()).to.be.a(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns intercept values', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
readInterceptor: () => ({
|
||||||
|
foo: 'bar'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await uiSettings.getUserProvided()).to.eql({
|
||||||
|
foo: {
|
||||||
|
userValue: 'bar'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getAll()', () => {
|
||||||
|
it('merges intercept value with defaults', async () => {
|
||||||
|
const { uiSettings } = setup({
|
||||||
|
readInterceptor: () => ({
|
||||||
|
foo: 'not foo'
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaults = getDefaultSettings();
|
||||||
|
const defaultValues = Object.keys(defaults).reduce((acc, key) => ({
|
||||||
|
...acc,
|
||||||
|
[key]: defaults[key].value,
|
||||||
|
}), {});
|
||||||
|
|
||||||
|
expect(await uiSettings.getAll()).to.eql({
|
||||||
|
...defaultValues,
|
||||||
|
foo: 'not foo',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,120 +0,0 @@
|
||||||
import { defaultsDeep } from 'lodash';
|
|
||||||
import Bluebird from 'bluebird';
|
|
||||||
|
|
||||||
import { getDefaultSettings } from './defaults';
|
|
||||||
|
|
||||||
function hydrateUserSettings(user) {
|
|
||||||
return Object.keys(user)
|
|
||||||
.map(key => ({ key, userValue: user[key] }))
|
|
||||||
.filter(({ userValue }) => userValue !== null)
|
|
||||||
.reduce((acc, { key, userValue }) => ({ ...acc, [key]: { userValue } }), {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertRequest(req) {
|
|
||||||
if (
|
|
||||||
!req ||
|
|
||||||
typeof req !== 'object' ||
|
|
||||||
typeof req.path !== 'string' ||
|
|
||||||
!req.headers ||
|
|
||||||
typeof req.headers !== 'object'
|
|
||||||
) {
|
|
||||||
throw new TypeError('all uiSettings methods must be passed a hapi.Request object');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UiSettings {
|
|
||||||
constructor(server, status) {
|
|
||||||
this._server = server;
|
|
||||||
this._status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaults() {
|
|
||||||
return getDefaultSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a Promise for the value of the requested setting
|
|
||||||
async get(req, key) {
|
|
||||||
assertRequest(req);
|
|
||||||
return this.getAll(req)
|
|
||||||
.then(all => all[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll(req) {
|
|
||||||
assertRequest(req);
|
|
||||||
return this.getRaw(req)
|
|
||||||
.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;
|
|
||||||
}, {})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getRaw(req) {
|
|
||||||
assertRequest(req);
|
|
||||||
return this.getUserProvided(req)
|
|
||||||
.then(user => defaultsDeep(user, this.getDefaults()));
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUserProvided(req, { ignore401Errors = false } = {}) {
|
|
||||||
assertRequest(req);
|
|
||||||
const { callWithRequest, errors } = this._server.plugins.elasticsearch.getCluster('admin');
|
|
||||||
|
|
||||||
// If the ui settings status isn't green, we shouldn't be attempting to get
|
|
||||||
// user settings, since we can't be sure that all the necessary conditions
|
|
||||||
// (e.g. elasticsearch being available) are met.
|
|
||||||
if (this._status.state !== 'green') {
|
|
||||||
return hydrateUserSettings({});
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = this._getClientSettings();
|
|
||||||
const allowedErrors = [errors[404], errors[403], errors.NoConnections];
|
|
||||||
if (ignore401Errors) allowedErrors.push(errors[401]);
|
|
||||||
|
|
||||||
return Bluebird
|
|
||||||
.resolve(callWithRequest(req, 'get', params, { wrap401Errors: !ignore401Errors }))
|
|
||||||
.catch(...allowedErrors, () => ({}))
|
|
||||||
.then(resp => resp._source || {})
|
|
||||||
.then(source => hydrateUserSettings(source));
|
|
||||||
}
|
|
||||||
|
|
||||||
async setMany(req, changes) {
|
|
||||||
assertRequest(req);
|
|
||||||
const { callWithRequest } = this._server.plugins.elasticsearch.getCluster('admin');
|
|
||||||
const clientParams = {
|
|
||||||
...this._getClientSettings(),
|
|
||||||
body: { doc: changes }
|
|
||||||
};
|
|
||||||
return callWithRequest(req, 'update', clientParams)
|
|
||||||
.then(() => ({}));
|
|
||||||
}
|
|
||||||
|
|
||||||
async set(req, key, value) {
|
|
||||||
assertRequest(req);
|
|
||||||
return this.setMany(req, { [key]: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(req, key) {
|
|
||||||
assertRequest(req);
|
|
||||||
return this.set(req, key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeMany(req, keys) {
|
|
||||||
assertRequest(req);
|
|
||||||
const changes = {};
|
|
||||||
keys.forEach(key => {
|
|
||||||
changes[key] = null;
|
|
||||||
});
|
|
||||||
return this.setMany(req, changes);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getClientSettings() {
|
|
||||||
const config = this._server.config();
|
|
||||||
const index = config.get('kibana.index');
|
|
||||||
const id = config.get('pkg.version');
|
|
||||||
const type = 'config';
|
|
||||||
return { index, type, id };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { UiSettings } from './ui_settings';
|
import { uiSettingsServiceFactory } from './ui_settings_service_factory';
|
||||||
|
import { getUiSettingsServiceForRequest } from './ui_settings_service_for_request';
|
||||||
import { mirrorStatus } from './mirror_status';
|
import { mirrorStatus } from './mirror_status';
|
||||||
|
|
||||||
export function uiSettingsMixin(kbnServer, server, config) {
|
export function uiSettingsMixin(kbnServer, server, config) {
|
||||||
|
@ -9,9 +10,35 @@ export function uiSettingsMixin(kbnServer, server, config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uiSettings = new UiSettings(server, status);
|
// Passed to the UiSettingsService.
|
||||||
server.decorate('server', 'uiSettings', () => uiSettings);
|
// UiSettingsService calls the function before trying to read data from
|
||||||
|
// elasticsearch, giving us a chance to prevent it from happening.
|
||||||
|
//
|
||||||
|
// If the ui settings status isn't green we shouldn't be attempting to get
|
||||||
|
// user settings, since we can't be sure that all the necessary conditions
|
||||||
|
// (e.g. elasticsearch being available) are met.
|
||||||
|
const readUiSettingsInterceptor = () => {
|
||||||
|
if (status.state !== 'green') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// don't return, just let it happen when the plugins are ready
|
||||||
kbnServer.ready().then(() => {
|
kbnServer.ready().then(() => {
|
||||||
mirrorStatus(status, kbnServer.status.getForPluginId('elasticsearch'));
|
mirrorStatus(status, kbnServer.status.getForPluginId('elasticsearch'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.decorate('server', 'uiSettingsServiceFactory', function (options) {
|
||||||
|
return uiSettingsServiceFactory(server, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.decorate('request', 'getUiSettingsService', function () {
|
||||||
|
return getUiSettingsServiceForRequest(server, this, readUiSettingsInterceptor);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.decorate('server', 'uiSettings', () => {
|
||||||
|
throw new Error(`
|
||||||
|
server.uiSettings has been removed, see https://github.com/elastic/kibana/pull/12243.
|
||||||
|
`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
144
src/ui/ui_settings/ui_settings_service.js
Normal file
144
src/ui/ui_settings/ui_settings_service.js
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import { defaultsDeep, noop } from 'lodash';
|
||||||
|
import { errors as esErrors } from 'elasticsearch';
|
||||||
|
|
||||||
|
import { getDefaultSettings } from './defaults';
|
||||||
|
|
||||||
|
function hydrateUserSettings(userSettings) {
|
||||||
|
return Object.keys(userSettings)
|
||||||
|
.map(key => ({ key, userValue: userSettings[key] }))
|
||||||
|
.filter(({ userValue }) => userValue !== null)
|
||||||
|
.reduce((acc, { key, userValue }) => ({ ...acc, [key]: { userValue } }), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides access to the UiSettings stored in elasticsearch.
|
||||||
|
*
|
||||||
|
* @class UiSettingsService
|
||||||
|
* @param {Object} options
|
||||||
|
* @property {string} options.index Elasticsearch index name where settings are stored
|
||||||
|
* @property {string} options.type type of ui settings Elasticsearch doc
|
||||||
|
* @property {string} options.id id of ui settings Elasticsearch doc
|
||||||
|
* @property {AsyncFunction} options.callCluster function that accepts a method name and
|
||||||
|
* param object which causes a request via some elasticsearch client
|
||||||
|
* @property {AsyncFunction} [options.readInterceptor] async function that is called when the
|
||||||
|
* UiSettingsService does a read() an has an oportunity to intercept the
|
||||||
|
* request and return an alternate `_source` value to use.
|
||||||
|
*/
|
||||||
|
export class UiSettingsService {
|
||||||
|
constructor(options) {
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
callCluster,
|
||||||
|
readInterceptor = noop,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
this._callCluster = callCluster;
|
||||||
|
this._readInterceptor = readInterceptor;
|
||||||
|
this._index = index;
|
||||||
|
this._type = type;
|
||||||
|
this._id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaults() {
|
||||||
|
return getDefaultSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a Promise for the value of the requested setting
|
||||||
|
async get(key) {
|
||||||
|
const all = await this.getAll();
|
||||||
|
return all[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
const raw = await this.getRaw();
|
||||||
|
|
||||||
|
return Object.keys(raw)
|
||||||
|
.reduce((all, key) => {
|
||||||
|
const item = raw[key];
|
||||||
|
const hasUserValue = 'userValue' in item;
|
||||||
|
all[key] = hasUserValue ? item.userValue : item.value;
|
||||||
|
return all;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRaw() {
|
||||||
|
const userProvided = await this.getUserProvided();
|
||||||
|
return defaultsDeep(userProvided, this.getDefaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserProvided(options) {
|
||||||
|
return hydrateUserSettings(await this._read(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMany(changes) {
|
||||||
|
await this._write(changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key, value) {
|
||||||
|
await this.setMany({ [key]: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(key) {
|
||||||
|
await this.set(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeMany(keys) {
|
||||||
|
const changes = {};
|
||||||
|
keys.forEach(key => {
|
||||||
|
changes[key] = null;
|
||||||
|
});
|
||||||
|
await this.setMany(changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _write(changes) {
|
||||||
|
await this._callCluster('update', {
|
||||||
|
index: this._index,
|
||||||
|
type: this._type,
|
||||||
|
id: this._id,
|
||||||
|
body: {
|
||||||
|
doc: changes
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _read(options = {}) {
|
||||||
|
const interceptValue = await this._readInterceptor(options);
|
||||||
|
if (interceptValue != null) {
|
||||||
|
return interceptValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
ignore401Errors = false
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const isIgnorableError = error => (
|
||||||
|
error instanceof esErrors[404] ||
|
||||||
|
error instanceof esErrors[403] ||
|
||||||
|
error instanceof esErrors.NoConnections ||
|
||||||
|
(ignore401Errors && error instanceof esErrors[401])
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientParams = {
|
||||||
|
index: this._index,
|
||||||
|
type: this._type,
|
||||||
|
id: this._id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const callOptions = {
|
||||||
|
wrap401Errors: !ignore401Errors
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = await this._callCluster('get', clientParams, callOptions);
|
||||||
|
return resp._source;
|
||||||
|
} catch (error) {
|
||||||
|
if (isIgnorableError(error)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/ui/ui_settings/ui_settings_service_factory.js
Normal file
31
src/ui/ui_settings/ui_settings_service_factory.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { UiSettingsService } from './ui_settings_service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of UiSettingsService that will use the
|
||||||
|
* passed `callCluster` function to communicate with elasticsearch
|
||||||
|
*
|
||||||
|
* @param {Hapi.Server} server
|
||||||
|
* @param {Object} options
|
||||||
|
* @property {AsyncFunction} options.callCluster function that accepts a method name and
|
||||||
|
* param object which causes a request via some elasticsearch client
|
||||||
|
* @property {AsyncFunction} [options.readInterceptor] async function that is called when the
|
||||||
|
* UiSettingsService does a read() an has an oportunity to intercept the
|
||||||
|
* request and return an alternate `_source` value to use.
|
||||||
|
* @return {UiSettingsService}
|
||||||
|
*/
|
||||||
|
export function uiSettingsServiceFactory(server, options) {
|
||||||
|
const config = server.config();
|
||||||
|
|
||||||
|
const {
|
||||||
|
callCluster,
|
||||||
|
readInterceptor
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
return new UiSettingsService({
|
||||||
|
index: config.get('kibana.index'),
|
||||||
|
type: 'config',
|
||||||
|
id: config.get('pkg.version'),
|
||||||
|
callCluster,
|
||||||
|
readInterceptor,
|
||||||
|
});
|
||||||
|
}
|
20
src/ui/ui_settings/ui_settings_service_for_request.js
Normal file
20
src/ui/ui_settings/ui_settings_service_for_request.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { uiSettingsServiceFactory } from './ui_settings_service_factory';
|
||||||
|
|
||||||
|
const BY_REQUEST_CACHE = new WeakMap();
|
||||||
|
|
||||||
|
export function getUiSettingsServiceForRequest(server, request, readInterceptor) {
|
||||||
|
if (BY_REQUEST_CACHE.has(request)) {
|
||||||
|
return BY_REQUEST_CACHE.get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminCluster = server.plugins.elasticsearch.getCluster('admin');
|
||||||
|
const uiSettingsService = uiSettingsServiceFactory(server, {
|
||||||
|
readInterceptor,
|
||||||
|
callCluster(...args) {
|
||||||
|
return adminCluster.callWithRequest(request, ...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BY_REQUEST_CACHE.set(request, uiSettingsService);
|
||||||
|
return uiSettingsService;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue