mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Use single ES document type (#12794)
Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co>
This commit is contained in:
parent
52e5d45f69
commit
3c0c0ff441
99 changed files with 3393 additions and 3115 deletions
|
@ -159,8 +159,7 @@ export default function (kibana) {
|
|||
}
|
||||
);
|
||||
// Set up the health check service and start it.
|
||||
const mappings = kibana.uiExports.mappings.getCombined();
|
||||
const { start, waitUntilReady } = healthCheck(this, server, { mappings });
|
||||
const { start, waitUntilReady } = healthCheck(this, server);
|
||||
server.expose('waitUntilReady', waitUntilReady);
|
||||
start();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ describe('plugins/elasticsearch', function () {
|
|||
config = function () { return { get: get }; };
|
||||
|
||||
_.set(server, 'config', config);
|
||||
_.set(server, 'getKibanaIndexMappingsDsl', sinon.stub().returns(mappings));
|
||||
|
||||
callWithInternalUser = sinon.stub();
|
||||
cluster = { callWithInternalUser: callWithInternalUser };
|
||||
|
@ -37,14 +38,14 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
|
||||
it('should check cluster.health upon successful index creation', function () {
|
||||
const fn = createKibanaIndex(server, mappings);
|
||||
const fn = createKibanaIndex(server);
|
||||
return fn.then(function () {
|
||||
sinon.assert.calledOnce(callWithInternalUser.withArgs('cluster.health', sinon.match.any));
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created with mappings for config.buildNum', function () {
|
||||
const fn = createKibanaIndex(server, mappings);
|
||||
const fn = createKibanaIndex(server);
|
||||
return fn.then(function () {
|
||||
const params = callWithInternalUser.args[0][1];
|
||||
expect(params)
|
||||
|
@ -63,7 +64,7 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
|
||||
it('should be created with 1 shard and default replica', function () {
|
||||
const fn = createKibanaIndex(server, mappings);
|
||||
const fn = createKibanaIndex(server);
|
||||
return fn.then(function () {
|
||||
const params = callWithInternalUser.args[0][1];
|
||||
expect(params)
|
||||
|
@ -78,7 +79,7 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
|
||||
it('should be created with index name set in the config', function () {
|
||||
const fn = createKibanaIndex(server, mappings);
|
||||
const fn = createKibanaIndex(server);
|
||||
return fn.then(function () {
|
||||
const params = callWithInternalUser.args[0][1];
|
||||
expect(params)
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import Chance from 'chance';
|
||||
|
||||
import { ensureTypesExist } from '../ensure_types_exist';
|
||||
|
||||
const chance = new Chance();
|
||||
|
||||
function createRandomTypes(n = chance.integer({ min: 10, max: 20 })) {
|
||||
return chance.n(
|
||||
() => ({
|
||||
name: chance.word(),
|
||||
mapping: {
|
||||
type: chance.pickone(['keyword', 'text', 'integer', 'boolean'])
|
||||
}
|
||||
}),
|
||||
n
|
||||
);
|
||||
}
|
||||
|
||||
function typesToMapping(types) {
|
||||
return types.reduce((acc, type) => ({
|
||||
...acc,
|
||||
[type.name]: type.mapping
|
||||
}), {});
|
||||
}
|
||||
|
||||
function createV5Index(name, types) {
|
||||
return {
|
||||
[name]: {
|
||||
mappings: typesToMapping(types)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createV6Index(name, types) {
|
||||
return {
|
||||
[name]: {
|
||||
mappings: {
|
||||
doc: {
|
||||
properties: typesToMapping(types)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createCallCluster(index) {
|
||||
return sinon.spy(async (method, params) => {
|
||||
switch (method) {
|
||||
case 'indices.get':
|
||||
expect(params).to.have.property('index', Object.keys(index)[0]);
|
||||
return cloneDeep(index);
|
||||
case 'indices.putMapping':
|
||||
return { ok: true };
|
||||
default:
|
||||
throw new Error(`stub not expecting callCluster('${method}')`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('es/healthCheck/ensureTypesExist()', () => {
|
||||
describe('general', () => {
|
||||
it('reads the _mappings feature of the indexName', async () => {
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV5Index(indexName, []));
|
||||
await ensureTypesExist({
|
||||
callCluster,
|
||||
indexName,
|
||||
types: [],
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({
|
||||
feature: '_mappings'
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('v5 index', () => {
|
||||
it('does nothing if mappings match elasticsearch', async () => {
|
||||
const types = createRandomTypes();
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV5Index(indexName, types));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
});
|
||||
|
||||
it('adds types that are not in index', async () => {
|
||||
const indexTypes = createRandomTypes();
|
||||
const missingTypes = indexTypes.splice(-5);
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV5Index(indexName, indexTypes));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types: [
|
||||
...indexTypes,
|
||||
...missingTypes,
|
||||
],
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.callCount(callCluster, 1 + missingTypes.length);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
missingTypes.forEach(type => {
|
||||
sinon.assert.calledWith(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: type.name,
|
||||
body: type.mapping
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores extra types in index', async () => {
|
||||
const indexTypes = createRandomTypes();
|
||||
const missingTypes = indexTypes.splice(-5);
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV5Index(indexName, indexTypes));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types: missingTypes,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.callCount(callCluster, 1 + missingTypes.length);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
missingTypes.forEach(type => {
|
||||
sinon.assert.calledWith(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: type.name,
|
||||
body: type.mapping
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('v6 index', () => {
|
||||
it('does nothing if mappings match elasticsearch', async () => {
|
||||
const types = createRandomTypes();
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV6Index(indexName, types));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
});
|
||||
|
||||
it('adds types that are not in index', async () => {
|
||||
const indexTypes = createRandomTypes();
|
||||
const missingTypes = indexTypes.splice(-5);
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV6Index(indexName, indexTypes));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types: [
|
||||
...indexTypes,
|
||||
...missingTypes,
|
||||
],
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.callCount(callCluster, 1 + missingTypes.length);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
missingTypes.forEach(type => {
|
||||
sinon.assert.calledWith(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: 'doc',
|
||||
body: {
|
||||
properties: {
|
||||
[type.name]: type.mapping,
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores extra types in index', async () => {
|
||||
const indexTypes = createRandomTypes();
|
||||
const missingTypes = indexTypes.splice(-5);
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV6Index(indexName, indexTypes));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types: missingTypes,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.callCount(callCluster, 1 + missingTypes.length);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
missingTypes.forEach(type => {
|
||||
sinon.assert.calledWith(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: 'doc',
|
||||
body: {
|
||||
properties: {
|
||||
[type.name]: type.mapping,
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('does not define the _default_ type', async () => {
|
||||
const indexTypes = [];
|
||||
const missingTypes = [
|
||||
{
|
||||
name: '_default_',
|
||||
mapping: {}
|
||||
}
|
||||
];
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createV6Index(indexName, indexTypes));
|
||||
await ensureTypesExist({
|
||||
indexName,
|
||||
callCluster,
|
||||
types: missingTypes,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWith(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,8 @@ import mappings from './fixtures/mappings';
|
|||
import healthCheck from '../health_check';
|
||||
import kibanaVersion from '../kibana_version';
|
||||
import { esTestServerUrlParts } from '../../../../../test/es_test_server_url_parts';
|
||||
import * as ensureTypesExistNS from '../ensure_types_exist';
|
||||
import * as patchKibanaIndexNS from '../patch_kibana_index';
|
||||
import * as migrateConfigNS from '../migrate_config';
|
||||
|
||||
const esPort = esTestServerUrlParts.port;
|
||||
const esUrl = url.format(esTestServerUrlParts);
|
||||
|
@ -21,13 +22,15 @@ describe('plugins/elasticsearch', () => {
|
|||
let health;
|
||||
let plugin;
|
||||
let cluster;
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
beforeEach(() => {
|
||||
const COMPATIBLE_VERSION_NUMBER = '5.0.0';
|
||||
|
||||
// Stub the Kibana version instead of drawing from package.json.
|
||||
sinon.stub(kibanaVersion, 'get').returns(COMPATIBLE_VERSION_NUMBER);
|
||||
sinon.stub(ensureTypesExistNS, 'ensureTypesExist');
|
||||
sandbox.stub(kibanaVersion, 'get').returns(COMPATIBLE_VERSION_NUMBER);
|
||||
sandbox.stub(patchKibanaIndexNS, 'patchKibanaIndex');
|
||||
sandbox.stub(migrateConfigNS, 'migrateConfig');
|
||||
|
||||
// setup the plugin stub
|
||||
plugin = {
|
||||
|
@ -73,19 +76,15 @@ describe('plugins/elasticsearch', () => {
|
|||
getCluster: sinon.stub().returns(cluster)
|
||||
}
|
||||
},
|
||||
savedObjectsClientFactory: () => ({
|
||||
find: sinon.stub().returns(Promise.resolve({ saved_objects: [] })),
|
||||
create: sinon.stub().returns(Promise.resolve({ id: 'foo' })),
|
||||
})
|
||||
getKibanaIndexMappingsDsl() {
|
||||
return mappings;
|
||||
}
|
||||
};
|
||||
|
||||
health = healthCheck(plugin, server, { mappings });
|
||||
health = healthCheck(plugin, server);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
kibanaVersion.get.restore();
|
||||
ensureTypesExistNS.ensureTypesExist.restore();
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
it('should set the cluster green if everything is ready', function () {
|
||||
cluster.callWithInternalUser.withArgs('ping').returns(Promise.resolve());
|
||||
|
@ -101,6 +100,7 @@ describe('plugins/elasticsearch', () => {
|
|||
sinon.assert.calledOnce(cluster.callWithInternalUser.withArgs('ping'));
|
||||
sinon.assert.calledTwice(cluster.callWithInternalUser.withArgs('nodes.info', sinon.match.any));
|
||||
sinon.assert.calledOnce(cluster.callWithInternalUser.withArgs('cluster.health', sinon.match.any));
|
||||
sinon.assert.notCalled(plugin.status.red);
|
||||
sinon.assert.calledOnce(plugin.status.green);
|
||||
|
||||
expect(plugin.status.green.args[0][0]).to.be('Kibana index ready');
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import { times, cloneDeep, pick, partition } from 'lodash';
|
||||
import Chance from 'chance';
|
||||
|
||||
import { patchKibanaIndex } from '../patch_kibana_index';
|
||||
import { getRootProperties, getRootType } from '../../../../server/mappings';
|
||||
|
||||
const chance = new Chance();
|
||||
|
||||
function createRandomMappings(n = chance.integer({ min: 10, max: 20 })) {
|
||||
return {
|
||||
[chance.word()]: {
|
||||
properties: times(n, () => chance.word())
|
||||
.reduce((acc, prop) => ({
|
||||
...acc,
|
||||
[prop]: {
|
||||
type: chance.pickone(['keyword', 'text', 'integer', 'boolean'])
|
||||
}
|
||||
}), {})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function splitMappings(mappings) {
|
||||
const type = getRootType(mappings);
|
||||
const allProps = getRootProperties(mappings);
|
||||
const keyGroups = partition(Object.keys(allProps), (p, i) => i % 2);
|
||||
return keyGroups.map(keys => ({
|
||||
[type]: {
|
||||
...mappings[type],
|
||||
properties: pick(allProps, keys)
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function createIndex(name, mappings = {}) {
|
||||
return {
|
||||
[name]: {
|
||||
mappings
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createCallCluster(index) {
|
||||
return sinon.spy(async (method, params) => {
|
||||
switch (method) {
|
||||
case 'indices.get':
|
||||
expect(params).to.have.property('index', Object.keys(index)[0]);
|
||||
return cloneDeep(index);
|
||||
case 'indices.putMapping':
|
||||
return { ok: true };
|
||||
default:
|
||||
throw new Error(`stub not expecting callCluster('${method}')`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('es/healthCheck/patchKibanaIndex()', () => {
|
||||
describe('general', () => {
|
||||
it('reads the _mappings feature of the indexName', async () => {
|
||||
const indexName = chance.word();
|
||||
const mappings = createRandomMappings();
|
||||
const callCluster = createCallCluster(createIndex(indexName, mappings));
|
||||
await patchKibanaIndex({
|
||||
callCluster,
|
||||
indexName,
|
||||
kibanaIndexMappingsDsl: mappings,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.get', sinon.match({
|
||||
feature: '_mappings'
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('multi-type index', () => {
|
||||
it('rejects', async () => {
|
||||
try {
|
||||
const mappings = createRandomMappings();
|
||||
const indexName = chance.word();
|
||||
const index = createIndex(indexName, {
|
||||
...mappings,
|
||||
...createRandomMappings(),
|
||||
...createRandomMappings(),
|
||||
...createRandomMappings(),
|
||||
});
|
||||
const callCluster = createCallCluster(index);
|
||||
|
||||
await patchKibanaIndex({
|
||||
indexName,
|
||||
callCluster,
|
||||
kibanaIndexMappingsDsl: mappings,
|
||||
log: sinon.stub()
|
||||
});
|
||||
throw new Error('expected patchKibanaIndex() to throw an error');
|
||||
} catch (error) {
|
||||
expect(error)
|
||||
.to.have.property('message')
|
||||
.contain('Your Kibana index is out of date');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('v6 index', () => {
|
||||
it('does nothing if mappings match elasticsearch', async () => {
|
||||
const mappings = createRandomMappings();
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createIndex(indexName, mappings));
|
||||
await patchKibanaIndex({
|
||||
indexName,
|
||||
callCluster,
|
||||
kibanaIndexMappingsDsl: mappings,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(callCluster);
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
});
|
||||
|
||||
it('adds properties that are not in index', async () => {
|
||||
const [indexMappings, missingMappings] = splitMappings(createRandomMappings());
|
||||
const mappings = {
|
||||
...indexMappings,
|
||||
...missingMappings,
|
||||
};
|
||||
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createIndex(indexName, indexMappings));
|
||||
await patchKibanaIndex({
|
||||
indexName,
|
||||
callCluster,
|
||||
kibanaIndexMappingsDsl: mappings,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledTwice(callCluster);
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.get', sinon.match({ index: indexName }));
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: getRootType(mappings),
|
||||
body: {
|
||||
properties: getRootProperties(mappings)
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
it('ignores extra properties in index', async () => {
|
||||
const [indexMappings, mappings] = splitMappings(createRandomMappings());
|
||||
const indexName = chance.word();
|
||||
const callCluster = createCallCluster(createIndex(indexName, indexMappings));
|
||||
await patchKibanaIndex({
|
||||
indexName,
|
||||
callCluster,
|
||||
kibanaIndexMappingsDsl: mappings,
|
||||
log: sinon.stub()
|
||||
});
|
||||
|
||||
sinon.assert.calledTwice(callCluster);
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.get', sinon.match({
|
||||
index: indexName
|
||||
}));
|
||||
sinon.assert.calledWithExactly(callCluster, 'indices.putMapping', sinon.match({
|
||||
index: indexName,
|
||||
type: getRootType(mappings),
|
||||
body: {
|
||||
properties: getRootProperties(mappings)
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,15 +1,13 @@
|
|||
export default function (server, mappings) {
|
||||
export default function (server) {
|
||||
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
|
||||
const index = server.config().get('kibana.index');
|
||||
return callWithInternalUser('indices.create', {
|
||||
index: index,
|
||||
body: {
|
||||
settings: {
|
||||
number_of_shards: 1,
|
||||
'index.mapper.dynamic': false,
|
||||
'index.mapping.single_type': false
|
||||
number_of_shards: 1
|
||||
},
|
||||
mappings
|
||||
mappings: server.getKibanaIndexMappingsDsl()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* Checks that a kibana index has all of the types specified. Any type
|
||||
* that is not defined in the existing index will be added via the
|
||||
* `indicies.putMapping` API.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @property {Function} options.log a method for writing log messages
|
||||
* @property {string} options.indexName name of the index in elasticsearch
|
||||
* @property {Function} options.callCluster a function for executing client requests
|
||||
* @property {Array<Object>} options.types an array of objects with `name` and `mapping` properties
|
||||
* describing the types that should be in the index
|
||||
* @return {Promise<undefined>}
|
||||
*/
|
||||
export async function ensureTypesExist({ log, indexName, callCluster, types }) {
|
||||
const index = await callCluster('indices.get', {
|
||||
index: indexName,
|
||||
feature: '_mappings'
|
||||
});
|
||||
|
||||
// could be different if aliases were resolved by `indices.get`
|
||||
const resolvedName = Object.keys(index)[0];
|
||||
const mappings = index[resolvedName].mappings;
|
||||
const literalTypes = Object.keys(mappings);
|
||||
const v6Index = literalTypes.length === 1 && literalTypes[0] === 'doc';
|
||||
|
||||
// our types aren't really es types, at least not in v6
|
||||
const typesDefined = Object.keys(
|
||||
v6Index
|
||||
? mappings.doc.properties
|
||||
: mappings
|
||||
);
|
||||
|
||||
for (const type of types) {
|
||||
if (v6Index && type.name === '_default_') {
|
||||
// v6 indices don't get _default_ types
|
||||
continue;
|
||||
}
|
||||
|
||||
const defined = typesDefined.includes(type.name);
|
||||
if (defined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log(['info', 'elasticsearch'], {
|
||||
tmpl: `Adding mappings to kibana index for SavedObject type "<%= typeName %>"`,
|
||||
typeName: type.name,
|
||||
typeMapping: type.mapping
|
||||
});
|
||||
|
||||
if (v6Index) {
|
||||
await callCluster('indices.putMapping', {
|
||||
index: indexName,
|
||||
type: 'doc',
|
||||
body: {
|
||||
properties: {
|
||||
[type.name]: type.mapping
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await callCluster('indices.putMapping', {
|
||||
index: indexName,
|
||||
type: type.name,
|
||||
body: type.mapping
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import _ from 'lodash';
|
||||
import Promise from 'bluebird';
|
||||
import elasticsearch from 'elasticsearch';
|
||||
import migrateConfig from './migrate_config';
|
||||
import { migrateConfig } from './migrate_config';
|
||||
import createKibanaIndex from './create_kibana_index';
|
||||
import kibanaVersion from './kibana_version';
|
||||
import { ensureEsVersion } from './ensure_es_version';
|
||||
import { ensureNotTribe } from './ensure_not_tribe';
|
||||
import { ensureAllowExplicitIndex } from './ensure_allow_explicit_index';
|
||||
import { ensureTypesExist } from './ensure_types_exist';
|
||||
import { patchKibanaIndex } from './patch_kibana_index';
|
||||
|
||||
const NoConnections = elasticsearch.errors.NoConnections;
|
||||
import util from 'util';
|
||||
|
@ -17,7 +17,7 @@ const NO_INDEX = 'no_index';
|
|||
const INITIALIZING = 'initializing';
|
||||
const READY = 'ready';
|
||||
|
||||
export default function (plugin, server, { mappings }) {
|
||||
export default function (plugin, server) {
|
||||
const config = server.config();
|
||||
const callAdminAsKibanaUser = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser;
|
||||
const callDataAsKibanaUser = server.plugins.elasticsearch.getCluster('data').callWithInternalUser;
|
||||
|
@ -71,7 +71,7 @@ export default function (plugin, server, { mappings }) {
|
|||
.then(function (health) {
|
||||
if (health === NO_INDEX) {
|
||||
plugin.status.yellow('No existing Kibana index found');
|
||||
return createKibanaIndex(server, mappings);
|
||||
return createKibanaIndex(server);
|
||||
}
|
||||
|
||||
if (health === INITIALIZING) {
|
||||
|
@ -99,11 +99,11 @@ export default function (plugin, server, { mappings }) {
|
|||
.then(() => ensureNotTribe(callAdminAsKibanaUser))
|
||||
.then(() => ensureAllowExplicitIndex(callAdminAsKibanaUser, config))
|
||||
.then(waitForShards)
|
||||
.then(() => ensureTypesExist({
|
||||
.then(() => patchKibanaIndex({
|
||||
callCluster: callAdminAsKibanaUser,
|
||||
log: (...args) => server.log(...args),
|
||||
indexName: config.get('kibana.index'),
|
||||
types: Object.keys(mappings).map(name => ({ name, mapping: mappings[name] }))
|
||||
kibanaIndexMappingsDsl: server.getKibanaIndexMappingsDsl()
|
||||
}))
|
||||
.then(_.partial(migrateConfig, server))
|
||||
.then(() => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import upgrade from './upgrade_config';
|
||||
|
||||
export default async function (server) {
|
||||
export async function migrateConfig(server) {
|
||||
const savedObjectsClient = server.savedObjectsClientFactory({
|
||||
callCluster: server.plugins.elasticsearch.getCluster('admin').callWithInternalUser
|
||||
});
|
||||
|
|
101
src/core_plugins/elasticsearch/lib/patch_kibana_index.js
Normal file
101
src/core_plugins/elasticsearch/lib/patch_kibana_index.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
getTypes,
|
||||
getRootType,
|
||||
getRootProperties
|
||||
} from '../../../server/mappings';
|
||||
|
||||
/**
|
||||
* Checks that the root type in the kibana index has all of the
|
||||
* root properties specified by the kibanaIndexMappings.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @property {Function} options.log
|
||||
* @property {string} options.indexName
|
||||
* @property {Function} options.callCluster
|
||||
* @property {EsMappingsDsl} options.kibanaIndexMappingsDsl
|
||||
* @return {Promise<undefined>}
|
||||
*/
|
||||
export async function patchKibanaIndex(options) {
|
||||
const {
|
||||
log,
|
||||
indexName,
|
||||
callCluster,
|
||||
kibanaIndexMappingsDsl
|
||||
} = options;
|
||||
|
||||
const rootEsType = getRootType(kibanaIndexMappingsDsl);
|
||||
const currentMappingsDsl = await getCurrentMappings(callCluster, indexName, rootEsType);
|
||||
const missingProperties = await getMissingRootProperties(currentMappingsDsl, kibanaIndexMappingsDsl);
|
||||
|
||||
const missingPropertyNames = Object.keys(missingProperties);
|
||||
if (!missingPropertyNames.length) {
|
||||
// all expected properties are in current mapping
|
||||
return;
|
||||
}
|
||||
|
||||
// log about new properties
|
||||
log(['info', 'elasticsearch'], {
|
||||
tmpl: `Adding mappings to kibana index for SavedObject types "<%= names.join('", "') %>"`,
|
||||
names: missingPropertyNames
|
||||
});
|
||||
|
||||
// add the new properties to the index mapping
|
||||
await callCluster('indices.putMapping', {
|
||||
index: indexName,
|
||||
type: rootEsType,
|
||||
body: {
|
||||
properties: missingProperties
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mappings dsl for the current Kibana index
|
||||
* @param {Function} callCluster
|
||||
* @param {string} indexName
|
||||
* @param {string} rootEsType
|
||||
* @return {EsMappingsDsl}
|
||||
*/
|
||||
async function getCurrentMappings(callCluster, indexName, rootEsType) {
|
||||
const index = await callCluster('indices.get', {
|
||||
index: indexName,
|
||||
feature: '_mappings'
|
||||
});
|
||||
|
||||
// could be different if aliases were resolved by `indices.get`
|
||||
const resolvedName = Object.keys(index)[0];
|
||||
const currentMappingsDsl = index[resolvedName].mappings;
|
||||
const currentTypes = getTypes(currentMappingsDsl);
|
||||
|
||||
const isV5Index = currentTypes.length > 1 || currentTypes[0] !== rootEsType;
|
||||
if (isV5Index) {
|
||||
throw new Error(
|
||||
'Your Kibana index is out of date, reset it or use the X-Pack upgrade assistant.'
|
||||
);
|
||||
}
|
||||
|
||||
return currentMappingsDsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the properties that are in the expectedMappingsDsl but not the
|
||||
* currentMappingsDsl. Properties will be an object of properties normally
|
||||
* found at `[index]mappings[typeName].properties` is es mapping responses
|
||||
*
|
||||
* @param {EsMappingsDsl} currentMappingsDsl
|
||||
* @param {EsMappingsDsl} expectedMappingsDsl
|
||||
* @return {PropertyMappings}
|
||||
*/
|
||||
async function getMissingRootProperties(currentMappingsDsl, expectedMappingsDsl) {
|
||||
const expectedProps = getRootProperties(expectedMappingsDsl);
|
||||
const existingProps = getRootProperties(currentMappingsDsl);
|
||||
|
||||
return Object.keys(expectedProps)
|
||||
.reduce((acc, prop) => {
|
||||
if (existingProps[prop]) {
|
||||
return acc;
|
||||
} else {
|
||||
return { ...acc, [prop]: expectedProps[prop] };
|
||||
}
|
||||
}, {});
|
||||
}
|
|
@ -46,7 +46,7 @@ uiModules.get('apps/management')
|
|||
|
||||
$scope.kbnUrl = Private(KbnUrlProvider);
|
||||
$scope.indexPattern = $route.current.locals.indexPattern;
|
||||
docTitle.change($scope.indexPattern.id);
|
||||
docTitle.change($scope.indexPattern.title);
|
||||
|
||||
const otherPatterns = _.filter($route.current.locals.indexPatterns, pattern => {
|
||||
return pattern.id !== $scope.indexPattern.id;
|
||||
|
|
|
@ -9,6 +9,19 @@ uiRoutes
|
|||
.when('/management/kibana/indices/:indexPatternId/create-field/', { mode: 'create' })
|
||||
.defaults(/management\/kibana\/indices\/[^\/]+\/(field|create-field)(\/|$)/, {
|
||||
template,
|
||||
mapBreadcrumbs($route, breadcrumbs) {
|
||||
const { indexPattern } = $route.current.locals;
|
||||
return breadcrumbs.map(crumb => {
|
||||
if (crumb.id !== indexPattern.id) {
|
||||
return crumb;
|
||||
}
|
||||
|
||||
return {
|
||||
...crumb,
|
||||
display: indexPattern.title
|
||||
};
|
||||
});
|
||||
},
|
||||
resolve: {
|
||||
indexPattern: function ($route, courier) {
|
||||
return courier.indexPatterns.get($route.current.params.indexPatternId)
|
||||
|
@ -46,7 +59,7 @@ uiRoutes
|
|||
throw new Error('unknown fieldSettings mode ' + this.mode);
|
||||
}
|
||||
|
||||
docTitle.change([this.field.name || 'New Scripted Field', this.indexPattern.id]);
|
||||
docTitle.change([this.field.name || 'New Scripted Field', this.indexPattern.title]);
|
||||
this.goBack = function () {
|
||||
kbnUrl.changeToRoute(this.indexPattern, 'edit');
|
||||
};
|
||||
|
|
|
@ -112,17 +112,21 @@ export function decorateMochaUi(lifecycle, context) {
|
|||
switch (property) {
|
||||
case 'describe':
|
||||
case 'describe.only':
|
||||
case 'describe.skip':
|
||||
case 'xdescribe':
|
||||
case 'context':
|
||||
case 'context.only':
|
||||
case 'context.skip':
|
||||
case 'xcontext':
|
||||
return wrapSuiteFunction(property, value);
|
||||
|
||||
case 'it':
|
||||
case 'it.only':
|
||||
case 'it.skip':
|
||||
case 'xit':
|
||||
case 'specify':
|
||||
case 'specify.only':
|
||||
case 'specify.skip':
|
||||
case 'xspecify':
|
||||
return wrapTestFunction(property, value);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import pluginsInitializeMixin from './plugins/initialize';
|
|||
import { indexPatternsMixin } from './index_patterns';
|
||||
import { savedObjectsMixin } from './saved_objects';
|
||||
import { statsMixin } from './stats';
|
||||
import { kibanaIndexMappingsMixin } from './mappings';
|
||||
import { serverExtensionsMixin } from './server_extensions';
|
||||
|
||||
const rootDir = fromRoot('.');
|
||||
|
@ -62,6 +63,9 @@ export default class KbnServer {
|
|||
// tell the config we are done loading plugins
|
||||
configCompleteMixin,
|
||||
|
||||
// setup kbnServer.mappings and server.getKibanaIndexMappingsDsl()
|
||||
kibanaIndexMappingsMixin,
|
||||
|
||||
// setup this.uiExports and this.bundles
|
||||
uiMixin,
|
||||
indexPatternsMixin,
|
||||
|
|
128
src/server/mappings/__tests__/index_mappings.js
Normal file
128
src/server/mappings/__tests__/index_mappings.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
import expect from 'expect.js';
|
||||
import Chance from 'chance';
|
||||
|
||||
import { IndexMappings } from '../index_mappings';
|
||||
import { getRootType } from '../lib';
|
||||
|
||||
const chance = new Chance();
|
||||
|
||||
describe('server/mapping/index_mapping', function () {
|
||||
describe('constructor', () => {
|
||||
it('initializes with a default mapping when no args', () => {
|
||||
const mapping = new IndexMappings();
|
||||
const dsl = mapping.getDsl();
|
||||
expect(dsl).to.be.an('object');
|
||||
expect(getRootType(dsl)).to.be.a('string');
|
||||
expect(dsl[getRootType(dsl)]).to.be.an('object');
|
||||
});
|
||||
|
||||
it('accepts a default mapping dsl as the only argument', () => {
|
||||
const mapping = new IndexMappings({
|
||||
foobar: {
|
||||
dynamic: false,
|
||||
properties: {}
|
||||
}
|
||||
});
|
||||
|
||||
expect(mapping.getDsl()).to.eql({
|
||||
foobar: {
|
||||
dynamic: false,
|
||||
properties: {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('throws if root type is of type=anything-but-object', () => {
|
||||
expect(() => {
|
||||
new IndexMappings({
|
||||
root: {
|
||||
type: chance.pickone(['string', 'keyword', 'geo_point'])
|
||||
}
|
||||
});
|
||||
}).to.throwException(/non-object/);
|
||||
});
|
||||
|
||||
it('throws if root type has no type and no properties', () => {
|
||||
expect(() => {
|
||||
new IndexMappings({
|
||||
root: {}
|
||||
});
|
||||
}).to.throwException(/non-object/);
|
||||
});
|
||||
|
||||
it('initialized root type with properties object if not set', () => {
|
||||
const mapping = new IndexMappings({
|
||||
root: {
|
||||
type: 'object'
|
||||
}
|
||||
});
|
||||
|
||||
expect(mapping.getDsl()).to.eql({
|
||||
root: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getDsl()', () => {
|
||||
// tests are light because this method is used all over these tests
|
||||
it('returns mapping as es dsl', function () {
|
||||
const mapping = new IndexMappings();
|
||||
expect(mapping.getDsl()).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#addRootProperties()', () => {
|
||||
it('extends the properties of the root type', () => {
|
||||
const mapping = new IndexMappings({
|
||||
x: { properties: {} }
|
||||
});
|
||||
|
||||
mapping.addRootProperties({
|
||||
y: {
|
||||
properties: {
|
||||
z: {
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(mapping.getDsl()).to.eql({
|
||||
x: {
|
||||
properties: {
|
||||
y: {
|
||||
properties: {
|
||||
z: {
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('throws if any property is conflicting', () => {
|
||||
const props = { foo: 'bar' };
|
||||
const mapping = new IndexMappings({
|
||||
root: { properties: props }
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
mapping.addRootProperties(props);
|
||||
}).to.throwException(/foo/);
|
||||
});
|
||||
|
||||
it('includes the plugin option in the error message when specified', () => {
|
||||
const props = { foo: 'bar' };
|
||||
const mapping = new IndexMappings({ root: { properties: props } });
|
||||
|
||||
expect(() => {
|
||||
mapping.addRootProperties(props, { plugin: 'abc123' });
|
||||
}).to.throwException(/plugin abc123/);
|
||||
});
|
||||
});
|
||||
});
|
10
src/server/mappings/index.js
Normal file
10
src/server/mappings/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export {
|
||||
kibanaIndexMappingsMixin
|
||||
} from './kibana_index_mappings_mixin';
|
||||
|
||||
export {
|
||||
getTypes,
|
||||
getRootType,
|
||||
getProperty,
|
||||
getRootProperties,
|
||||
} from './lib';
|
61
src/server/mappings/index_mappings.js
Normal file
61
src/server/mappings/index_mappings.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { cloneDeep, isPlainObject } from 'lodash';
|
||||
|
||||
import { formatListAsProse } from '../../utils';
|
||||
import { getRootProperties, getRootType } from './lib';
|
||||
|
||||
const DEFAULT_INITIAL_DSL = {
|
||||
rootType: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
};
|
||||
|
||||
export class IndexMappings {
|
||||
constructor(initialDsl = DEFAULT_INITIAL_DSL) {
|
||||
this._dsl = cloneDeep(initialDsl);
|
||||
if (!isPlainObject(this._dsl)) {
|
||||
throw new TypeError('initial mapping must be an object');
|
||||
}
|
||||
|
||||
// ensure that we have a properties object in the dsl
|
||||
// and that the dsl can be parsed with getRootProperties() and kin
|
||||
this._setProperties(getRootProperties(this._dsl) || {});
|
||||
}
|
||||
|
||||
getDsl() {
|
||||
return cloneDeep(this._dsl);
|
||||
}
|
||||
|
||||
addRootProperties(newProperties, options = {}) {
|
||||
const { plugin } = options;
|
||||
const rootProperties = getRootProperties(this._dsl);
|
||||
|
||||
|
||||
const conflicts = Object.keys(newProperties)
|
||||
.filter(key => rootProperties.hasOwnProperty(key));
|
||||
|
||||
if (conflicts.length) {
|
||||
const props = formatListAsProse(conflicts);
|
||||
const owner = plugin ? `registered by plugin ${plugin} ` : '';
|
||||
throw new Error(
|
||||
`Mappings for ${props} ${owner}have already been defined`
|
||||
);
|
||||
}
|
||||
|
||||
this._setProperties({
|
||||
...rootProperties,
|
||||
...newProperties
|
||||
});
|
||||
}
|
||||
|
||||
_setProperties(newProperties) {
|
||||
const rootType = getRootType(this._dsl);
|
||||
this._dsl = {
|
||||
...this._dsl,
|
||||
[rootType]: {
|
||||
...this._dsl[rootType],
|
||||
properties: newProperties
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
60
src/server/mappings/kibana_index_mappings_mixin.js
Normal file
60
src/server/mappings/kibana_index_mappings_mixin.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { IndexMappings } from './index_mappings';
|
||||
|
||||
/**
|
||||
* The default mappings used for the kibana index. This is
|
||||
* extended via uiExports type "mappings". See the kibana
|
||||
* and timelion plugins for examples.
|
||||
* @type {EsMappingDsl}
|
||||
*/
|
||||
const BASE_KIBANA_INDEX_MAPPINGS_DSL = {
|
||||
doc: {
|
||||
'dynamic': 'strict',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword'
|
||||
},
|
||||
|
||||
config: {
|
||||
dynamic: true,
|
||||
properties: {
|
||||
buildNum: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function kibanaIndexMappingsMixin(kbnServer, server) {
|
||||
/**
|
||||
* Stores the current mappings that we expect to find in the Kibana
|
||||
* index. Using `kbnServer.mappings.addRootProperties()` the UiExports
|
||||
* class extends these mappings based on `mappings` ui export specs.
|
||||
*
|
||||
* Application code should not access this object, and instead should
|
||||
* use `server.getKibanaIndexMappingsDsl()` from below, mixed with the
|
||||
* helpers exposed by this module, to interact with the mappings via
|
||||
* their DSL.
|
||||
*
|
||||
* @type {IndexMappings}
|
||||
*/
|
||||
kbnServer.mappings = new IndexMappings(BASE_KIBANA_INDEX_MAPPINGS_DSL);
|
||||
|
||||
/**
|
||||
* Get the mappings dsl that we expect to see in the
|
||||
* Kibana index. Used by the elasticsearch plugin to create
|
||||
* and update the kibana index. Also used by the SavedObjectsClient
|
||||
* to determine the properties defined in the mapping as well as
|
||||
* things like the "rootType".
|
||||
*
|
||||
* See `src/server/mappings/lib/index.js` for helpers useful for reading
|
||||
* the EsMappingDsl object.
|
||||
*
|
||||
* @method server.getKibanaIndexMappingsDsl
|
||||
* @returns {EsMappingDsl}
|
||||
*/
|
||||
server.decorate('server', 'getKibanaIndexMappingsDsl', () => {
|
||||
return kbnServer.mappings.getDsl();
|
||||
});
|
||||
}
|
68
src/server/mappings/lib/__tests__/get_property.js
Normal file
68
src/server/mappings/lib/__tests__/get_property.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
import { getProperty } from '../get_property';
|
||||
|
||||
const MAPPINGS = {
|
||||
rootType: {
|
||||
properties: {
|
||||
foo: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'text'
|
||||
},
|
||||
description: {
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
},
|
||||
bar: {
|
||||
properties: {
|
||||
baz: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
box: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function test(key, mapping) {
|
||||
expect(typeof key === 'string' || Array.isArray(key)).to.be.ok();
|
||||
expect(mapping).to.be.an('object');
|
||||
|
||||
expect(getProperty(MAPPINGS, key)).to.be(mapping);
|
||||
}
|
||||
|
||||
describe('getProperty(mappings, path)', () => {
|
||||
describe('string key', () => {
|
||||
it('finds root properties', () => {
|
||||
test('foo', MAPPINGS.rootType.properties.foo);
|
||||
});
|
||||
it('finds nested properties', () => {
|
||||
test('foo.name', MAPPINGS.rootType.properties.foo.properties.name);
|
||||
test('foo.description', MAPPINGS.rootType.properties.foo.properties.description);
|
||||
test('bar.baz', MAPPINGS.rootType.properties.bar.properties.baz);
|
||||
});
|
||||
it('finds nested multi-fields', () => {
|
||||
test('bar.baz.box', MAPPINGS.rootType.properties.bar.properties.baz.fields.box);
|
||||
});
|
||||
});
|
||||
describe('string key', () => {
|
||||
it('finds root properties', () => {
|
||||
test(['foo'], MAPPINGS.rootType.properties.foo);
|
||||
});
|
||||
it('finds nested properties', () => {
|
||||
test(['foo', 'name'], MAPPINGS.rootType.properties.foo.properties.name);
|
||||
test(['foo', 'description'], MAPPINGS.rootType.properties.foo.properties.description);
|
||||
test(['bar', 'baz'], MAPPINGS.rootType.properties.bar.properties.baz);
|
||||
});
|
||||
it('finds nested multi-fields', () => {
|
||||
test(['bar', 'baz', 'box'], MAPPINGS.rootType.properties.bar.properties.baz.fields.box);
|
||||
});
|
||||
});
|
||||
});
|
40
src/server/mappings/lib/get_property.js
Normal file
40
src/server/mappings/lib/get_property.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import toPath from 'lodash/internal/toPath';
|
||||
|
||||
import { getRootType } from './get_root_type';
|
||||
|
||||
/**
|
||||
* Recursively read properties from the mapping object of type "object"
|
||||
* until the `path` is resolved.
|
||||
* @param {EsObjectMapping} mapping
|
||||
* @param {Array<string>} path
|
||||
* @return {Objects|undefined}
|
||||
*/
|
||||
function getPropertyMappingFromObjectMapping(mapping, path) {
|
||||
const props = mapping && (mapping.properties || mapping.fields);
|
||||
|
||||
if (!props) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (path.length > 1) {
|
||||
return getPropertyMappingFromObjectMapping(
|
||||
props[path[0]],
|
||||
path.slice(1)
|
||||
);
|
||||
} else {
|
||||
return props[path[0]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping for a specific property within the root type of the EsMappingsDsl.
|
||||
* @param {EsMappingsDsl} mappings
|
||||
* @param {string|Array<string>} path
|
||||
* @return {Object|undefined}
|
||||
*/
|
||||
export function getProperty(mappings, path) {
|
||||
return getPropertyMappingFromObjectMapping(
|
||||
mappings[getRootType(mappings)],
|
||||
toPath(path)
|
||||
);
|
||||
}
|
29
src/server/mappings/lib/get_root_properties.js
Normal file
29
src/server/mappings/lib/get_root_properties.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { getRootType } from './get_root_type';
|
||||
|
||||
/**
|
||||
* Get the property mappings for the root type in the EsMappingsDsl
|
||||
*
|
||||
* If the mappings don't have a root type, or the root type is not
|
||||
* an object type (it's a keyword or something) this function will
|
||||
* throw an error.
|
||||
*
|
||||
* EsPropertyMappings objects have the root property names as their
|
||||
* first level keys which map to the mappings object for each property.
|
||||
* If the property is of type object it too could have a `properties`
|
||||
* key whose value follows the same format.
|
||||
*
|
||||
* This data can be found at `{indexName}.mappings.{typeName}.properties`
|
||||
* in the es indices.get() response.
|
||||
*
|
||||
* @param {EsMappingsDsl} mappings
|
||||
* @return {EsPropertyMappings}
|
||||
*/
|
||||
export function getRootProperties(mappings) {
|
||||
const mapping = mappings[getRootType(mappings)];
|
||||
|
||||
if (mapping.type !== 'object' && !mapping.properties) {
|
||||
throw new TypeError('Unable to get property names non-object root mapping');
|
||||
}
|
||||
|
||||
return mapping.properties || {};
|
||||
}
|
19
src/server/mappings/lib/get_root_type.js
Normal file
19
src/server/mappings/lib/get_root_type.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { getTypes } from './get_types';
|
||||
|
||||
/**
|
||||
* Get the singular root type in the EsMappingsDsl
|
||||
* object. If there are no types, or there are more
|
||||
* that one type, this function will throw an error.
|
||||
*
|
||||
* @param {EsMappingsDsl} mappings
|
||||
* @return {string}
|
||||
*/
|
||||
export function getRootType(mappings) {
|
||||
const allTypes = getTypes(mappings);
|
||||
|
||||
if (allTypes.length !== 1) {
|
||||
throw new TypeError(`Unable to get root type of mappings object with ${allTypes.length} root types.`);
|
||||
}
|
||||
|
||||
return allTypes[0];
|
||||
}
|
9
src/server/mappings/lib/get_types.js
Normal file
9
src/server/mappings/lib/get_types.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Get the names of the types defined in the EsMappingsDsl
|
||||
*
|
||||
* @param {EsMappingsDsl} mappings
|
||||
* @return {Array<string>}
|
||||
*/
|
||||
export function getTypes(mappings) {
|
||||
return Object.keys(mappings);
|
||||
}
|
4
src/server/mappings/lib/index.js
Normal file
4
src/server/mappings/lib/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
export { getProperty } from './get_property';
|
||||
export { getTypes } from './get_types';
|
||||
export { getRootType } from './get_root_type';
|
||||
export { getRootProperties } from './get_root_properties';
|
|
@ -1,71 +1,94 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import { SavedObjectsClient } from '../saved_objects_client';
|
||||
import { createIdQuery } from '../lib/create_id_query';
|
||||
import * as getSearchDslNS from '../lib/search_dsl/search_dsl';
|
||||
import { getSearchDsl } from '../lib';
|
||||
|
||||
describe('SavedObjectsClient', () => {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
let callAdminCluster;
|
||||
let savedObjectsClient;
|
||||
const docs = {
|
||||
const searchResults = {
|
||||
hits: {
|
||||
total: 3,
|
||||
hits: [{
|
||||
_index: '.kibana',
|
||||
_type: 'index-pattern',
|
||||
_id: 'logstash-*',
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:logstash-*',
|
||||
_score: 1,
|
||||
_source: {
|
||||
title: 'logstash-*',
|
||||
timeFieldName: '@timestamp',
|
||||
notExpandable: true
|
||||
type: 'index-pattern',
|
||||
'index-pattern': {
|
||||
title: 'logstash-*',
|
||||
timeFieldName: '@timestamp',
|
||||
notExpandable: true
|
||||
}
|
||||
}
|
||||
}, {
|
||||
_index: '.kibana',
|
||||
_type: 'config',
|
||||
_id: '6.0.0-alpha1',
|
||||
_type: 'doc',
|
||||
_id: 'config:6.0.0-alpha1',
|
||||
_score: 1,
|
||||
_source: {
|
||||
buildNum: 8467,
|
||||
defaultIndex: 'logstash-*'
|
||||
type: 'config',
|
||||
config: {
|
||||
buildNum: 8467,
|
||||
defaultIndex: 'logstash-*'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
_index: '.kibana',
|
||||
_type: 'index-pattern',
|
||||
_id: 'stocks-*',
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:stocks-*',
|
||||
_score: 1,
|
||||
_source: {
|
||||
title: 'stocks-*',
|
||||
timeFieldName: '@timestamp',
|
||||
notExpandable: true
|
||||
type: 'index-pattern',
|
||||
'index-pattern': {
|
||||
title: 'stocks-*',
|
||||
timeFieldName: '@timestamp',
|
||||
notExpandable: true
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
const mappings = {
|
||||
'index-pattern': {
|
||||
doc: {
|
||||
properties: {
|
||||
someField: {
|
||||
type: 'keyword'
|
||||
'index-pattern': {
|
||||
properties: {
|
||||
someField: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
callAdminCluster = sinon.mock();
|
||||
callAdminCluster = sandbox.stub();
|
||||
savedObjectsClient = new SavedObjectsClient('.kibana-test', mappings, callAdminCluster);
|
||||
sandbox.stub(getSearchDslNS, 'getSearchDsl').returns({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
callAdminCluster.reset();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
||||
describe('#create', () => {
|
||||
it('formats Elasticsearch response', async () => {
|
||||
callAdminCluster.returns({ _type: 'index-pattern', _id: 'logstash-*', _version: 2 });
|
||||
beforeEach(() => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:logstash-*',
|
||||
_version: 2
|
||||
}));
|
||||
});
|
||||
|
||||
it('formats Elasticsearch response', async () => {
|
||||
const response = await savedObjectsClient.create('index-pattern', {
|
||||
title: 'Logstash'
|
||||
});
|
||||
|
@ -81,8 +104,6 @@ describe('SavedObjectsClient', () => {
|
|||
});
|
||||
|
||||
it('should use ES index action', async () => {
|
||||
callAdminCluster.returns({ _type: 'index-pattern', _id: 'logstash-*', _version: 2 });
|
||||
|
||||
await savedObjectsClient.create('index-pattern', {
|
||||
id: 'logstash-*',
|
||||
title: 'Logstash'
|
||||
|
@ -95,8 +116,6 @@ describe('SavedObjectsClient', () => {
|
|||
});
|
||||
|
||||
it('should use create action if ID defined and overwrite=false', async () => {
|
||||
callAdminCluster.returns({ _type: 'index-pattern', _id: 'logstash-*', _version: 2 });
|
||||
|
||||
await savedObjectsClient.create('index-pattern', {
|
||||
title: 'Logstash'
|
||||
}, {
|
||||
|
@ -110,22 +129,32 @@ describe('SavedObjectsClient', () => {
|
|||
});
|
||||
|
||||
it('allows for id to be provided', async () => {
|
||||
callAdminCluster.returns({ _type: 'index-pattern', _id: 'logstash-*', _version: 2 });
|
||||
|
||||
await savedObjectsClient.create('index-pattern', {
|
||||
id: 'logstash-*',
|
||||
title: 'Logstash'
|
||||
}, { id: 'myId' });
|
||||
}, { id: 'logstash-*' });
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const args = callAdminCluster.getCall(0).args;
|
||||
expect(args[1].id).to.be('myId');
|
||||
expect(args[1].id).to.be('index-pattern:logstash-*');
|
||||
});
|
||||
|
||||
it('self-generates an ID', async () => {
|
||||
await savedObjectsClient.create('index-pattern', {
|
||||
title: 'Logstash'
|
||||
});
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const args = callAdminCluster.getCall(0).args;
|
||||
expect(args[1].id).to.match(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#bulkCreate', () => {
|
||||
it('formats Elasticsearch request', async () => {
|
||||
callAdminCluster.returns({ items: [] });
|
||||
|
||||
await savedObjectsClient.bulkCreate([
|
||||
{ type: 'config', id: 'one', attributes: { title: 'Test One' } },
|
||||
{ type: 'index-pattern', id: 'two', attributes: { title: 'Test Two' } }
|
||||
|
@ -137,30 +166,39 @@ describe('SavedObjectsClient', () => {
|
|||
|
||||
expect(args[0]).to.be('bulk');
|
||||
expect(args[1].body).to.eql([
|
||||
{ create: { _type: 'config', _id: 'one' } },
|
||||
{ title: 'Test One' },
|
||||
{ create: { _type: 'index-pattern', _id: 'two' } },
|
||||
{ title: 'Test Two' }
|
||||
{ create: { _type: 'doc', _id: 'config:one' } },
|
||||
{ type: 'config', config: { title: 'Test One' } },
|
||||
{ create: { _type: 'doc', _id: 'index-pattern:two' } },
|
||||
{ type: 'index-pattern', 'index-pattern': { title: 'Test Two' } }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should overwrite objects if overwrite is truthy', async () => {
|
||||
await savedObjectsClient.bulkCreate([
|
||||
{ type: 'config', id: 'one', attributes: { title: 'Test One' } },
|
||||
{ type: 'index-pattern', id: 'two', attributes: { title: 'Test Two' } }
|
||||
], { overwrite: true });
|
||||
callAdminCluster.returns({ items: [] });
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
await savedObjectsClient.bulkCreate([{ type: 'foo', id: 'bar', attributes: {} }], { overwrite: false });
|
||||
sinon.assert.calledOnce(callAdminCluster);
|
||||
sinon.assert.calledWithExactly(callAdminCluster, 'bulk', sinon.match({
|
||||
body: [
|
||||
// uses create because overwriting is not allowed
|
||||
{ create: { _type: 'doc', _id: 'foo:bar' } },
|
||||
{ type: 'foo', 'foo': {} },
|
||||
]
|
||||
}));
|
||||
|
||||
callAdminCluster.reset();
|
||||
|
||||
await savedObjectsClient.bulkCreate([{ type: 'foo', id: 'bar', attributes: {} }], { overwrite: true });
|
||||
sinon.assert.calledOnce(callAdminCluster);
|
||||
sinon.assert.calledWithExactly(callAdminCluster, 'bulk', sinon.match({
|
||||
body: [
|
||||
// uses index because overwriting is allowed
|
||||
{ index: { _type: 'doc', _id: 'foo:bar' } },
|
||||
{ type: 'foo', 'foo': {} },
|
||||
]
|
||||
}));
|
||||
|
||||
const args = callAdminCluster.getCall(0).args;
|
||||
|
||||
expect(args[0]).to.be('bulk');
|
||||
expect(args[1].body).to.eql([
|
||||
{ index: { _type: 'config', _id: 'one' } },
|
||||
{ title: 'Test One' },
|
||||
{ index: { _type: 'index-pattern', _id: 'two' } },
|
||||
{ title: 'Test Two' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns document errors', async () => {
|
||||
|
@ -168,16 +206,16 @@ describe('SavedObjectsClient', () => {
|
|||
errors: false,
|
||||
items: [{
|
||||
create: {
|
||||
_type: 'config',
|
||||
_id: 'one',
|
||||
_type: 'doc',
|
||||
_id: 'config:one',
|
||||
error: {
|
||||
reason: 'type[config] missing'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
create: {
|
||||
_type: 'index-pattern',
|
||||
_id: 'two',
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:two',
|
||||
_version: 2
|
||||
}
|
||||
}]
|
||||
|
@ -192,15 +230,12 @@ describe('SavedObjectsClient', () => {
|
|||
{
|
||||
id: 'one',
|
||||
type: 'config',
|
||||
version: undefined,
|
||||
attributes: { title: 'Test One' },
|
||||
error: { message: 'type[config] missing' }
|
||||
}, {
|
||||
id: 'two',
|
||||
type: 'index-pattern',
|
||||
version: 2,
|
||||
attributes: { title: 'Test Two' },
|
||||
error: undefined
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
@ -210,14 +245,14 @@ describe('SavedObjectsClient', () => {
|
|||
errors: false,
|
||||
items: [{
|
||||
create: {
|
||||
_type: 'config',
|
||||
_id: 'one',
|
||||
_type: 'doc',
|
||||
_id: 'config:one',
|
||||
_version: 2
|
||||
}
|
||||
}, {
|
||||
create: {
|
||||
_type: 'index-pattern',
|
||||
_id: 'two',
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:two',
|
||||
_version: 2
|
||||
}
|
||||
}]
|
||||
|
@ -234,41 +269,40 @@ describe('SavedObjectsClient', () => {
|
|||
type: 'config',
|
||||
version: 2,
|
||||
attributes: { title: 'Test One' },
|
||||
error: undefined
|
||||
}, {
|
||||
id: 'two',
|
||||
type: 'index-pattern',
|
||||
version: 2,
|
||||
attributes: { title: 'Test Two' },
|
||||
error: undefined
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#delete', () => {
|
||||
it('throws notFound when ES is unable to find the document', (done) => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
deleted: 0
|
||||
}));
|
||||
it('throws notFound when ES is unable to find the document', async () => {
|
||||
callAdminCluster.returns(Promise.resolve({ found: false }));
|
||||
|
||||
try {
|
||||
await savedObjectsClient.delete('index-pattern', 'logstash-*');
|
||||
expect().fail('should throw error');
|
||||
} catch(e) {
|
||||
expect(e.output.statusCode).to.eql(404);
|
||||
}
|
||||
|
||||
savedObjectsClient.delete('index-pattern', 'logstash-*').then(() => {
|
||||
done('failed');
|
||||
}).catch(e => {
|
||||
expect(e.output.statusCode).to.be(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('passes the parameters to callAdminCluster', async () => {
|
||||
callAdminCluster.returns({});
|
||||
await savedObjectsClient.delete('index-pattern', 'logstash-*');
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const args = callAdminCluster.getCall(0).args;
|
||||
expect(args[0]).to.be('deleteByQuery');
|
||||
expect(args[1]).to.eql({
|
||||
body: createIdQuery({ type: 'index-pattern', id: 'logstash-*' }),
|
||||
const [method, args] = callAdminCluster.getCall(0).args;
|
||||
expect(method).to.be('delete');
|
||||
expect(args).to.eql({
|
||||
type: 'doc',
|
||||
id: 'index-pattern:logstash-*',
|
||||
refresh: 'wait_for',
|
||||
index: '.kibana-test'
|
||||
});
|
||||
|
@ -276,20 +310,68 @@ describe('SavedObjectsClient', () => {
|
|||
});
|
||||
|
||||
describe('#find', () => {
|
||||
it('formats Elasticsearch response', async () => {
|
||||
const count = docs.hits.hits.length;
|
||||
beforeEach(() => {
|
||||
callAdminCluster.returns(searchResults);
|
||||
});
|
||||
|
||||
it('requires searchFields be an array if defined', async () => {
|
||||
try {
|
||||
await savedObjectsClient.find({ searchFields: 'string' });
|
||||
throw new Error('expected find() to reject');
|
||||
} catch (error) {
|
||||
expect(error).to.have.property('message').contain('must be an array');
|
||||
}
|
||||
});
|
||||
|
||||
it('requires fields be an array if defined', async () => {
|
||||
try {
|
||||
await savedObjectsClient.find({ fields: 'string' });
|
||||
throw new Error('expected find() to reject');
|
||||
} catch (error) {
|
||||
expect(error).to.have.property('message').contain('must be an array');
|
||||
}
|
||||
});
|
||||
|
||||
it('passes mappings, search, searchFields, type, sortField, and sortOrder to getSearchDsl', async () => {
|
||||
const relevantOpts = {
|
||||
search: 'foo*',
|
||||
searchFields: ['foo'],
|
||||
type: 'bar',
|
||||
sortField: 'name',
|
||||
sortOrder: 'desc'
|
||||
};
|
||||
|
||||
await savedObjectsClient.find(relevantOpts);
|
||||
sinon.assert.calledOnce(getSearchDsl);
|
||||
sinon.assert.calledWithExactly(getSearchDsl, mappings, relevantOpts);
|
||||
});
|
||||
|
||||
it('merges output of getSearchDsl into es request body', async () => {
|
||||
getSearchDsl.returns({ query: 1, aggregations: 2 });
|
||||
await savedObjectsClient.find();
|
||||
sinon.assert.calledOnce(callAdminCluster);
|
||||
sinon.assert.calledWithExactly(callAdminCluster, 'search', sinon.match({
|
||||
body: sinon.match({
|
||||
query: 1,
|
||||
aggregations: 2,
|
||||
})
|
||||
}));
|
||||
});
|
||||
|
||||
it('formats Elasticsearch response', async () => {
|
||||
const count = searchResults.hits.hits.length;
|
||||
|
||||
callAdminCluster.returns(Promise.resolve(docs));
|
||||
const response = await savedObjectsClient.find();
|
||||
|
||||
expect(response.total).to.be(count);
|
||||
expect(response.saved_objects).to.have.length(count);
|
||||
docs.hits.hits.forEach((doc, i) => {
|
||||
|
||||
searchResults.hits.hits.forEach((doc, i) => {
|
||||
expect(response.saved_objects[i]).to.eql({
|
||||
id: doc._id,
|
||||
type: doc._type,
|
||||
id: doc._id.replace(/(index-pattern|config)\:/, ''),
|
||||
type: doc._source.type,
|
||||
version: doc._version,
|
||||
attributes: doc._source
|
||||
attributes: doc._source[doc._source.type]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -304,80 +386,8 @@ describe('SavedObjectsClient', () => {
|
|||
expect(options.from).to.be(50);
|
||||
});
|
||||
|
||||
it('accepts type', async () => {
|
||||
await savedObjectsClient.find({ type: 'index-pattern' });
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const options = callAdminCluster.getCall(0).args[1];
|
||||
const expectedQuery = {
|
||||
bool: {
|
||||
must: [{ match_all: {} }],
|
||||
filter: [
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {
|
||||
_type: 'index-pattern'
|
||||
}
|
||||
}, {
|
||||
term: {
|
||||
type: 'index-pattern'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
expect(options.body).to.eql({
|
||||
query: expectedQuery, version: true
|
||||
});
|
||||
});
|
||||
|
||||
it('throws error when providing sortField but no type', (done) => {
|
||||
savedObjectsClient.find({
|
||||
sortField: 'someField'
|
||||
}).then(() => {
|
||||
done('failed');
|
||||
}).catch(e => {
|
||||
expect(e).to.be.an(Error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts sort with type', async () => {
|
||||
await savedObjectsClient.find({
|
||||
type: 'index-pattern',
|
||||
sortField: 'someField',
|
||||
sortOrder: 'desc',
|
||||
});
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const options = callAdminCluster.getCall(0).args[1];
|
||||
const expectedQuerySort = [
|
||||
{
|
||||
someField: {
|
||||
order: 'desc',
|
||||
unmapped_type: 'keyword'
|
||||
},
|
||||
}, {
|
||||
'index-pattern.someField': {
|
||||
order: 'desc',
|
||||
unmapped_type: 'keyword'
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(options.body.sort).to.eql(expectedQuerySort);
|
||||
});
|
||||
|
||||
it('can filter by fields', async () => {
|
||||
await savedObjectsClient.find({ fields: 'title' });
|
||||
await savedObjectsClient.find({ fields: ['title'] });
|
||||
|
||||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
|
@ -389,22 +399,21 @@ describe('SavedObjectsClient', () => {
|
|||
});
|
||||
|
||||
describe('#get', () => {
|
||||
it('formats Elasticsearch response', async () => {
|
||||
beforeEach(() => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'logstash-*',
|
||||
_type: 'index-pattern',
|
||||
_version: 2,
|
||||
_source: {
|
||||
title: 'Testing'
|
||||
}
|
||||
}
|
||||
]
|
||||
_id: 'index-pattern:logstash-*',
|
||||
_type: 'doc',
|
||||
_version: 2,
|
||||
_source: {
|
||||
type: 'index-pattern',
|
||||
'index-pattern': {
|
||||
title: 'Testing'
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
it('formats Elasticsearch response', async () => {
|
||||
const response = await savedObjectsClient.get('index-pattern', 'logstash-*');
|
||||
expect(response).to.eql({
|
||||
id: 'logstash-*',
|
||||
|
@ -415,10 +424,20 @@ describe('SavedObjectsClient', () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('prepends type to the id', async () => {
|
||||
await savedObjectsClient.get('index-pattern', 'logstash-*');
|
||||
|
||||
const [, args] = callAdminCluster.getCall(0).args;
|
||||
expect(args.id).to.eql('index-pattern:logstash-*');
|
||||
expect(args.type).to.eql('doc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#bulkGet', () => {
|
||||
it('accepts an array of mixed type and ids', async () => {
|
||||
it('accepts a array of mixed type and ids', async () => {
|
||||
callAdminCluster.returns({ docs: [] });
|
||||
|
||||
await savedObjectsClient.bulkGet([
|
||||
{ id: 'one', type: 'config' },
|
||||
{ id: 'two', type: 'index-pattern' }
|
||||
|
@ -427,15 +446,15 @@ describe('SavedObjectsClient', () => {
|
|||
expect(callAdminCluster.calledOnce).to.be(true);
|
||||
|
||||
const options = callAdminCluster.getCall(0).args[1];
|
||||
expect(options.body).to.eql([
|
||||
{},
|
||||
createIdQuery({ type: 'config', id: 'one' }),
|
||||
{},
|
||||
createIdQuery({ type: 'index-pattern', id: 'two' })
|
||||
expect(options.body.docs).to.eql([
|
||||
{ _type: 'doc', _id: 'config:one' },
|
||||
{ _type: 'doc', _id: 'index-pattern:two' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns early for empty objects argument', async () => {
|
||||
callAdminCluster.returns({ docs: [] });
|
||||
|
||||
const response = await savedObjectsClient.bulkGet([]);
|
||||
|
||||
expect(response.saved_objects).to.have.length(0);
|
||||
|
@ -444,74 +463,73 @@ describe('SavedObjectsClient', () => {
|
|||
|
||||
it('reports error on missed objects', async () => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
responses: [
|
||||
{
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'good',
|
||||
_type: 'doc',
|
||||
_version: 2,
|
||||
_source: {
|
||||
type: 'config',
|
||||
config: {
|
||||
title: 'Test'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
docs:[{
|
||||
_type: 'doc',
|
||||
_id: 'config:good',
|
||||
found: true,
|
||||
_version: 2,
|
||||
_source: { config: { title: 'Test' } }
|
||||
}, {
|
||||
_type: 'doc',
|
||||
_id: 'config:bad',
|
||||
found: false
|
||||
}]
|
||||
}));
|
||||
|
||||
const { saved_objects: savedObjects } = await savedObjectsClient.bulkGet(
|
||||
[{ id: 'good', type: 'config' }, { id: 'bad', type: 'config' }]
|
||||
);
|
||||
|
||||
expect(savedObjects).to.have.length(1);
|
||||
expect(savedObjects).to.have.length(2);
|
||||
expect(savedObjects[0]).to.eql({
|
||||
id: 'good',
|
||||
type: 'config',
|
||||
version: 2,
|
||||
attributes: { title: 'Test' }
|
||||
});
|
||||
expect(savedObjects[1]).to.eql({
|
||||
id: 'bad',
|
||||
type: 'config',
|
||||
error: { statusCode: 404, message: 'Not found' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#update', () => {
|
||||
it('returns current ES document version', async () => {
|
||||
const id = 'logstash-*';
|
||||
const type = 'index-pattern';
|
||||
const version = 2;
|
||||
const attributes = { title: 'Testing' };
|
||||
const id = 'logstash-*';
|
||||
const type = 'index-pattern';
|
||||
const newVersion = 2;
|
||||
const attributes = { title: 'Testing' };
|
||||
|
||||
beforeEach(() => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
_id: id,
|
||||
_type: type,
|
||||
_version: version,
|
||||
_id: `${type}:${id}`,
|
||||
_type: 'doc',
|
||||
_version: newVersion,
|
||||
result: 'updated'
|
||||
}));
|
||||
});
|
||||
|
||||
it('returns current ES document version', async () => {
|
||||
const response = await savedObjectsClient.update('index-pattern', 'logstash-*', attributes);
|
||||
expect(response).to.eql({
|
||||
id,
|
||||
type,
|
||||
version,
|
||||
version: newVersion,
|
||||
attributes
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts version', async () => {
|
||||
await savedObjectsClient.update(
|
||||
'index-pattern',
|
||||
'logstash-*',
|
||||
type,
|
||||
id,
|
||||
{ title: 'Testing' },
|
||||
{ version: 1 }
|
||||
{ version: newVersion - 1 }
|
||||
);
|
||||
|
||||
const esParams = callAdminCluster.getCall(0).args[1];
|
||||
expect(esParams.version).to.be(1);
|
||||
expect(esParams.version).to.be(newVersion - 1);
|
||||
});
|
||||
|
||||
it('passes the parameters to callAdminCluster', async () => {
|
||||
|
@ -523,10 +541,10 @@ describe('SavedObjectsClient', () => {
|
|||
|
||||
expect(args[0]).to.be('update');
|
||||
expect(args[1]).to.eql({
|
||||
type: 'index-pattern',
|
||||
id: 'logstash-*',
|
||||
type: 'doc',
|
||||
id: 'index-pattern:logstash-*',
|
||||
version: undefined,
|
||||
body: { doc: { title: 'Testing' } },
|
||||
body: { doc: { 'index-pattern': { title: 'Testing' } } },
|
||||
refresh: 'wait_for',
|
||||
index: '.kibana-test'
|
||||
});
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
import elasticsearch from 'elasticsearch';
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { SavedObjectsClient } from '../saved_objects_client';
|
||||
const { BadRequest } = elasticsearch.errors;
|
||||
|
||||
describe('SavedObjectsClient', () => {
|
||||
let callAdminCluster;
|
||||
let savedObjectsClient;
|
||||
const illegalArgumentException = { type: 'type_missing_exception' };
|
||||
|
||||
describe('mapping', () => {
|
||||
beforeEach(() => {
|
||||
callAdminCluster = sinon.stub();
|
||||
savedObjectsClient = new SavedObjectsClient('.kibana-test', {}, callAdminCluster);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
callAdminCluster.reset();
|
||||
});
|
||||
|
||||
|
||||
describe('#create', () => {
|
||||
it('falls back to single-type mapping', async () => {
|
||||
const error = new BadRequest('[illegal_argument_exception] Rejecting mapping update to [.kibana-v6]', {
|
||||
body: {
|
||||
error: illegalArgumentException
|
||||
}
|
||||
});
|
||||
|
||||
callAdminCluster
|
||||
.onFirstCall().throws(error)
|
||||
.onSecondCall().returns(Promise.resolve({ _type: 'index-pattern', _id: 'logstash-*', _version: 2 }));
|
||||
|
||||
const response = await savedObjectsClient.create('index-pattern', {
|
||||
title: 'Logstash'
|
||||
});
|
||||
|
||||
expect(response).to.eql({
|
||||
type: 'index-pattern',
|
||||
id: 'logstash-*',
|
||||
version: 2,
|
||||
attributes: {
|
||||
title: 'Logstash',
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('prepends id for single-type', async () => {
|
||||
const id = 'foo';
|
||||
const error = new BadRequest('[illegal_argument_exception] Rejecting mapping update to [.kibana-v6]', {
|
||||
body: {
|
||||
error: illegalArgumentException
|
||||
}
|
||||
});
|
||||
|
||||
callAdminCluster
|
||||
.onFirstCall().throws(error)
|
||||
.onSecondCall().returns(Promise.resolve());
|
||||
|
||||
await savedObjectsClient.create('index-pattern', {}, { id });
|
||||
|
||||
const [, args] = callAdminCluster.getCall(1).args;
|
||||
expect(args.id).to.eql('index-pattern:foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#bulkCreate', () => {
|
||||
const firstResponse = {
|
||||
errors: true,
|
||||
items: [{
|
||||
create: {
|
||||
_type: 'config',
|
||||
_id: 'one',
|
||||
_version: 2,
|
||||
status: 400,
|
||||
error: illegalArgumentException
|
||||
}
|
||||
}, {
|
||||
create: {
|
||||
_type: 'index-pattern',
|
||||
_id: 'two',
|
||||
_version: 2,
|
||||
status: 400,
|
||||
error: illegalArgumentException
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
const secondResponse = {
|
||||
errors: false,
|
||||
items: [{
|
||||
create: {
|
||||
_type: 'config',
|
||||
_id: 'one',
|
||||
_version: 2
|
||||
}
|
||||
}, {
|
||||
create: {
|
||||
_type: 'index-pattern',
|
||||
_id: 'two',
|
||||
_version: 2
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
it('falls back to single-type mappings', async () => {
|
||||
callAdminCluster
|
||||
.onFirstCall().returns(Promise.resolve(firstResponse))
|
||||
.onSecondCall().returns(Promise.resolve(secondResponse));
|
||||
|
||||
const response = await savedObjectsClient.bulkCreate([
|
||||
{ type: 'config', id: 'one', attributes: { title: 'Test One' } },
|
||||
{ type: 'index-pattern', id: 'two', attributes: { title: 'Test Two' } }
|
||||
]);
|
||||
|
||||
expect(response).to.eql([
|
||||
{
|
||||
id: 'one',
|
||||
type: 'config',
|
||||
version: 2,
|
||||
attributes: { title: 'Test One' },
|
||||
error: undefined
|
||||
}, {
|
||||
id: 'two',
|
||||
type: 'index-pattern',
|
||||
version: 2,
|
||||
attributes: { title: 'Test Two' },
|
||||
error: undefined
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('prepends id for single-type', async () => {
|
||||
callAdminCluster
|
||||
.onFirstCall().returns(Promise.resolve(firstResponse))
|
||||
.onSecondCall().returns(Promise.resolve(secondResponse));
|
||||
|
||||
await savedObjectsClient.bulkCreate([
|
||||
{ type: 'config', id: 'one', attributes: { title: 'Test One' } },
|
||||
{ type: 'index-pattern', id: 'two', attributes: { title: 'Test Two' } }
|
||||
]);
|
||||
|
||||
const [, { body }] = callAdminCluster.getCall(1).args;
|
||||
expect(body[0].create._id).to.eql('config:one');
|
||||
expect(body[2].create._id).to.eql('index-pattern:two');
|
||||
// expect(args.id).to.eql('index-pattern:foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
const id = 'logstash-*';
|
||||
const type = 'index-pattern';
|
||||
const version = 2;
|
||||
const attributes = { title: 'Testing' };
|
||||
const error = new BadRequest('[document_missing_exception] [config][logstash-*]: document missing', {
|
||||
body: {
|
||||
error: {
|
||||
type: 'document_missing_exception'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
callAdminCluster
|
||||
.onFirstCall().throws(error)
|
||||
.onSecondCall().returns(Promise.resolve({
|
||||
_id: id,
|
||||
_type: type,
|
||||
_version: version,
|
||||
result: 'updated'
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('falls back to single-type mappings', async () => {
|
||||
const response = await savedObjectsClient.update('index-pattern', 'logstash-*', attributes);
|
||||
expect(response).to.eql({
|
||||
id,
|
||||
type,
|
||||
version,
|
||||
attributes
|
||||
});
|
||||
});
|
||||
|
||||
it('prepends id for single-type', async () => {
|
||||
await savedObjectsClient.update('index-pattern', 'logstash-*', attributes);
|
||||
|
||||
const [, args] = callAdminCluster.getCall(1).args;
|
||||
expect(args.id).to.eql('index-pattern:logstash-*');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,53 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import { v5BulkCreate, v6BulkCreate } from '../compatibility';
|
||||
|
||||
describe('compatibility', () => {
|
||||
const testObjects = [
|
||||
{ type: 'index-pattern', id: 'one', attributes: { title: 'Test Index Pattern' } },
|
||||
{ type: 'config', id: 'two', attributes: { title: 'Test Config Value' } }
|
||||
];
|
||||
|
||||
describe('v5BulkCreate', () => {
|
||||
it('handles default options', () => {
|
||||
const objects = v5BulkCreate(testObjects);
|
||||
expect(objects).to.eql([
|
||||
{ create: { _type: 'index-pattern', _id: 'one' } },
|
||||
{ title: 'Test Index Pattern' },
|
||||
{ create: { _type: 'config', _id: 'two' } },
|
||||
{ title: 'Test Config Value' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses index action for options.overwrite=true', () => {
|
||||
const objects = v5BulkCreate(testObjects, { overwrite: true });
|
||||
expect(objects).to.eql([
|
||||
{ index: { _type: 'index-pattern', _id: 'one' } },
|
||||
{ title: 'Test Index Pattern' },
|
||||
{ index: { _type: 'config', _id: 'two' } },
|
||||
{ title: 'Test Config Value' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('v6BulkCreate', () => {
|
||||
it('handles default options', () => {
|
||||
const objects = v6BulkCreate(testObjects);
|
||||
expect(objects).to.eql([
|
||||
{ create: { _type: 'doc', _id: 'index-pattern:one' } },
|
||||
{ type: 'index-pattern', 'index-pattern': { title: 'Test Index Pattern' } },
|
||||
{ create: { _type: 'doc', _id: 'config:two' } },
|
||||
{ type: 'config', config: { title: 'Test Config Value' } }
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses index action for options.overwrite=true', () => {
|
||||
const objects = v6BulkCreate(testObjects, { overwrite: true });
|
||||
expect(objects).to.eql([
|
||||
{ index: { _type: 'doc', _id: 'index-pattern:one' } },
|
||||
{ type: 'index-pattern', 'index-pattern': { title: 'Test Index Pattern' } },
|
||||
{ index: { _type: 'doc', _id: 'config:two' } },
|
||||
{ type: 'config', config: { title: 'Test Config Value' } }
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,96 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import { createFindQuery } from '../create_find_query';
|
||||
|
||||
const mappings = {};
|
||||
|
||||
describe('createFindQuery', () => {
|
||||
it('matches all when there is no type or filter', () => {
|
||||
const query = createFindQuery(mappings);
|
||||
expect(query).to.eql({ query: { match_all: {} }, version: true });
|
||||
});
|
||||
|
||||
it('adds bool filter for type', () => {
|
||||
const query = createFindQuery(mappings, { type: 'index-pattern' });
|
||||
expect(query).to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {
|
||||
_type: 'index-pattern'
|
||||
}
|
||||
},
|
||||
{
|
||||
term: {
|
||||
type: 'index-pattern'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
}],
|
||||
must: [{
|
||||
match_all: {}
|
||||
}]
|
||||
}
|
||||
},
|
||||
version: true
|
||||
});
|
||||
});
|
||||
|
||||
it('can search across all fields', () => {
|
||||
const query = createFindQuery(mappings, { search: 'foo' });
|
||||
expect(query).to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [],
|
||||
must: [{
|
||||
simple_query_string: {
|
||||
query: 'foo',
|
||||
all_fields: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
version: true
|
||||
});
|
||||
});
|
||||
|
||||
it('can search a single field', () => {
|
||||
const query = createFindQuery(mappings, { search: 'foo', searchFields: 'title' });
|
||||
expect(query).to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [],
|
||||
must: [{
|
||||
simple_query_string: {
|
||||
query: 'foo',
|
||||
fields: ['title']
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
version: true
|
||||
});
|
||||
});
|
||||
|
||||
it('can search across multiple fields', () => {
|
||||
const query = createFindQuery(mappings, { search: 'foo', searchFields: ['title', 'description'] });
|
||||
expect(query).to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [],
|
||||
must: [{
|
||||
simple_query_string: {
|
||||
query: 'foo',
|
||||
fields: ['title', 'description']
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
version: true
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import { createIdQuery } from '../create_id_query';
|
||||
|
||||
describe('createIdQuery', () => {
|
||||
it('takes an id and type', () => {
|
||||
const query = createIdQuery({ id: 'foo', type: 'bar' });
|
||||
|
||||
const expectedQuery = {
|
||||
version: true,
|
||||
size: 1,
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
// v5 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: 'foo' } },
|
||||
{ term: { _type: 'bar' } }
|
||||
]
|
||||
}
|
||||
},
|
||||
// migrated v5 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: 'bar:foo' } },
|
||||
{ term: { type: 'bar' } }
|
||||
]
|
||||
}
|
||||
},
|
||||
// v6 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: 'foo' } },
|
||||
{ term: { type: 'bar' } }
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(query).to.eql(expectedQuery);
|
||||
});
|
||||
});
|
|
@ -1,105 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import { normalizeEsDoc } from '../normalize_es_doc';
|
||||
|
||||
describe('normalizeEsDoc', () => {
|
||||
it('handle legacy doc types', () => {
|
||||
const doc = {
|
||||
_id: 'foo',
|
||||
_type: 'test',
|
||||
_version: 2,
|
||||
_source: { title: 'test' }
|
||||
};
|
||||
|
||||
expect(normalizeEsDoc(doc)).to.eql({
|
||||
id: 'foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { title: 'test' }
|
||||
});
|
||||
});
|
||||
|
||||
it('handle migrated single doc type', () => {
|
||||
const doc = {
|
||||
_id: 'test:foo',
|
||||
_type: 'doc',
|
||||
_version: 2,
|
||||
_source: { type: 'test', test: { title: 'test' } }
|
||||
};
|
||||
|
||||
expect(normalizeEsDoc(doc)).to.eql({
|
||||
id: 'foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { title: 'test' }
|
||||
});
|
||||
});
|
||||
|
||||
it('handles an overwritten type', () => {
|
||||
const doc = {
|
||||
_type: 'doc',
|
||||
_id: 'test:foo',
|
||||
_version: 2,
|
||||
_source: { type: 'test', test: { title: 'test' } }
|
||||
};
|
||||
const overrides = { type: 'test' };
|
||||
|
||||
expect(normalizeEsDoc(doc, overrides)).to.eql({
|
||||
id: 'foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { title: 'test' }
|
||||
});
|
||||
});
|
||||
|
||||
it('can add additional keys', () => {
|
||||
const doc = {
|
||||
_type: 'doc',
|
||||
_id: 'test:foo',
|
||||
_version: 2,
|
||||
_source: { type: 'test', test: { title: 'test' } }
|
||||
};
|
||||
const overrides = { error: 'An error!' };
|
||||
|
||||
expect(normalizeEsDoc(doc, overrides)).to.eql({
|
||||
id: 'foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { title: 'test' },
|
||||
error: 'An error!'
|
||||
});
|
||||
});
|
||||
|
||||
it('handles already prefixed ids with the type', () => {
|
||||
const doc = {
|
||||
_type: 'doc',
|
||||
_id: 'test:test:foo',
|
||||
_version: 2,
|
||||
_source: { type: 'test', test: { title: 'test' } }
|
||||
};
|
||||
const overrides = { error: 'An error!' };
|
||||
|
||||
expect(normalizeEsDoc(doc, overrides)).to.eql({
|
||||
id: 'test:foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { title: 'test' },
|
||||
error: 'An error!'
|
||||
});
|
||||
});
|
||||
|
||||
it('handles legacy doc having an attribute the same as type', () => {
|
||||
const doc = {
|
||||
_id: 'foo',
|
||||
_type: 'test',
|
||||
_version: 2,
|
||||
_source: { test: 'test' }
|
||||
};
|
||||
|
||||
expect(normalizeEsDoc(doc)).to.eql({
|
||||
id: 'foo',
|
||||
type: 'test',
|
||||
version: 2,
|
||||
attributes: { test: 'test' }
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import uuid from 'uuid';
|
||||
import { V6_TYPE } from '../saved_objects_client';
|
||||
|
||||
/**
|
||||
* @param {array} objects - [{ type, id, attributes }]
|
||||
* @param {object} [options={}]
|
||||
* @property {boolean} [options.overwrite=false] - overrides existing documents
|
||||
* @returns {array}
|
||||
*/
|
||||
export function v5BulkCreate(objects, options = {}) {
|
||||
return objects.reduce((acc, object) => {
|
||||
const method = object.id && !options.overwrite ? 'create' : 'index';
|
||||
|
||||
acc.push({ [method]: { _type: object.type, _id: object.id } });
|
||||
acc.push(object.attributes);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {array} objects - [{ type, id, attributes }]
|
||||
* @param {object} [options={}]
|
||||
* @property {boolean} [options.overwrite=false] - overrides existing documents
|
||||
* @returns {array}
|
||||
*/
|
||||
export function v6BulkCreate(objects, options = {}) {
|
||||
return objects.reduce((acc, object) => {
|
||||
const method = object.id && !options.overwrite ? 'create' : 'index';
|
||||
|
||||
acc.push({ [method]: {
|
||||
_type: V6_TYPE,
|
||||
_id: `${object.type}:${object.id || uuid.v1()}`,
|
||||
} });
|
||||
|
||||
acc.push(Object.assign({},
|
||||
{ type: object.type },
|
||||
{ [object.type]: object.attributes }
|
||||
));
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
import { get } from 'lodash';
|
||||
export function createFindQuery(mappings, options = {}) {
|
||||
const { type, search, searchFields, sortField, sortOrder } = options;
|
||||
|
||||
if (!type && sortField) {
|
||||
throw new Error('Cannot sort without knowing the type');
|
||||
}
|
||||
|
||||
if (!type && !search) {
|
||||
return { version: true, query: { match_all: {} } };
|
||||
}
|
||||
|
||||
const bool = { must: [], filter: [] };
|
||||
|
||||
if (type) {
|
||||
bool.filter.push({
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {
|
||||
_type: type
|
||||
}
|
||||
},
|
||||
{
|
||||
term: {
|
||||
type
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (search) {
|
||||
const simpleQueryString = {
|
||||
query: search
|
||||
};
|
||||
|
||||
if (!searchFields) {
|
||||
simpleQueryString.all_fields = true;
|
||||
} else if (Array.isArray(searchFields)) {
|
||||
simpleQueryString.fields = searchFields;
|
||||
} else {
|
||||
simpleQueryString.fields = [searchFields];
|
||||
}
|
||||
|
||||
bool.must.push({ simple_query_string: simpleQueryString });
|
||||
} else {
|
||||
bool.must.push({
|
||||
match_all: {}
|
||||
});
|
||||
}
|
||||
|
||||
const query = { version: true, query: { bool } };
|
||||
|
||||
if (sortField) {
|
||||
const value = {
|
||||
order: sortOrder,
|
||||
unmapped_type: get(mappings, [type, 'properties', sortField, 'type'])
|
||||
};
|
||||
|
||||
query.sort = [{
|
||||
[sortField]: value
|
||||
}, {
|
||||
[`${type}.${sortField}`]: value
|
||||
}];
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* Finds a document by either its v5 or v6 format
|
||||
*
|
||||
* @param type The documents type
|
||||
* @param id The documents id or legacy id
|
||||
**/
|
||||
export function createIdQuery({ type, id }) {
|
||||
return {
|
||||
version: true,
|
||||
size: 1,
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
// v5 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: id } },
|
||||
{ term: { _type: type } }
|
||||
]
|
||||
}
|
||||
},
|
||||
// migrated v5 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: `${type}:${id}` } },
|
||||
{ term: { type: type } }
|
||||
]
|
||||
}
|
||||
},
|
||||
// v6 document
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { _id: id } },
|
||||
{ term: { type: type } }
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
export { createFindQuery } from './create_find_query';
|
||||
export { createIdQuery } from './create_id_query';
|
||||
export { getSearchDsl } from './search_dsl';
|
||||
export { handleEsError } from './handle_es_error';
|
||||
export { v5BulkCreate, v6BulkCreate } from './compatibility';
|
||||
export { normalizeEsDoc } from './normalize_es_doc';
|
||||
export { trimIdPrefix } from './trim_id_prefix';
|
||||
export { includedFields } from './included_fields';
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import { get } from 'lodash';
|
||||
import { V6_TYPE } from '../saved_objects_client';
|
||||
|
||||
export function normalizeEsDoc(doc, overrides = {}) {
|
||||
if (!doc) return {};
|
||||
|
||||
let type;
|
||||
let id = doc._id;
|
||||
let attributes;
|
||||
|
||||
if (doc._type === V6_TYPE) {
|
||||
type = overrides.type || get(doc, '_source.type');
|
||||
attributes = get(doc, `_source.${type}`);
|
||||
|
||||
// migrated v5 indices and objects created with a specified ID
|
||||
// have the type prefixed to the id.
|
||||
id = doc._id.replace(`${type}:`, '');
|
||||
} else {
|
||||
type = overrides.type || doc._type;
|
||||
attributes = doc._source;
|
||||
}
|
||||
|
||||
return Object.assign({}, {
|
||||
id,
|
||||
type,
|
||||
version: doc._version,
|
||||
attributes
|
||||
}, overrides);
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
import { getQueryParams } from '../query_params';
|
||||
|
||||
const MAPPINGS = {
|
||||
rootType: {
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword'
|
||||
},
|
||||
pending: {
|
||||
properties: {
|
||||
title: {
|
||||
type: 'text',
|
||||
}
|
||||
}
|
||||
},
|
||||
saved: {
|
||||
properties: {
|
||||
title: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
raw: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
},
|
||||
obj: {
|
||||
properties: {
|
||||
key1: {
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('searchDsl/queryParams', () => {
|
||||
describe('{}', () => {
|
||||
it('searches for everything', () => {
|
||||
expect(getQueryParams(MAPPINGS))
|
||||
.to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{type}', () => {
|
||||
it('includes just a terms filter', () => {
|
||||
expect(getQueryParams(MAPPINGS, 'saved'))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
term: { type: 'saved' }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{search}', () => {
|
||||
it('includes just a sqs query', () => {
|
||||
expect(getQueryParams(MAPPINGS, null, 'us*'))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'us*',
|
||||
all_fields: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{type,search}', () => {
|
||||
it('includes bool with sqs query and term filter for type', () => {
|
||||
expect(getQueryParams(MAPPINGS, 'saved', 'y*'))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { type: 'saved' } }
|
||||
],
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
all_fields: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{search,searchFields}', () => {
|
||||
it('includes all types for field', () => {
|
||||
expect(getQueryParams(MAPPINGS, null, 'y*', ['title']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'type.title',
|
||||
'pending.title',
|
||||
'saved.title'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
it('supports field boosting', () => {
|
||||
expect(getQueryParams(MAPPINGS, null, 'y*', ['title^3']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'type.title^3',
|
||||
'pending.title^3',
|
||||
'saved.title^3'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
it('supports field and multi-field', () => {
|
||||
expect(getQueryParams(MAPPINGS, null, 'y*', ['title', 'title.raw']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'type.title',
|
||||
'pending.title',
|
||||
'saved.title',
|
||||
'type.title.raw',
|
||||
'pending.title.raw',
|
||||
'saved.title.raw',
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{type,search,searchFields}', () => {
|
||||
it('includes bool, and sqs with field list', () => {
|
||||
expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { type: 'saved' } }
|
||||
],
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'saved.title'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
it('supports fields pointing to multi-fields', () => {
|
||||
expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title.raw']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { type: 'saved' } }
|
||||
],
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'saved.title.raw'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
it('supports multiple search fields', () => {
|
||||
expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title', 'title.raw']))
|
||||
.to.eql({
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { type: 'saved' } }
|
||||
],
|
||||
must: [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: 'y*',
|
||||
fields: [
|
||||
'saved.title',
|
||||
'saved.title.raw'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import { getSearchDsl } from '../search_dsl';
|
||||
import * as queryParamsNS from '../query_params';
|
||||
import * as sortParamsNS from '../sorting_params';
|
||||
|
||||
describe('getSearchDsl', () => {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
describe('validation', () => {
|
||||
it('throws when sortField is passed without type', () => {
|
||||
expect(() => {
|
||||
getSearchDsl({}, {
|
||||
type: undefined,
|
||||
sortField: 'title'
|
||||
});
|
||||
}).to.throwException(/sort without .+ type/);
|
||||
});
|
||||
it('throws when sortOrder without sortField', () => {
|
||||
expect(() => {
|
||||
getSearchDsl({}, {
|
||||
type: 'foo',
|
||||
sortOrder: 'desc'
|
||||
});
|
||||
}).to.throwException(/sortOrder requires a sortField/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('passes control', () => {
|
||||
it('passes (mappings, type, search, searchFields) to getQueryParams', () => {
|
||||
const spy = sandbox.spy(queryParamsNS, 'getQueryParams');
|
||||
const mappings = { type: { properties: {} } };
|
||||
const opts = {
|
||||
type: 'foo',
|
||||
search: 'bar',
|
||||
searchFields: ['baz']
|
||||
};
|
||||
|
||||
getSearchDsl(mappings, opts);
|
||||
sinon.assert.calledOnce(spy);
|
||||
sinon.assert.calledWithExactly(
|
||||
spy,
|
||||
mappings,
|
||||
opts.type,
|
||||
opts.search,
|
||||
opts.searchFields,
|
||||
);
|
||||
});
|
||||
|
||||
it('passes (mappings, type, sortField, sortOrder) to getSortingParams', () => {
|
||||
const spy = sandbox.stub(sortParamsNS, 'getSortingParams').returns({});
|
||||
const mappings = { type: { properties: {} } };
|
||||
const opts = {
|
||||
type: 'foo',
|
||||
sortField: 'bar',
|
||||
sortOrder: 'baz'
|
||||
};
|
||||
|
||||
getSearchDsl(mappings, opts);
|
||||
sinon.assert.calledOnce(spy);
|
||||
sinon.assert.calledWithExactly(
|
||||
spy,
|
||||
mappings,
|
||||
opts.type,
|
||||
opts.sortField,
|
||||
opts.sortOrder,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns combination of getQueryParams and getSortingParams', () => {
|
||||
sandbox.stub(queryParamsNS, 'getQueryParams').returns({ a: 'a' });
|
||||
sandbox.stub(sortParamsNS, 'getSortingParams').returns({ b: 'b' });
|
||||
expect(getSearchDsl({})).to.eql({ a: 'a', b: 'b' });
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,125 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
import { getSortingParams } from '../sorting_params';
|
||||
|
||||
const MAPPINGS = {
|
||||
rootType: {
|
||||
properties: {
|
||||
pending: {
|
||||
properties: {
|
||||
title: {
|
||||
type: 'text',
|
||||
}
|
||||
}
|
||||
},
|
||||
saved: {
|
||||
properties: {
|
||||
title: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
raw: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
},
|
||||
obj: {
|
||||
properties: {
|
||||
key1: {
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('searchDsl/getSortParams', () => {
|
||||
describe('no sortField, type, or order', () => {
|
||||
it('returns no params', () => {
|
||||
expect(getSortingParams(MAPPINGS))
|
||||
.to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type, no sortField', () => {
|
||||
it('returns no params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'pending'))
|
||||
.to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type, order, no sortField', () => {
|
||||
it('returns no params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'saved', null, 'desc'))
|
||||
.to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('search field no direction', () => {
|
||||
describe('search field is simple property', () => {
|
||||
it('returns correct params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'saved', 'title'))
|
||||
.to.eql({
|
||||
sort: [
|
||||
{
|
||||
'saved.title': {
|
||||
order: undefined,
|
||||
unmapped_type: 'text'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('search field is multi-field', () => {
|
||||
it('returns correct params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'saved', 'title.raw'))
|
||||
.to.eql({
|
||||
sort: [
|
||||
{
|
||||
'saved.title.raw': {
|
||||
order: undefined,
|
||||
unmapped_type: 'keyword'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('search with direction', () => {
|
||||
describe('search field is simple property', () => {
|
||||
it('returns correct params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'saved', 'title', 'desc'))
|
||||
.to.eql({
|
||||
sort: [
|
||||
{
|
||||
'saved.title': {
|
||||
order: 'desc',
|
||||
unmapped_type: 'text'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('search field is multi-field', () => {
|
||||
it('returns correct params', () => {
|
||||
expect(getSortingParams(MAPPINGS, 'saved', 'title.raw', 'asc'))
|
||||
.to.eql({
|
||||
sort: [
|
||||
{
|
||||
'saved.title.raw': {
|
||||
order: 'asc',
|
||||
unmapped_type: 'keyword'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
1
src/server/saved_objects/client/lib/search_dsl/index.js
Normal file
1
src/server/saved_objects/client/lib/search_dsl/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export { getSearchDsl } from './search_dsl';
|
|
@ -0,0 +1,60 @@
|
|||
import { getRootProperties } from '../../../../mappings';
|
||||
|
||||
/**
|
||||
* Get the field params based on the types and searchFields
|
||||
* @param {Array<string>} searchFields
|
||||
* @param {Array<string>} types
|
||||
* @return {Object}
|
||||
*/
|
||||
function getFieldsForTypes(searchFields, types) {
|
||||
if (!searchFields || !searchFields.length) {
|
||||
return {
|
||||
all_fields: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
fields: searchFields.reduce((acc, field) => [
|
||||
...acc,
|
||||
...types.map(prefix => `${prefix}.${field}`)
|
||||
], []),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "query" related keys for the search body
|
||||
* @param {EsMapping} mapping mappings from Ui
|
||||
* @param {Object} type
|
||||
* @param {String} search
|
||||
* @param {Array<string>} searchFields
|
||||
* @return {Object}
|
||||
*/
|
||||
export function getQueryParams(mappings, type, search, searchFields) {
|
||||
if (!type && !search) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const bool = {};
|
||||
|
||||
if (type) {
|
||||
bool.filter = [
|
||||
{ term: { type } }
|
||||
];
|
||||
}
|
||||
|
||||
if (search) {
|
||||
bool.must = [
|
||||
{
|
||||
simple_query_string: {
|
||||
query: search,
|
||||
...getFieldsForTypes(
|
||||
searchFields,
|
||||
type ? [type] : Object.keys(getRootProperties(mappings))
|
||||
)
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return { query: { bool } };
|
||||
}
|
27
src/server/saved_objects/client/lib/search_dsl/search_dsl.js
Normal file
27
src/server/saved_objects/client/lib/search_dsl/search_dsl.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
import { getQueryParams } from './query_params';
|
||||
import { getSortingParams } from './sorting_params';
|
||||
|
||||
export function getSearchDsl(mappings, options = {}) {
|
||||
const {
|
||||
type,
|
||||
search,
|
||||
searchFields,
|
||||
sortField,
|
||||
sortOrder
|
||||
} = options;
|
||||
|
||||
if (!type && sortField) {
|
||||
throw Boom.notAcceptable('Cannot sort without filtering by type');
|
||||
}
|
||||
|
||||
if (sortOrder && !sortField) {
|
||||
throw Boom.notAcceptable('sortOrder requires a sortField');
|
||||
}
|
||||
|
||||
return {
|
||||
...getQueryParams(mappings, type, search, searchFields),
|
||||
...getSortingParams(mappings, type, sortField, sortOrder),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
import { getProperty } from '../../../../mappings';
|
||||
|
||||
export function getSortingParams(mappings, type, sortField, sortOrder) {
|
||||
if (!sortField) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const field = getProperty(mappings, `${type}.${sortField}`);
|
||||
if (!field) {
|
||||
throw Boom.badRequest(`Unknown sort field ${sortField}`);
|
||||
}
|
||||
|
||||
return {
|
||||
sort: [
|
||||
{
|
||||
[`${type}.${sortField}`]: {
|
||||
order: sortOrder,
|
||||
unmapped_type: field.type
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
25
src/server/saved_objects/client/lib/trim_id_prefix.js
Normal file
25
src/server/saved_objects/client/lib/trim_id_prefix.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
function assertNonEmptyString(value, name) {
|
||||
if (!value || typeof value !== 'string') {
|
||||
throw new TypeError(`Expected "${value}" to be a ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the prefix from the id of a saved object doc
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {string} type
|
||||
* @return {string}
|
||||
*/
|
||||
export function trimIdPrefix(id, type) {
|
||||
assertNonEmptyString(id, 'document id');
|
||||
assertNonEmptyString(type, 'saved object type');
|
||||
|
||||
const prefix = `${type}:`;
|
||||
|
||||
if (!id.startsWith(prefix)) {
|
||||
return id;
|
||||
}
|
||||
|
||||
return id.slice(prefix.length);
|
||||
}
|
|
@ -1,23 +1,20 @@
|
|||
import Boom from 'boom';
|
||||
import uuid from 'uuid';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { getRootType } from '../../mappings';
|
||||
|
||||
import {
|
||||
createFindQuery,
|
||||
createIdQuery,
|
||||
getSearchDsl,
|
||||
handleEsError,
|
||||
v5BulkCreate,
|
||||
v6BulkCreate,
|
||||
normalizeEsDoc,
|
||||
trimIdPrefix,
|
||||
includedFields
|
||||
} from './lib';
|
||||
|
||||
export const V6_TYPE = 'doc';
|
||||
|
||||
export class SavedObjectsClient {
|
||||
constructor(kibanaIndex, mappings, callAdminCluster) {
|
||||
this._kibanaIndex = kibanaIndex;
|
||||
this._mappings = mappings;
|
||||
this._type = getRootType(this._mappings);
|
||||
this._callAdminCluster = callAdminCluster;
|
||||
}
|
||||
|
||||
|
@ -32,22 +29,28 @@ export class SavedObjectsClient {
|
|||
* @returns {promise} - { id, type, version, attributes }
|
||||
*/
|
||||
async create(type, attributes = {}, options = {}) {
|
||||
const method = options.id && !options.overwrite ? 'create' : 'index';
|
||||
const response = await this._withKibanaIndexAndMappingFallback(method, {
|
||||
type,
|
||||
id: options.id,
|
||||
body: attributes,
|
||||
refresh: 'wait_for'
|
||||
}, {
|
||||
type: V6_TYPE,
|
||||
id: `${type}:${options.id || uuid.v1()}`,
|
||||
const {
|
||||
id,
|
||||
overwrite = false
|
||||
} = options;
|
||||
|
||||
const method = id && !overwrite ? 'create' : 'index';
|
||||
const response = await this._withKibanaIndex(method, {
|
||||
id: this._generateEsId(type, id),
|
||||
type: this._type,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
type,
|
||||
[type]: attributes
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return normalizeEsDoc(response, { type, attributes });
|
||||
return {
|
||||
id: trimIdPrefix(response._id, type),
|
||||
type,
|
||||
version: response._version,
|
||||
attributes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,41 +58,68 @@ export class SavedObjectsClient {
|
|||
*
|
||||
* @param {array} objects - [{ type, id, attributes }]
|
||||
* @param {object} [options={}]
|
||||
* @property {boolean} [options.force=false] - overrides existing documents
|
||||
* @property {string} [options.format=v5]
|
||||
* @property {boolean} [options.overwrite=false] - overwrites existing documents
|
||||
* @returns {promise} - [{ id, type, version, attributes, error: { message } }]
|
||||
*/
|
||||
async bulkCreate(objects, options = {}) {
|
||||
const { format = 'v5' } = options;
|
||||
const {
|
||||
overwrite = false
|
||||
} = options;
|
||||
|
||||
const bulkCreate = format === 'v5' ? v5BulkCreate : v6BulkCreate;
|
||||
const response = await this._withKibanaIndex('bulk', {
|
||||
body: bulkCreate(objects, options),
|
||||
refresh: 'wait_for'
|
||||
const objectToBulkRequest = (object) => {
|
||||
const method = object.id && !overwrite ? 'create' : 'index';
|
||||
|
||||
return [
|
||||
{
|
||||
[method]: {
|
||||
_id: this._generateEsId(object.type, object.id),
|
||||
_type: this._type,
|
||||
}
|
||||
},
|
||||
{
|
||||
type: object.type,
|
||||
[object.type]: object.attributes
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const { items } = await this._withKibanaIndex('bulk', {
|
||||
refresh: 'wait_for',
|
||||
body: objects.reduce((acc, object) => ([
|
||||
...acc,
|
||||
...objectToBulkRequest(object)
|
||||
]), []),
|
||||
});
|
||||
|
||||
const items = get(response, 'items', []);
|
||||
const missingTypesCount = items.filter(item => {
|
||||
const method = Object.keys(item)[0];
|
||||
return get(item, `${method}.error.type`) === 'type_missing_exception';
|
||||
}).length;
|
||||
return items.map((response, i) => {
|
||||
const {
|
||||
error,
|
||||
_id: responseId,
|
||||
_version: version,
|
||||
} = Object.values(response)[0];
|
||||
|
||||
const formatFallback = format === 'v5' && items.length > 0 && items.length === missingTypesCount;
|
||||
|
||||
if (formatFallback) {
|
||||
return this.bulkCreate(objects, Object.assign({}, options, { format: 'v6' }));
|
||||
}
|
||||
|
||||
return get(response, 'items', []).map((resp, i) => {
|
||||
const method = Object.keys(resp)[0];
|
||||
const { type, attributes } = objects[i];
|
||||
|
||||
return normalizeEsDoc(resp[method], {
|
||||
id: resp[method]._id,
|
||||
const {
|
||||
id = responseId,
|
||||
type,
|
||||
attributes,
|
||||
error: resp[method].error ? { message: get(resp[method], 'error.reason') } : undefined
|
||||
});
|
||||
} = objects[i];
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
error: {
|
||||
message: error.reason || JSON.stringify(error)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
version,
|
||||
attributes
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -101,27 +131,28 @@ export class SavedObjectsClient {
|
|||
* @returns {promise}
|
||||
*/
|
||||
async delete(type, id) {
|
||||
const response = await this._withKibanaIndex('deleteByQuery', {
|
||||
body: createIdQuery({ type, id }),
|
||||
refresh: 'wait_for'
|
||||
const response = await this._withKibanaIndex('delete', {
|
||||
id: this._generateEsId(type, id),
|
||||
type: this._type,
|
||||
refresh: 'wait_for',
|
||||
});
|
||||
|
||||
if (get(response, 'deleted') === 0) {
|
||||
if (response.found === false) {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} [options={}]
|
||||
* @property {string} options.type
|
||||
* @property {string} options.search
|
||||
* @property {string} options.searchFields - see Elasticsearch Simple Query String
|
||||
* @property {string} [options.type]
|
||||
* @property {string} [options.search]
|
||||
* @property {Array<string>} [options.searchFields] - see Elasticsearch Simple Query String
|
||||
* Query field argument for more information
|
||||
* @property {integer} [options.page=1]
|
||||
* @property {integer} [options.perPage=20]
|
||||
* @property {string} options.sortField
|
||||
* @property {string} options.sortOrder
|
||||
* @property {array|string} options.fields
|
||||
* @property {string} [options.sortField]
|
||||
* @property {string} [options.sortOrder]
|
||||
* @property {Array<string>} [options.fields]
|
||||
* @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page }
|
||||
*/
|
||||
async find(options = {}) {
|
||||
|
@ -136,23 +167,46 @@ export class SavedObjectsClient {
|
|||
fields,
|
||||
} = options;
|
||||
|
||||
if (searchFields && !Array.isArray(searchFields)) {
|
||||
throw new TypeError('options.searchFields must be an array');
|
||||
}
|
||||
|
||||
if (fields && !Array.isArray(fields)) {
|
||||
throw new TypeError('options.searchFields must be an array');
|
||||
}
|
||||
|
||||
const esOptions = {
|
||||
_source: includedFields(type, fields),
|
||||
size: perPage,
|
||||
from: perPage * (page - 1),
|
||||
body: createFindQuery(this._mappings, { search, searchFields, type, sortField, sortOrder })
|
||||
_source: includedFields(type, fields),
|
||||
body: {
|
||||
version: true,
|
||||
...getSearchDsl(this._mappings, {
|
||||
search,
|
||||
searchFields,
|
||||
type,
|
||||
sortField,
|
||||
sortOrder
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const response = await this._withKibanaIndex('search', esOptions);
|
||||
|
||||
return {
|
||||
saved_objects: get(response, 'hits.hits', []).map(hit => {
|
||||
return normalizeEsDoc(hit);
|
||||
}),
|
||||
total: get(response, 'hits.total', 0),
|
||||
page,
|
||||
per_page: perPage,
|
||||
page
|
||||
total: response.hits.total,
|
||||
saved_objects: response.hits.hits.map(hit => {
|
||||
const type = hit._source.type;
|
||||
|
||||
return {
|
||||
id: trimIdPrefix(hit._id, type),
|
||||
type,
|
||||
version: hit._version,
|
||||
attributes: hit._source[type],
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -173,24 +227,33 @@ export class SavedObjectsClient {
|
|||
return { saved_objects: [] };
|
||||
}
|
||||
|
||||
const docs = objects.reduce((acc, { type, id }) => {
|
||||
return [...acc, {}, createIdQuery({ type, id })];
|
||||
}, []);
|
||||
|
||||
const response = await this._withKibanaIndex('msearch', { body: docs });
|
||||
const responses = get(response, 'responses', []);
|
||||
const response = await this._withKibanaIndex('mget', {
|
||||
body: {
|
||||
docs: objects.map(object => ({
|
||||
_id: this._generateEsId(object.type, object.id),
|
||||
_type: this._type,
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
saved_objects: responses.map((r, i) => {
|
||||
const [hit] = get(r, 'hits.hits', []);
|
||||
saved_objects: response.docs.map((doc, i) => {
|
||||
const { id, type } = objects[i];
|
||||
|
||||
if (!hit) {
|
||||
return Object.assign({}, objects[i], {
|
||||
if (doc.found === false) {
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
error: { statusCode: 404, message: 'Not found' }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return normalizeEsDoc(hit, objects[i]);
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
version: doc._version,
|
||||
attributes: doc._source[type]
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
@ -203,14 +266,17 @@ export class SavedObjectsClient {
|
|||
* @returns {promise} - { id, type, version, attributes }
|
||||
*/
|
||||
async get(type, id) {
|
||||
const response = await this._withKibanaIndex('search', { body: createIdQuery({ type, id }) });
|
||||
const [hit] = get(response, 'hits.hits', []);
|
||||
const response = await this._withKibanaIndex('get', {
|
||||
id: this._generateEsId(type, id),
|
||||
type: this._type,
|
||||
});
|
||||
|
||||
if (!hit) {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
|
||||
return normalizeEsDoc(hit);
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
version: response._version,
|
||||
attributes: response._source[type]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,41 +289,24 @@ export class SavedObjectsClient {
|
|||
* @returns {promise}
|
||||
*/
|
||||
async update(type, id, attributes, options = {}) {
|
||||
const response = await this._withKibanaIndexAndMappingFallback('update', {
|
||||
id,
|
||||
type,
|
||||
const response = await this._withKibanaIndex('update', {
|
||||
id: this._generateEsId(type, id),
|
||||
type: this._type,
|
||||
version: options.version,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
doc: attributes
|
||||
}
|
||||
}, {
|
||||
type: V6_TYPE,
|
||||
id: `${type}:${id}`,
|
||||
body: {
|
||||
doc: {
|
||||
[type]: attributes
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return normalizeEsDoc(response, { id, type, attributes });
|
||||
}
|
||||
|
||||
_withKibanaIndexAndMappingFallback(method, params, fallbackParams) {
|
||||
const fallbacks = {
|
||||
'create': ['type_missing_exception'],
|
||||
'index': ['type_missing_exception'],
|
||||
'update': ['document_missing_exception']
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
version: response._version,
|
||||
attributes
|
||||
};
|
||||
|
||||
return this._withKibanaIndex(method, params).catch(err => {
|
||||
if (get(fallbacks, method, []).includes(get(err, 'data.type'))) {
|
||||
return this._withKibanaIndex(method, Object.assign({}, params, fallbackParams));
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
async _withKibanaIndex(method, params) {
|
||||
|
@ -270,4 +319,8 @@ export class SavedObjectsClient {
|
|||
throw handleEsError(err);
|
||||
}
|
||||
}
|
||||
|
||||
_generateEsId(type, id) {
|
||||
return `${type}:${id || uuid.v1()}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ describe('GET /api/saved_objects/{type?}', () => {
|
|||
expect(savedObjectsClient.find.calledOnce).to.be(true);
|
||||
|
||||
const options = savedObjectsClient.find.getCall(0).args[0];
|
||||
expect(options).to.eql({ perPage: 20, page: 1, searchFields: 'title' });
|
||||
expect(options).to.eql({ perPage: 20, page: 1, searchFields: ['title'] });
|
||||
});
|
||||
|
||||
it('accepts the query parameter fields as a string', async () => {
|
||||
|
@ -113,7 +113,7 @@ describe('GET /api/saved_objects/{type?}', () => {
|
|||
expect(savedObjectsClient.find.calledOnce).to.be(true);
|
||||
|
||||
const options = savedObjectsClient.find.getCall(0).args[0];
|
||||
expect(options).to.eql({ perPage: 20, page: 1, fields: 'title' });
|
||||
expect(options).to.eql({ perPage: 20, page: 1, fields: ['title'] });
|
||||
});
|
||||
|
||||
it('accepts the query parameter fields as an array', async () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ export const createCreateRoute = (prereqs) => {
|
|||
validate: {
|
||||
query: Joi.object().keys({
|
||||
overwrite: Joi.boolean().default(false)
|
||||
}),
|
||||
}).default(),
|
||||
params: Joi.object().keys({
|
||||
type: Joi.string().required(),
|
||||
id: Joi.string()
|
||||
|
|
|
@ -9,15 +9,15 @@ export const createFindRoute = (prereqs) => ({
|
|||
validate: {
|
||||
params: Joi.object().keys({
|
||||
type: Joi.string()
|
||||
}),
|
||||
}).default(),
|
||||
query: Joi.object().keys({
|
||||
per_page: Joi.number().min(0).default(20),
|
||||
page: Joi.number().min(0).default(1),
|
||||
type: Joi.string(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
search_fields: [Joi.string(), Joi.array().items(Joi.string())],
|
||||
fields: [Joi.string(), Joi.array().items(Joi.string())]
|
||||
})
|
||||
search_fields: Joi.array().items(Joi.string()).single(),
|
||||
fields: Joi.array().items(Joi.string()).single()
|
||||
}).default()
|
||||
},
|
||||
handler(request, reply) {
|
||||
const options = keysToCamelCaseShallow(request.query);
|
||||
|
|
|
@ -29,7 +29,7 @@ export function savedObjectsMixin(kbnServer, server) {
|
|||
server.decorate('server', 'savedObjectsClientFactory', ({ callCluster }) => {
|
||||
return new SavedObjectsClient(
|
||||
server.config().get('kibana.index'),
|
||||
kbnServer.uiExports.mappings.getCombined(),
|
||||
server.getKibanaIndexMappingsDsl(),
|
||||
callCluster
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import { has } from 'lodash';
|
||||
import { MappingsCollection } from '../ui_mappings';
|
||||
|
||||
|
||||
describe('UiExports', function () {
|
||||
describe('MappingsCollection', function () {
|
||||
let mappingsCollection;
|
||||
beforeEach(() => {
|
||||
mappingsCollection = new MappingsCollection();
|
||||
});
|
||||
|
||||
it('provides default mappings', function () {
|
||||
expect(mappingsCollection.getCombined()).to.be.an('object');
|
||||
});
|
||||
|
||||
it('registers new mappings', () => {
|
||||
mappingsCollection.register({
|
||||
foo: {
|
||||
'properties': {
|
||||
'bar': {
|
||||
'type': 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const mappings = mappingsCollection.getCombined();
|
||||
expect(has(mappings, 'foo.properties.bar')).to.be(true);
|
||||
});
|
||||
|
||||
it('throws and includes the plugin id in the mapping conflict message', () => {
|
||||
const mappings = { foo: 'bar' };
|
||||
const plugin = { plugin: 'abc123' };
|
||||
mappingsCollection.register(mappings, plugin);
|
||||
expect(mappingsCollection.register).withArgs(mappings, plugin).to.throwException(/abc123/);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -14,7 +14,8 @@ import { fieldFormatsMixin } from './field_formats_mixin';
|
|||
|
||||
export default async (kbnServer, server, config) => {
|
||||
const uiExports = kbnServer.uiExports = new UiExports({
|
||||
urlBasePath: config.get('server.basePath')
|
||||
urlBasePath: config.get('server.basePath'),
|
||||
kibanaIndexMappings: kbnServer.mappings,
|
||||
});
|
||||
|
||||
await kbnServer.mixin(uiSettingsMixin);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<div class="hintbox" ng-if="!indexedFields.length">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>No Compatible Fields:</strong> The "{{ vis.indexPattern.id }}" index pattern does not contain any of the following field types: {{ agg.type.params.byName.field.filterFieldTypes | commaList:false }}
|
||||
<strong>No Compatible Fields:</strong> The "{{ vis.indexPattern.title }}" index pattern does not contain any of the following field types: {{ agg.type.params.byName.field.filterFieldTypes | commaList:false }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
14
src/ui/public/chrome/api/angular.js
vendored
14
src/ui/public/chrome/api/angular.js
vendored
|
@ -11,7 +11,6 @@ const URL_LIMIT_WARN_WITHIN = 1000;
|
|||
|
||||
export function initAngularApi(chrome, internals) {
|
||||
chrome.getFirstPathSegment = _.noop;
|
||||
chrome.getBreadcrumbs = _.noop;
|
||||
|
||||
chrome.setupAngular = function () {
|
||||
const kibana = uiModules.get('kibana');
|
||||
|
@ -49,19 +48,6 @@ export function initAngularApi(chrome, internals) {
|
|||
return $location.path().split('/')[1];
|
||||
};
|
||||
|
||||
chrome.getBreadcrumbs = () => {
|
||||
const path = $location.path();
|
||||
let length = path.length - 1;
|
||||
|
||||
// trim trailing slash
|
||||
if (path.charAt(length) === '/') {
|
||||
length--;
|
||||
}
|
||||
|
||||
return path.substr(1, length)
|
||||
.split('/');
|
||||
};
|
||||
|
||||
const notify = new Notifier();
|
||||
const urlOverflow = Private(UrlOverflowServiceProvider);
|
||||
const check = () => {
|
||||
|
|
|
@ -89,7 +89,7 @@ export class SavedObjectLoader {
|
|||
* @param size
|
||||
* @returns {Promise}
|
||||
*/
|
||||
find(search, size = 100) {
|
||||
find(search = '', size = 100) {
|
||||
return this.savedObjectsClient.find(
|
||||
{
|
||||
type: this.lowercaseType,
|
||||
|
|
|
@ -458,7 +458,7 @@ describe('index pattern', function () {
|
|||
expect(notif.content).to.match(MARKDOWN_LINK_RE);
|
||||
|
||||
const [,text,url] = notif.content.match(MARKDOWN_LINK_RE);
|
||||
expect(text).to.contain(indexPattern.id);
|
||||
expect(text).to.contain(indexPattern.title);
|
||||
expect(url).to.contain(indexPattern.id);
|
||||
expect(url).to.contain('management/kibana/indices');
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ export function IndexPatternsFieldProvider(Private, shortDotsFilter, $rootScope,
|
|||
notify.error(
|
||||
'Unknown field type "' + spec.type + '"' +
|
||||
' for field "' + spec.name + '"' +
|
||||
' in indexPattern "' + indexPattern.id + '"'
|
||||
' in indexPattern "' + indexPattern.title + '"'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,11 +93,15 @@ export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise,
|
|||
// give index pattern all of the values in _source
|
||||
_.assign(indexPattern, response._source);
|
||||
|
||||
if (!indexPattern.title) {
|
||||
indexPattern.title = indexPattern.id;
|
||||
}
|
||||
|
||||
if (indexPattern.isUnsupportedTimePattern()) {
|
||||
if (!isUserAwareOfUnsupportedTimePattern(indexPattern)) {
|
||||
const warning = (
|
||||
'Support for time-intervals has been removed. ' +
|
||||
`View the ["${indexPattern.id}" index pattern in management](` +
|
||||
`View the ["${indexPattern.title}" index pattern in management](` +
|
||||
kbnUrl.getRouteHref(indexPattern, 'edit') +
|
||||
') for more information.'
|
||||
);
|
||||
|
@ -403,7 +407,7 @@ export function IndexPatternProvider(Private, $http, config, kbnIndex, Promise,
|
|||
});
|
||||
}
|
||||
|
||||
async save() {
|
||||
save() {
|
||||
return savedObjectsClient.update(type, this.id, this.prepBody())
|
||||
.then(({ id }) => setId(this, id));
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
import { getBreadCrumbUrls } from '../bread_crumbs/bread_crumb_urls';
|
||||
|
||||
describe('getBreadCrumbUrls', function () {
|
||||
|
||||
it('returns urls for the breadcrumbs', function () {
|
||||
const breadCrumbUrls = getBreadCrumbUrls(
|
||||
['path1', 'path2', 'a', 'longlonglonglong'],
|
||||
'http://test.com/path1/path2/a/longlonglonglong');
|
||||
expect(breadCrumbUrls.length).to.equal(4);
|
||||
expect(breadCrumbUrls[0].url).to.equal('http://test.com/path1');
|
||||
expect(breadCrumbUrls[0].title).to.equal('Path 1');
|
||||
|
||||
expect(breadCrumbUrls[1].url).to.equal('http://test.com/path1/path2');
|
||||
expect(breadCrumbUrls[1].title).to.equal('Path 2');
|
||||
|
||||
expect(breadCrumbUrls[2].url).to.equal('http://test.com/path1/path2/a');
|
||||
expect(breadCrumbUrls[2].title).to.equal('A');
|
||||
|
||||
expect(breadCrumbUrls[3].url).to.equal('http://test.com/path1/path2/a/longlonglonglong');
|
||||
expect(breadCrumbUrls[3].title).to.equal('Longlonglonglong');
|
||||
});
|
||||
|
||||
it('is case insensitive', function () {
|
||||
const breadCrumbUrls = getBreadCrumbUrls(['paTh1', 'path2'], 'http://TEST.com/paTh1/path2');
|
||||
expect(breadCrumbUrls.length).to.equal(2);
|
||||
expect(breadCrumbUrls[0].url).to.equal('http://TEST.com/paTh1');
|
||||
expect(breadCrumbUrls[0].path).to.equal('paTh1');
|
||||
expect(breadCrumbUrls[0].title).to.equal('Pa Th 1');
|
||||
|
||||
expect(breadCrumbUrls[1].url).to.equal('http://TEST.com/paTh1/path2');
|
||||
expect(breadCrumbUrls[1].title).to.equal('Path 2');
|
||||
});
|
||||
|
||||
it('handles no breadcrumbs case', function () {
|
||||
const breadCrumbUrls = getBreadCrumbUrls([], 'http://test.com');
|
||||
expect(breadCrumbUrls.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('handles spaces in breadcrumbs', function () {
|
||||
const breadCrumbUrls = getBreadCrumbUrls(
|
||||
['something', 'somethingElse', 'snake_case', 'longLongLongLong'],
|
||||
'http://test.com/something/somethingElse/snake_case/longLongLongLong');
|
||||
expect(breadCrumbUrls.length).to.equal(4);
|
||||
expect(breadCrumbUrls[0].url).to.equal('http://test.com/something');
|
||||
expect(breadCrumbUrls[0].title).to.equal('Something');
|
||||
|
||||
expect(breadCrumbUrls[1].url).to.equal('http://test.com/something/somethingElse');
|
||||
expect(breadCrumbUrls[1].path).to.equal('somethingElse');
|
||||
expect(breadCrumbUrls[1].title).to.equal('Something Else');
|
||||
|
||||
expect(breadCrumbUrls[2].url).to.equal('http://test.com/something/somethingElse/snake_case');
|
||||
expect(breadCrumbUrls[2].path).to.equal('snake_case');
|
||||
expect(breadCrumbUrls[2].title).to.equal('Snake Case');
|
||||
|
||||
expect(breadCrumbUrls[3].url).to.equal('http://test.com/something/somethingElse/snake_case/longLongLongLong');
|
||||
expect(breadCrumbUrls[3].title).to.equal('Long Long Long Long');
|
||||
});
|
||||
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
/**
|
||||
* @typedef BreadCrumbUrl {Object}
|
||||
* @property title {String} the display title for the breadcrumb
|
||||
* @property path {String} the subdirectory for this particular breadcrumb
|
||||
* @property url {String} a url for the breadcrumb
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array.<String>} breadcrumbs An array of breadcrumbs for the given url.
|
||||
* @param {String} url The current url that the breadcrumbs have been generated for
|
||||
* @returns {Array.<BreadCrumbUrl> An array comprised of objects that
|
||||
* will contain both the url for the given breadcrumb, as well as the breadcrumb the url
|
||||
* was generated for.
|
||||
*/
|
||||
export function getBreadCrumbUrls(breadcrumbs, url) {
|
||||
// the url should not have a slash on the end or else the route will not be properly built
|
||||
const urlBase = url.replace(/\/+$/, '').replace(breadcrumbs.join('/'), '');
|
||||
return breadcrumbs.map((path, index) => {
|
||||
return {
|
||||
path: path,
|
||||
title: _.startCase(path),
|
||||
url: urlBase + breadcrumbs.slice(0, index + 1).join('/')
|
||||
};
|
||||
});
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
<div class="kuiLocalBreadcrumbs" data-test-subj="breadcrumbs">
|
||||
<div
|
||||
class="kuiLocalBreadcrumb"
|
||||
ng-if="useLinks && (!omitPages || !omitPages.includes(breadcrumb.path))"
|
||||
ng-if="useLinks"
|
||||
ng-repeat="breadcrumb in breadcrumbs"
|
||||
>
|
||||
<a
|
||||
class="kuiLocalBreadcrumb__link"
|
||||
href="{{breadcrumb.url}}"
|
||||
href="{{ breadcrumb.href }}"
|
||||
>
|
||||
{{breadcrumb.title}}
|
||||
{{ breadcrumb.display }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiLocalBreadcrumb"
|
||||
ng-if="!useLinks && (!omitPages || !omitPages.includes(breadcrumb.path))"
|
||||
ng-if="!useLinks"
|
||||
ng-repeat="breadcrumb in breadcrumbs"
|
||||
>
|
||||
{{ breadcrumb.title }}
|
||||
{{ breadcrumb.display }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import _ from 'lodash';
|
||||
import chrome from 'ui/chrome/chrome';
|
||||
import breadCrumbsTemplate from './bread_crumbs.html';
|
||||
import { getBreadCrumbUrls } from './bread_crumb_urls';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import uiRouter from 'ui/routes';
|
||||
|
||||
const module = uiModules.get('kibana');
|
||||
|
||||
module.directive('breadCrumbs', function ($location) {
|
||||
module.directive('breadCrumbs', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
|
@ -30,20 +29,29 @@ module.directive('breadCrumbs', function ($location) {
|
|||
},
|
||||
template: breadCrumbsTemplate,
|
||||
controller: function ($scope) {
|
||||
const breadcrumbs = chrome.getBreadcrumbs();
|
||||
|
||||
if ($scope.useLinks) {
|
||||
const url = '#' + $location.path();
|
||||
$scope.breadcrumbs = getBreadCrumbUrls(breadcrumbs, url);
|
||||
} else {
|
||||
$scope.breadcrumbs = breadcrumbs.map(path => ({
|
||||
path: path,
|
||||
title: _.startCase(path)
|
||||
}));
|
||||
function omitPagesFilter(crumb) {
|
||||
return (
|
||||
!$scope.omitPages ||
|
||||
!$scope.omitPages.includes(crumb.id)
|
||||
);
|
||||
}
|
||||
if ($scope.omitCurrentPage === true) {
|
||||
$scope.breadcrumbs.pop();
|
||||
|
||||
function omitCurrentPageFilter(crumb) {
|
||||
return !($scope.omitCurrentPage && crumb.current);
|
||||
}
|
||||
|
||||
$scope.$watchMulti([
|
||||
'[]omitPages',
|
||||
'omitCurrentPage'
|
||||
], function getBreadcrumbs() {
|
||||
$scope.breadcrumbs = (
|
||||
uiRouter
|
||||
.getBreadcrumbs()
|
||||
.filter(omitPagesFilter)
|
||||
.filter(omitCurrentPageFilter)
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
22
src/ui/public/routes/breadcrumbs.js
Normal file
22
src/ui/public/routes/breadcrumbs.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { trim, startCase } from 'lodash';
|
||||
|
||||
/**
|
||||
* Take a path (from $location.path() usually) and parse
|
||||
* it's segments into a list of breadcrumbs
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Array<Breadcrumb>}
|
||||
*/
|
||||
export function parsePathToBreadcrumbs(path) {
|
||||
return trim(path, '/')
|
||||
.split('/')
|
||||
.reduce((acc, id, i, parts) => [
|
||||
...acc,
|
||||
{
|
||||
id,
|
||||
display: startCase(id),
|
||||
href: i === 0 ? `#/${id}` : `${acc[i - 1].href}/${id}`,
|
||||
current: i === (parts.length - 1)
|
||||
}
|
||||
], []);
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import _ from 'lodash';
|
||||
import { defaultsDeep, wrap } from 'lodash';
|
||||
|
||||
import { wrapRouteWithPrep } from './wrap_route_with_prep';
|
||||
import { RouteSetupManager } from './route_setup_manager';
|
||||
import { parsePathToBreadcrumbs } from './breadcrumbs';
|
||||
|
||||
// eslint-disable-next-line kibana-custom/no-default-export
|
||||
export default function RouteManager() {
|
||||
|
@ -16,18 +17,17 @@ export default function RouteManager() {
|
|||
const path = args[0];
|
||||
const route = args[1] || {};
|
||||
|
||||
// merge in any defaults
|
||||
defaults.forEach(function (args) {
|
||||
if (args[0].test(path)) {
|
||||
_.merge(route, args[1]);
|
||||
defaults.forEach(def => {
|
||||
if (def.regex.test(path)) {
|
||||
defaultsDeep(route, def.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (route.reloadOnSearch === void 0) {
|
||||
if (route.reloadOnSearch == null) {
|
||||
route.reloadOnSearch = false;
|
||||
}
|
||||
|
||||
if (route.requireDefaultIndex === void 0) {
|
||||
if (route.requireDefaultIndex == null) {
|
||||
route.requireDefaultIndex = false;
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,22 @@ export default function RouteManager() {
|
|||
}
|
||||
};
|
||||
|
||||
self.run = function ($location, $route, $injector) {
|
||||
self.getBreadcrumbs = () => {
|
||||
const breadcrumbs = parsePathToBreadcrumbs($location.path());
|
||||
const map = $route.current.mapBreadcrumbs;
|
||||
return map ? $injector.invoke(map, null, { breadcrumbs }) : breadcrumbs;
|
||||
};
|
||||
};
|
||||
|
||||
const wrapSetupAndChain = (fn, ...args) => {
|
||||
fn.apply(setup, args);
|
||||
return this;
|
||||
};
|
||||
|
||||
this.addSetupWork = _.wrap(setup.addSetupWork, wrapSetupAndChain);
|
||||
this.afterSetupWork = _.wrap(setup.afterSetupWork, wrapSetupAndChain);
|
||||
this.afterWork = _.wrap(setup.afterWork, wrapSetupAndChain);
|
||||
this.addSetupWork = wrap(setup.addSetupWork, wrapSetupAndChain);
|
||||
this.afterSetupWork = wrap(setup.afterSetupWork, wrapSetupAndChain);
|
||||
this.afterWork = wrap(setup.afterWork, wrapSetupAndChain);
|
||||
|
||||
self.when = function (path, route) {
|
||||
when.push([path, route]);
|
||||
|
@ -57,8 +65,8 @@ export default function RouteManager() {
|
|||
|
||||
// before attaching the routes to the routeProvider, test the RE
|
||||
// against the .when() path and add/override the resolves if there is a match
|
||||
self.defaults = function (RE, def) {
|
||||
defaults.push([RE, def]);
|
||||
self.defaults = function (regex, value) {
|
||||
defaults.push({ regex, value });
|
||||
return self;
|
||||
};
|
||||
|
||||
|
@ -67,5 +75,10 @@ export default function RouteManager() {
|
|||
return self;
|
||||
};
|
||||
|
||||
self.getBreadcrumbs = function () {
|
||||
// overwritten in self.run();
|
||||
return [];
|
||||
};
|
||||
|
||||
self.RouteManager = RouteManager;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,17 @@ import { WAIT_FOR_URL_CHANGE_TOKEN } from './route_setup_manager';
|
|||
const defaultRouteManager = new RouteManager();
|
||||
|
||||
// eslint-disable-next-line kibana-custom/no-default-export
|
||||
export default {
|
||||
...defaultRouteManager,
|
||||
WAIT_FOR_URL_CHANGE_TOKEN,
|
||||
enable() {
|
||||
uiModules
|
||||
.get('kibana', ['ngRoute'])
|
||||
.config(defaultRouteManager.config);
|
||||
export default Object.create(defaultRouteManager, {
|
||||
WAIT_FOR_URL_CHANGE_TOKEN: {
|
||||
value: WAIT_FOR_URL_CHANGE_TOKEN
|
||||
},
|
||||
|
||||
enable: {
|
||||
value() {
|
||||
uiModules
|
||||
.get('kibana', ['ngRoute'])
|
||||
.config(defaultRouteManager.config)
|
||||
.run(defaultRouteManager.run);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -298,7 +298,7 @@ describe('SavedObjectsClient', () => {
|
|||
expect($http.calledOnce).to.be(true);
|
||||
|
||||
const options = $http.getCall(0).args[0];
|
||||
expect(options.url).to.eql(`${basePath}/api/saved_objects/index-pattern?type=index-pattern&invalid=true`);
|
||||
expect(options.url).to.eql(`${basePath}/api/saved_objects/?type=index-pattern&invalid=true`);
|
||||
});
|
||||
|
||||
it('accepts fields', () => {
|
||||
|
|
|
@ -17,7 +17,7 @@ export function findObjectByTitle(savedObjectsClient, type, title) {
|
|||
type,
|
||||
perPage: 10,
|
||||
search: `"${title}"`,
|
||||
searchFields: 'title',
|
||||
searchFields: ['title'],
|
||||
fields: ['title']
|
||||
}).then(response => {
|
||||
const match = find(response.savedObjects, (obj) => {
|
||||
|
|
|
@ -74,7 +74,7 @@ export class SavedObjectsClient {
|
|||
* @returns {promise} - { savedObjects: [ SavedObject({ id, type, version, attributes }) ]}
|
||||
*/
|
||||
find(options = {}) {
|
||||
const url = this._getUrl([options.type], keysToSnakeCaseShallow(options));
|
||||
const url = this._getUrl([], keysToSnakeCaseShallow(options));
|
||||
|
||||
return this._request('GET', url).then(resp => {
|
||||
resp.saved_objects = resp.saved_objects.map(d => this.createSavedObject(d));
|
||||
|
|
|
@ -3,10 +3,9 @@ import minimatch from 'minimatch';
|
|||
|
||||
import UiAppCollection from './ui_app_collection';
|
||||
import UiNavLinkCollection from './ui_nav_link_collection';
|
||||
import { MappingsCollection } from './ui_mappings';
|
||||
|
||||
export default class UiExports {
|
||||
constructor({ urlBasePath }) {
|
||||
constructor({ urlBasePath, kibanaIndexMappings }) {
|
||||
this.navLinks = new UiNavLinkCollection(this);
|
||||
this.apps = new UiAppCollection(this);
|
||||
this.aliases = {
|
||||
|
@ -29,7 +28,7 @@ export default class UiExports {
|
|||
this.bundleProviders = [];
|
||||
this.defaultInjectedVars = {};
|
||||
this.injectedVarsReplacers = [];
|
||||
this.mappings = new MappingsCollection();
|
||||
this.kibanaIndexMappings = kibanaIndexMappings;
|
||||
}
|
||||
|
||||
consumePlugin(plugin) {
|
||||
|
@ -146,7 +145,7 @@ export default class UiExports {
|
|||
|
||||
case 'mappings':
|
||||
return (plugin, mappings) => {
|
||||
this.mappings.register(mappings, { plugin: plugin.id });
|
||||
this.kibanaIndexMappings.addRootProperties(mappings, { plugin: plugin.id });
|
||||
};
|
||||
|
||||
case 'replaceInjectedVars':
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
class MappingsCollection {
|
||||
constructor() {
|
||||
this._defaultMappings = {
|
||||
'_default_': {
|
||||
'dynamic': 'strict'
|
||||
},
|
||||
config: {
|
||||
dynamic: true,
|
||||
properties: {
|
||||
buildNum: {
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
this._currentMappings = _.cloneDeep(this._defaultMappings);
|
||||
}
|
||||
|
||||
getCombined = () => {
|
||||
return this._currentMappings;
|
||||
}
|
||||
|
||||
register = (newMappings, options = {}) => {
|
||||
Object.keys(this._currentMappings).forEach(key => {
|
||||
if (newMappings.hasOwnProperty(key)) {
|
||||
const pluginPartial = options.plugin ? `registered by plugin ${options.plugin} ` : '';
|
||||
throw new Error(`Mappings for ${key} ${pluginPartial}have already been defined`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
Object.assign(this._currentMappings, newMappings);
|
||||
}
|
||||
}
|
||||
|
||||
export { MappingsCollection };
|
|
@ -74,7 +74,6 @@ module.exports = function (grunt) {
|
|||
...stdDevArgs,
|
||||
'--dev',
|
||||
'--no-base-path',
|
||||
'--no-ssl',
|
||||
'--optimize.enabled=false',
|
||||
'--elasticsearch.url=' + format(esTestServerUrlParts),
|
||||
'--server.port=' + kibanaTestServerUrlParts.port,
|
||||
|
|
|
@ -13,6 +13,8 @@ export default async function ({ readConfigFile }) {
|
|||
services: {
|
||||
es: commonConfig.get('services.es'),
|
||||
esArchiver: commonConfig.get('services.esArchiver'),
|
||||
kibanaIndex: commonConfig.get('services.kibanaIndex'),
|
||||
retry: commonConfig.get('services.retry'),
|
||||
supertest: SupertestProvider,
|
||||
chance: ChanceProvider,
|
||||
},
|
||||
|
|
Binary file not shown.
|
@ -30,4 +30,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -46,4 +46,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -121,4 +121,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -4,242 +4,241 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"section": {
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {
|
||||
KibanaServerProvider,
|
||||
KibanaIndexProvider,
|
||||
EsProvider,
|
||||
EsArchiverProvider,
|
||||
RetryProvider,
|
||||
} from './services';
|
||||
|
||||
import { esTestServerUrlParts } from '../es_test_server_url_parts';
|
||||
|
@ -15,6 +17,8 @@ export default function () {
|
|||
},
|
||||
services: {
|
||||
kibanaServer: KibanaServerProvider,
|
||||
kibanaIndex: KibanaIndexProvider,
|
||||
retry: RetryProvider,
|
||||
es: EsProvider,
|
||||
esArchiver: EsArchiverProvider,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export { KibanaServerProvider } from './kibana_server';
|
||||
export { KibanaIndexProvider } from './kibana_index';
|
||||
export { EsProvider } from './es';
|
||||
export { EsArchiverProvider } from './es_archiver';
|
||||
export { RetryProvider } from './retry';
|
||||
|
|
21
test/common/services/kibana_index.js
Normal file
21
test/common/services/kibana_index.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
export async function KibanaIndexProvider({ getService }) {
|
||||
const retry = getService('retry');
|
||||
const es = getService('es');
|
||||
|
||||
const KIBANA_INDEX_NAME = '.kibana';
|
||||
const esIndex = await retry.try(async () => {
|
||||
return await es.indices.get({
|
||||
index: KIBANA_INDEX_NAME
|
||||
});
|
||||
});
|
||||
|
||||
return new class KibanaIndex {
|
||||
getName() {
|
||||
return KIBANA_INDEX_NAME;
|
||||
}
|
||||
|
||||
getMappingsDsl() {
|
||||
return Object.values(esIndex)[0].mappings;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -6,18 +6,19 @@ import { KibanaServerStatus } from './status';
|
|||
import { KibanaServerUiSettings } from './ui_settings';
|
||||
import { KibanaServerVersion } from './version';
|
||||
|
||||
export function KibanaServerProvider({ getService }) {
|
||||
export async function KibanaServerProvider({ getService }) {
|
||||
const log = getService('log');
|
||||
const config = getService('config');
|
||||
const lifecycle = getService('lifecycle');
|
||||
const es = getService('es');
|
||||
const kibanaIndex = await getService('kibanaIndex').init();
|
||||
|
||||
class KibanaServer {
|
||||
return new class KibanaServer {
|
||||
constructor() {
|
||||
const url = formatUrl(config.get('servers.kibana'));
|
||||
this.status = new KibanaServerStatus(url);
|
||||
this.version = new KibanaServerVersion(this.status);
|
||||
this.uiSettings = new KibanaServerUiSettings(log, es, this.version);
|
||||
this.uiSettings = new KibanaServerUiSettings(log, es, kibanaIndex, this.version);
|
||||
|
||||
lifecycle.on('beforeEachTest', async () => {
|
||||
await this.waitForStabilization();
|
||||
|
@ -63,7 +64,5 @@ export function KibanaServerProvider({ getService }) {
|
|||
const docState = exists ? 'exists' : `doesn't exist`;
|
||||
throw new Error(`Kibana never stabilized: config doc ${docState} and status is ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
return new KibanaServer();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,46 +1,49 @@
|
|||
import { get } from 'lodash';
|
||||
import toPath from 'lodash/internal/toPath';
|
||||
|
||||
import { SavedObjectsClient } from '../../../../src/server/saved_objects';
|
||||
|
||||
function createCallCluster(es) {
|
||||
return function callCluster(method, params) {
|
||||
const path = toPath(method);
|
||||
const contextPath = path.slice(0, -1);
|
||||
|
||||
const action = get(es, path);
|
||||
const context = contextPath.length ? get(es, contextPath) : es;
|
||||
|
||||
return action.call(context, params);
|
||||
};
|
||||
}
|
||||
|
||||
export class KibanaServerUiSettings {
|
||||
constructor(log, es, kibanaVersion) {
|
||||
this.es = es;
|
||||
this.log = log;
|
||||
this.kibanaVersion = kibanaVersion;
|
||||
constructor(log, es, kibanaIndex, kibanaVersion) {
|
||||
this._log = log;
|
||||
this._kibanaVersion = kibanaVersion;
|
||||
this._savedObjectsClient = new SavedObjectsClient(
|
||||
kibanaIndex.getName(),
|
||||
kibanaIndex.getMappingsDsl(),
|
||||
createCallCluster(es)
|
||||
);
|
||||
}
|
||||
|
||||
async _docParams() {
|
||||
const { kibanaVersion } = this;
|
||||
return {
|
||||
index: '.kibana',
|
||||
type: 'config',
|
||||
id: await kibanaVersion.get()
|
||||
};
|
||||
async _id() {
|
||||
return await this._kibanaVersion.get();
|
||||
}
|
||||
|
||||
async existInEs() {
|
||||
const { es } = this;
|
||||
return await es.exists(await this._docParams());
|
||||
}
|
||||
|
||||
async _read() {
|
||||
const { log, es } = this;
|
||||
try {
|
||||
const doc = await es.get(await this._docParams());
|
||||
log.verbose('Fetched kibana config doc', doc);
|
||||
return doc;
|
||||
} catch (err) {
|
||||
log.debug('Failed to fetch kibana config doc', err.message);
|
||||
return;
|
||||
}
|
||||
return !!(await this._read());
|
||||
}
|
||||
|
||||
/*
|
||||
** Gets defaultIndex from the config doc.
|
||||
*/
|
||||
async getDefaultIndex() {
|
||||
const { log } = this;
|
||||
const doc = await this._read();
|
||||
const defaultIndex = get(doc, ['_source', 'defaultIndex']);
|
||||
log.verbose('uiSettings.defaultIndex: %j', defaultIndex);
|
||||
if (!doc) {
|
||||
throw new TypeError('Failed to fetch kibana config doc');
|
||||
}
|
||||
const defaultIndex = doc.attributes.defaultIndex;
|
||||
this._log.verbose('uiSettings.defaultIndex: %j', defaultIndex);
|
||||
return defaultIndex;
|
||||
}
|
||||
|
||||
|
@ -62,12 +65,10 @@ export class KibanaServerUiSettings {
|
|||
}
|
||||
|
||||
async replace(doc) {
|
||||
const { log, es } = this;
|
||||
log.debug('updating kibana config doc: %j', doc);
|
||||
await es.index({
|
||||
...(await this._docParams()),
|
||||
refresh: 'wait_for',
|
||||
body: doc,
|
||||
this._log.debug('replacing kibana config doc: %j', doc);
|
||||
await this._savedObjectsClient.create('config', { doc }, {
|
||||
id: await this._id(),
|
||||
overwrite: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -75,13 +76,19 @@ export class KibanaServerUiSettings {
|
|||
* Add fields to the config doc (like setting timezone and defaultIndex)
|
||||
* @return {Promise} A promise that is resolved when elasticsearch has a response
|
||||
*/
|
||||
async update(doc) {
|
||||
const { log, es } = this;
|
||||
log.debug('updating kibana config doc: %j', doc);
|
||||
await es.update({
|
||||
...(await this._docParams()),
|
||||
refresh: 'wait_for',
|
||||
body: { doc, upsert: doc },
|
||||
});
|
||||
async update(updates) {
|
||||
this._log.debug('applying update to kibana config: %j', updates);
|
||||
await this._savedObjectsClient.update('config', await this._id(), updates);
|
||||
}
|
||||
|
||||
async _read() {
|
||||
try {
|
||||
const doc = await this._savedObjectsClient.get('config', await this._id());
|
||||
this._log.verbose('Fetched kibana config doc', doc);
|
||||
return doc;
|
||||
} catch (err) {
|
||||
this._log.debug('Failed to fetch kibana config doc', err.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
RemoteProvider,
|
||||
FilterBarProvider,
|
||||
FindProvider,
|
||||
RetryProvider,
|
||||
TestSubjectsProvider,
|
||||
DocTableProvider,
|
||||
ScreenshotsProvider,
|
||||
|
@ -53,10 +52,11 @@ export default async function ({ readConfigFile }) {
|
|||
es: commonConfig.get('services.es'),
|
||||
esArchiver: commonConfig.get('services.esArchiver'),
|
||||
kibanaServer: commonConfig.get('services.kibanaServer'),
|
||||
kibanaIndex: commonConfig.get('services.kibanaIndex'),
|
||||
retry: commonConfig.get('services.retry'),
|
||||
remote: RemoteProvider,
|
||||
filterBar: FilterBarProvider,
|
||||
find: FindProvider,
|
||||
retry: RetryProvider,
|
||||
testSubjects: TestSubjectsProvider,
|
||||
docTable: DocTableProvider,
|
||||
screenshots: ScreenshotsProvider,
|
||||
|
|
Binary file not shown.
|
@ -4,244 +4,243 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,242 +4,241 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,249 +4,248 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"section": {
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dateFormat:tz": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"dateFormat:tz": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,242 +4,241 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"section": {
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,240 +4,239 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"section": {
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,260 +4,259 @@
|
|||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"mapping": {
|
||||
"single_type": "false"
|
||||
},
|
||||
"number_of_shards": "1",
|
||||
"mapper": {
|
||||
"dynamic": "false"
|
||||
},
|
||||
"mapper.dynamic": false,
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"doc": {
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"_default_": {
|
||||
"dynamic": "strict"
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"dateFormat:tz": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultIndex": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"dateFormat:tz": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultIndex": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export { RetryProvider } from './retry';
|
||||
export { FilterBarProvider } from './filter_bar';
|
||||
export { FindProvider } from './find';
|
||||
export { TestSubjectsProvider } from './test_subjects';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue