Add usage collection for savedObject tagging (#83160)

* add so tagging usage collection

* update telemetry mappings

* fix types

* remove check on esClient presence

* update schema and README
This commit is contained in:
Pierre Gayvallet 2020-11-20 16:34:02 +01:00 committed by GitHub
parent 7c80a6be68
commit 22e494e386
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1035 additions and 32 deletions

View file

@ -1,3 +1,53 @@
# SavedObjectsTagging
Add tagging capability to saved objects
Add tagging capability to saved objects
## Integrating tagging on a new object type
In addition to use the UI api to plug the tagging feature in your application, there is a couple
things that needs to be done on the server:
### Add read-access to the `tag` SO type to your feature's capabilities
In order to be able to fetch the tags assigned to an object, the user must have read permission
for the `tag` saved object type. Which is why all features relying on SO tagging must update
their capabilities.
```typescript
features.registerKibanaFeature({
id: 'myFeature',
// ...
privileges: {
all: {
// ...
savedObject: {
all: ['some-type'],
read: ['tag'], // <-- HERE
},
},
read: {
// ...
savedObject: {
all: [],
read: ['some-type', 'tag'], // <-- AND HERE
},
},
},
});
```
### Update the SOT telemetry collector schema to add the new type
The schema is located here: `x-pack/plugins/saved_objects_tagging/server/usage/schema.ts`. You
just need to add the name of the SO type you are adding.
```ts
export const tagUsageCollectorSchema: MakeSchemaFrom<TaggingUsageData> = {
// ...
types: {
dashboard: perTypeSchema,
visualization: perTypeSchema,
// <-- add your type here
},
};
```

View file

@ -6,5 +6,6 @@
"ui": true,
"configPath": ["xpack", "saved_object_tagging"],
"requiredPlugins": ["features", "management", "savedObjectsTaggingOss"],
"requiredBundles": ["kibanaReact"]
"requiredBundles": ["kibanaReact"],
"optionalPlugins": ["usageCollection"]
}

View file

@ -8,3 +8,8 @@ export const registerRoutesMock = jest.fn();
jest.doMock('./routes', () => ({
registerRoutes: registerRoutesMock,
}));
export const createTagUsageCollectorMock = jest.fn();
jest.doMock('./usage', () => ({
createTagUsageCollector: createTagUsageCollectorMock,
}));

View file

@ -4,20 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { registerRoutesMock } from './plugin.test.mocks';
import { registerRoutesMock, createTagUsageCollectorMock } from './plugin.test.mocks';
import { coreMock } from '../../../../src/core/server/mocks';
import { featuresPluginMock } from '../../features/server/mocks';
import { usageCollectionPluginMock } from '../../../../src/plugins/usage_collection/server/mocks';
import { SavedObjectTaggingPlugin } from './plugin';
import { savedObjectsTaggingFeature } from './features';
describe('SavedObjectTaggingPlugin', () => {
let plugin: SavedObjectTaggingPlugin;
let featuresPluginSetup: ReturnType<typeof featuresPluginMock.createSetup>;
let usageCollectionSetup: ReturnType<typeof usageCollectionPluginMock.createSetupContract>;
beforeEach(() => {
plugin = new SavedObjectTaggingPlugin(coreMock.createPluginInitializerContext());
featuresPluginSetup = featuresPluginMock.createSetup();
usageCollectionSetup = usageCollectionPluginMock.createSetupContract();
// `usageCollection` 'mocked' implementation use the real `CollectorSet` implementation
// that throws when registering things that are not collectors.
// We just want to assert that it was called here, so jest.fn is fine.
usageCollectionSetup.registerCollector = jest.fn();
});
afterEach(() => {
registerRoutesMock.mockReset();
createTagUsageCollectorMock.mockReset();
});
describe('#setup', () => {
@ -43,5 +55,18 @@ describe('SavedObjectTaggingPlugin', () => {
savedObjectsTaggingFeature
);
});
it('registers the usage collector if `usageCollection` is present', async () => {
const tagUsageCollector = Symbol('saved_objects_tagging');
createTagUsageCollectorMock.mockReturnValue(tagUsageCollector);
await plugin.setup(coreMock.createSetup(), {
features: featuresPluginSetup,
usageCollection: usageCollectionSetup,
});
expect(usageCollectionSetup.registerCollector).toHaveBeenCalledTimes(1);
expect(usageCollectionSetup.registerCollector).toHaveBeenCalledWith(tagUsageCollector);
});
});
});

View file

@ -4,22 +4,36 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { CoreSetup, CoreStart, PluginInitializerContext, Plugin } from 'src/core/server';
import { Observable } from 'rxjs';
import {
CoreSetup,
CoreStart,
PluginInitializerContext,
Plugin,
SharedGlobalConfig,
} from 'src/core/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server';
import { savedObjectsTaggingFeature } from './features';
import { tagType } from './saved_objects';
import { ITagsRequestHandlerContext } from './types';
import { registerRoutes } from './routes';
import { TagsRequestHandlerContext } from './request_handler_context';
import { registerRoutes } from './routes';
import { createTagUsageCollector } from './usage';
interface SetupDeps {
features: FeaturesPluginSetup;
usageCollection?: UsageCollectionSetup;
}
export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> {
constructor(context: PluginInitializerContext) {}
private readonly legacyConfig$: Observable<SharedGlobalConfig>;
public setup({ savedObjects, http }: CoreSetup, { features }: SetupDeps) {
constructor(context: PluginInitializerContext) {
this.legacyConfig$ = context.config.legacy.globalConfig$;
}
public setup({ savedObjects, http }: CoreSetup, { features, usageCollection }: SetupDeps) {
savedObjects.registerType(tagType);
const router = http.createRouter();
@ -34,6 +48,15 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> {
features.registerKibanaFeature(savedObjectsTaggingFeature);
if (usageCollection) {
usageCollection.registerCollector(
createTagUsageCollector({
usageCollection,
legacyConfig$: this.legacyConfig$,
})
);
}
return {};
}

View file

@ -0,0 +1,131 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ElasticsearchClient } from 'src/core/server';
import { TaggingUsageData, ByTypeTaggingUsageData } from './types';
/**
* Manual type reflection of the `tagDataAggregations` resulting payload
*/
interface AggregatedTagUsageResponseBody {
aggregations: {
by_type: {
buckets: Array<{
key: string;
doc_count: number;
nested_ref: {
tag_references: {
doc_count: number;
tag_id: {
buckets: Array<{
key: string;
doc_count: number;
}>;
};
};
};
}>;
};
};
}
export const fetchTagUsageData = async ({
esClient,
kibanaIndex,
}: {
esClient: ElasticsearchClient;
kibanaIndex: string;
}): Promise<TaggingUsageData> => {
const { body } = await esClient.search<AggregatedTagUsageResponseBody>({
index: [kibanaIndex],
ignore_unavailable: true,
filter_path: 'aggregations',
body: {
size: 0,
query: {
bool: {
must: [hasTagReferenceClause],
},
},
aggs: tagDataAggregations,
},
});
const byTypeUsages: Record<string, ByTypeTaggingUsageData> = {};
const allUsedTags = new Set<string>();
let totalTaggedObjects = 0;
const typeBuckets = body.aggregations.by_type.buckets;
typeBuckets.forEach((bucket) => {
const type = bucket.key;
const taggedDocCount = bucket.doc_count;
const usedTagIds = bucket.nested_ref.tag_references.tag_id.buckets.map(
(tagBucket) => tagBucket.key
);
totalTaggedObjects += taggedDocCount;
usedTagIds.forEach((tagId) => allUsedTags.add(tagId));
byTypeUsages[type] = {
taggedObjects: taggedDocCount,
usedTags: usedTagIds.length,
};
});
return {
usedTags: allUsedTags.size,
taggedObjects: totalTaggedObjects,
types: byTypeUsages,
};
};
const hasTagReferenceClause = {
nested: {
path: 'references',
query: {
bool: {
must: [
{
term: {
'references.type': 'tag',
},
},
],
},
},
},
};
const tagDataAggregations = {
by_type: {
terms: {
field: 'type',
},
aggs: {
nested_ref: {
nested: {
path: 'references',
},
aggs: {
tag_references: {
filter: {
term: {
'references.type': 'tag',
},
},
aggs: {
tag_id: {
terms: {
field: 'references.id',
},
},
},
},
},
},
},
},
};

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { createTagUsageCollector } from './tag_usage_collector';

View file

@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { MakeSchemaFrom } from '../../../../../src/plugins/usage_collection/server';
import { TaggingUsageData, ByTypeTaggingUsageData } from './types';
const perTypeSchema: MakeSchemaFrom<ByTypeTaggingUsageData> = {
usedTags: { type: 'integer' },
taggedObjects: { type: 'integer' },
};
export const tagUsageCollectorSchema: MakeSchemaFrom<TaggingUsageData> = {
usedTags: { type: 'integer' },
taggedObjects: { type: 'integer' },
types: {
dashboard: perTypeSchema,
visualization: perTypeSchema,
map: perTypeSchema,
},
};

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { SharedGlobalConfig } from 'src/core/server';
import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/server';
import { TaggingUsageData } from './types';
import { fetchTagUsageData } from './fetch_tag_usage_data';
import { tagUsageCollectorSchema } from './schema';
export const createTagUsageCollector = ({
usageCollection,
legacyConfig$,
}: {
usageCollection: UsageCollectionSetup;
legacyConfig$: Observable<SharedGlobalConfig>;
}) => {
return usageCollection.makeUsageCollector<TaggingUsageData>({
type: 'saved_objects_tagging',
isReady: () => true,
schema: tagUsageCollectorSchema,
fetch: async ({ esClient }) => {
const { kibana } = await legacyConfig$.pipe(take(1)).toPromise();
return fetchTagUsageData({ esClient, kibanaIndex: kibana.index });
},
});
};

View file

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/**
* @internal
*/
export interface TaggingUsageData {
usedTags: number;
taggedObjects: number;
types: Record<string, ByTypeTaggingUsageData>;
}
/**
* @internal
*/
export interface ByTypeTaggingUsageData {
usedTags: number;
taggedObjects: number;
}

View file

@ -1778,30 +1778,6 @@
}
}
},
"infraops": {
"properties": {
"last_24_hours": {
"properties": {
"hits": {
"properties": {
"infraops_hosts": {
"type": "long"
},
"infraops_docker": {
"type": "long"
},
"infraops_kubernetes": {
"type": "long"
},
"logs": {
"type": "long"
}
}
}
}
}
}
},
"ingest_manager": {
"properties": {
"fleet_enabled": {
@ -1841,6 +1817,30 @@
}
}
},
"infraops": {
"properties": {
"last_24_hours": {
"properties": {
"hits": {
"properties": {
"infraops_hosts": {
"type": "long"
},
"infraops_docker": {
"type": "long"
},
"infraops_kubernetes": {
"type": "long"
},
"logs": {
"type": "long"
}
}
}
}
}
}
},
"lens": {
"properties": {
"events_30_days": {
@ -3136,6 +3136,50 @@
}
}
},
"saved_objects_tagging": {
"properties": {
"usedTags": {
"type": "integer"
},
"taggedObjects": {
"type": "integer"
},
"types": {
"properties": {
"dashboard": {
"properties": {
"usedTags": {
"type": "integer"
},
"taggedObjects": {
"type": "integer"
}
}
},
"visualization": {
"properties": {
"usedTags": {
"type": "integer"
},
"taggedObjects": {
"type": "integer"
}
}
},
"map": {
"properties": {
"usedTags": {
"type": "integer"
},
"taggedObjects": {
"type": "integer"
}
}
}
}
}
}
},
"security_solution": {
"properties": {
"detections": {

View file

@ -40,7 +40,7 @@ export function UsageAPIProvider({ getService }: FtrProviderContext) {
async getTelemetryStats(payload: {
unencrypted?: boolean;
timestamp: number | string;
}): Promise<TelemetryCollectionManagerPlugin['getStats']> {
}): Promise<ReturnType<TelemetryCollectionManagerPlugin['getStats']>> {
const { body } = await supertest
.post('/api/telemetry/v2/clusters/_stats')
.set('kbn-xsrf', 'xxx')

View file

@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./delete'));
loadTestFile(require.resolve('./create'));
loadTestFile(require.resolve('./update'));
loadTestFile(require.resolve('./usage_collection'));
});
}

View file

@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../services';
// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const usageAPI = getService('usageAPI');
describe('saved_object_tagging usage collector data', () => {
beforeEach(async () => {
await esArchiver.load('usage_collection');
});
afterEach(async () => {
await esArchiver.unload('usage_collection');
});
/*
* Dataset description:
*
* 5 tags: tag-1 tag-2 tag-3 tag-4 ununsed-tag
* 3 dashboard:
* - dash-1: ref to tag-1 + tag-2
* - dash-2: ref to tag-2 + tag 4
* - dash-3: no ref to any tag
* 3 visualization:
* - vis-1: ref to tag-1
* - vis-2: ref to tag-1 + tag-3
* - vis-3: ref to tag-3
*/
it('collects the expected data', async () => {
const telemetryStats = (await usageAPI.getTelemetryStats({
unencrypted: true,
timestamp: Date.now(),
})) as any;
const taggingStats = telemetryStats[0].stack_stats.kibana.plugins.saved_objects_tagging;
expect(taggingStats).to.eql({
usedTags: 4,
taggedObjects: 5,
types: {
dashboard: {
taggedObjects: 2,
usedTags: 3,
},
visualization: {
taggedObjects: 3,
usedTags: 2,
},
},
});
});
});
}

View file

@ -0,0 +1,313 @@
{
"type": "doc",
"value": {
"id": "space:default",
"index": ".kibana",
"source": {
"space": {
"_reserved": true,
"description": "This is the default space",
"name": "Default Space"
},
"type": "space",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"id": "tag:tag-1",
"index": ".kibana",
"source": {
"tag": {
"name": "tag-1",
"description": "My first tag!",
"color": "#FF00FF"
},
"type": "tag",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"id": "tag:tag-2",
"index": ".kibana",
"source": {
"tag": {
"name": "tag-2",
"description": "Another awesome tag",
"color": "#FFFFFF"
},
"type": "tag",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"id": "tag:tag-3",
"index": ".kibana",
"source": {
"tag": {
"name": "tag-3",
"description": "Last but not least",
"color": "#000000"
},
"type": "tag",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"id": "tag:tag-4",
"index": ".kibana",
"source": {
"tag": {
"name": "tag-4",
"description": "Last",
"color": "#000000"
},
"type": "tag",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"id": "tag:unused-tag",
"index": ".kibana",
"source": {
"tag": {
"name": "unused-tag",
"description": "This tag is unused and should only appear in totalTags",
"color": "#123456"
},
"type": "tag",
"updated_at": "2017-09-21T18:49:16.270Z"
},
"type": "doc"
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "visualization:ref-to-tag-1",
"source": {
"type": "visualization",
"updated_at": "2017-09-21T18:51:23.794Z",
"visualization": {
"title": "Vis with ref to tag-1",
"visState": "{}",
"uiStateJSON": "{\"spy\":{\"mode\":{\"name\":null,\"fill\":false}}}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"references": [
{
"type": "tag",
"id": "tag-1",
"name": "tag-1"
}
]
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "visualization:ref-to-tag-1-and-tag-3",
"source": {
"type": "visualization",
"updated_at": "2017-09-21T18:51:23.794Z",
"visualization": {
"title": "Vis with ref to tag-1 and tag-2",
"visState": "{}",
"uiStateJSON": "{\"spy\":{\"mode\":{\"name\":null,\"fill\":false}}}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"references": [
{
"type": "tag",
"id": "tag-1",
"name": "tag-1"
},
{
"type": "tag",
"id": "tag-3",
"name": "tag-3"
}
]
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "visualization:ref-to-tag-3",
"source": {
"type": "visualization",
"updated_at": "2017-09-21T18:51:23.794Z",
"visualization": {
"title": "Vis with ref to tag-2",
"visState": "{}",
"uiStateJSON": "{\"spy\":{\"mode\":{\"name\":null,\"fill\":false}}}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"references": [
{
"type": "tag",
"id": "tag-3",
"name": "tag-3"
}
]
}
}
}
{
"type": "doc",
"value": {
"id": "dashboard:ref-to-tag-1-and-tag-2",
"index": ".kibana",
"source": {
"dashboard": {
"title": "dashboard 1 (tag-2)",
"description": "",
"hits": 0,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}"
},
"optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}",
"panelsJSON": "[]",
"timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400",
"timeRestore": true,
"timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400",
"version": 1
},
"migrationVersion": {
"dashboard": "7.3.0"
},
"references": [
{
"id": "tag-1",
"name": "tag-1-ref",
"type": "tag"
},
{
"id": "tag-2",
"name": "tag-2-ref",
"type": "tag"
}
],
"type": "dashboard",
"updated_at": "2018-04-11T21:57:52.253Z"
}
}
}
{
"type": "doc",
"value": {
"id": "dashboard:ref-to-tag-2-and-tag-4",
"index": ".kibana",
"source": {
"dashboard": {
"title": "dashboard 2 (tag-2 and tag-4)",
"description": "",
"hits": 0,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}"
},
"optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}",
"panelsJSON": "[]",
"timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400",
"timeRestore": true,
"timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400",
"version": 1
},
"migrationVersion": {
"dashboard": "7.3.0"
},
"references": [
{
"id": "tag-2",
"name": "tag-2-ref",
"type": "tag"
},
{
"id": "tag-4",
"name": "tag-4-ref",
"type": "tag"
}
],
"type": "dashboard",
"updated_at": "2018-04-11T21:57:52.253Z"
}
}
}
{
"type": "doc",
"value": {
"id": "dashboard:no-tag-reference",
"index": ".kibana",
"source": {
"dashboard": {
"title": "dashboard 2 (tag-2 and tag-4)",
"description": "",
"hits": 0,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}"
},
"optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}",
"panelsJSON": "[]",
"timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400",
"timeRestore": true,
"timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400",
"version": 1
},
"migrationVersion": {
"dashboard": "7.3.0"
},
"references": [
],
"type": "dashboard",
"updated_at": "2018-04-11T21:57:52.253Z"
}
}
}

View file

@ -0,0 +1,266 @@
{
"type": "index",
"value": {
"aliases": {},
"index": ".kibana",
"mappings": {
"dynamic": "strict",
"properties": {
"config": {
"dynamic": "true",
"properties": {
"buildNum": {
"type": "keyword"
},
"defaultIndex": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
}
}
},
"migrationVersion": {
"dynamic": "true",
"properties": {
"dashboard": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"index-pattern": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"search": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"visualization": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"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"
}
}
},
"index-pattern": {
"properties": {
"fieldFormatMap": {
"type": "text"
},
"fields": {
"type": "text"
},
"intervalName": {
"type": "keyword"
},
"notExpandable": {
"type": "boolean"
},
"sourceFilters": {
"type": "text"
},
"timeFieldName": {
"type": "keyword"
},
"title": {
"type": "text"
}
}
},
"namespace": {
"type": "keyword"
},
"namespaces": {
"type": "keyword"
},
"originId": {
"type": "keyword"
},
"server": {
"properties": {
"uuid": {
"type": "keyword"
}
}
},
"tag": {
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text"
},
"color": {
"type": "text"
}
}
},
"space": {
"properties": {
"_reserved": {
"type": "boolean"
},
"color": {
"type": "keyword"
},
"description": {
"type": "text"
},
"disabledFeatures": {
"type": "keyword"
},
"initials": {
"type": "keyword"
},
"name": {
"fields": {
"keyword": {
"ignore_above": 2048,
"type": "keyword"
}
},
"type": "text"
}
}
},
"references": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"type": {
"type": "keyword"
}
},
"type": "nested"
},
"type": {
"type": "keyword"
},
"updated_at": {
"type": "date"
},
"visualization": {
"properties": {
"description": {
"type": "text"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"savedSearchId": {
"type": "keyword"
},
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
},
"visState": {
"type": "text"
}
}
}
}
},
"settings": {
"index": {
"auto_expand_replicas": "0-1",
"number_of_replicas": "0",
"number_of_shards": "1"
}
}
}
}