diff --git a/x-pack/plugins/saved_objects_tagging/README.md b/x-pack/plugins/saved_objects_tagging/README.md index 5e4281a8c4e7d..0da16746f6494 100644 --- a/x-pack/plugins/saved_objects_tagging/README.md +++ b/x-pack/plugins/saved_objects_tagging/README.md @@ -1,3 +1,53 @@ # SavedObjectsTagging -Add tagging capability to saved objects \ No newline at end of file +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 = { + // ... + types: { + dashboard: perTypeSchema, + visualization: perTypeSchema, + // <-- add your type here + }, +}; +``` diff --git a/x-pack/plugins/saved_objects_tagging/kibana.json b/x-pack/plugins/saved_objects_tagging/kibana.json index 89c5e7a134339..134e48a671f28 100644 --- a/x-pack/plugins/saved_objects_tagging/kibana.json +++ b/x-pack/plugins/saved_objects_tagging/kibana.json @@ -6,5 +6,6 @@ "ui": true, "configPath": ["xpack", "saved_object_tagging"], "requiredPlugins": ["features", "management", "savedObjectsTaggingOss"], - "requiredBundles": ["kibanaReact"] + "requiredBundles": ["kibanaReact"], + "optionalPlugins": ["usageCollection"] } diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.test.mocks.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.test.mocks.ts index 1223b1ec20389..f0c3285667817 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.test.mocks.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.test.mocks.ts @@ -8,3 +8,8 @@ export const registerRoutesMock = jest.fn(); jest.doMock('./routes', () => ({ registerRoutes: registerRoutesMock, })); + +export const createTagUsageCollectorMock = jest.fn(); +jest.doMock('./usage', () => ({ + createTagUsageCollector: createTagUsageCollectorMock, +})); diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts index 1a3e4071f5e09..0730b29cde4a8 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts @@ -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; + let usageCollectionSetup: ReturnType; 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); + }); }); }); diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.ts index 8347fb1f8ef20..6eb8080793d0e 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.ts @@ -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; - 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 {}; } diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts b/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts new file mode 100644 index 0000000000000..692088e66003e --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts @@ -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 => { + const { body } = await esClient.search({ + index: [kibanaIndex], + ignore_unavailable: true, + filter_path: 'aggregations', + body: { + size: 0, + query: { + bool: { + must: [hasTagReferenceClause], + }, + }, + aggs: tagDataAggregations, + }, + }); + + const byTypeUsages: Record = {}; + const allUsedTags = new Set(); + 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', + }, + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/index.ts b/x-pack/plugins/saved_objects_tagging/server/usage/index.ts new file mode 100644 index 0000000000000..023295ab19aef --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/usage/index.ts @@ -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'; diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/schema.ts b/x-pack/plugins/saved_objects_tagging/server/usage/schema.ts new file mode 100644 index 0000000000000..8132c60daf964 --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/usage/schema.ts @@ -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 = { + usedTags: { type: 'integer' }, + taggedObjects: { type: 'integer' }, +}; + +export const tagUsageCollectorSchema: MakeSchemaFrom = { + usedTags: { type: 'integer' }, + taggedObjects: { type: 'integer' }, + + types: { + dashboard: perTypeSchema, + visualization: perTypeSchema, + map: perTypeSchema, + }, +}; diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts b/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts new file mode 100644 index 0000000000000..a38dc46193332 --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts @@ -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; +}) => { + return usageCollection.makeUsageCollector({ + 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 }); + }, + }); +}; diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/types.ts b/x-pack/plugins/saved_objects_tagging/server/usage/types.ts new file mode 100644 index 0000000000000..3f6ebb752de13 --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/usage/types.ts @@ -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; +} + +/** + * @internal + */ +export interface ByTypeTaggingUsageData { + usedTags: number; + taggedObjects: number; +} diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index c965623ebfc17..8936cdafa3827 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -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": { diff --git a/x-pack/test/api_integration/services/usage_api.ts b/x-pack/test/api_integration/services/usage_api.ts index c56de5127f743..b4adc6c61b664 100644 --- a/x-pack/test/api_integration/services/usage_api.ts +++ b/x-pack/test/api_integration/services/usage_api.ts @@ -40,7 +40,7 @@ export function UsageAPIProvider({ getService }: FtrProviderContext) { async getTelemetryStats(payload: { unencrypted?: boolean; timestamp: number | string; - }): Promise { + }): Promise> { const { body } = await supertest .post('/api/telemetry/v2/clusters/_stats') .set('kbn-xsrf', 'xxx') diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts index d78513ca06206..6bacd5a625a15 100644 --- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts +++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts @@ -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')); }); } diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/usage_collection.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/usage_collection.ts new file mode 100644 index 0000000000000..8804c2cd2ad59 --- /dev/null +++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/usage_collection.ts @@ -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, + }, + }, + }); + }); + }); +} diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/data.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/data.json new file mode 100644 index 0000000000000..a9535ae9e40b2 --- /dev/null +++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/data.json @@ -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" + } + } +} + diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json new file mode 100644 index 0000000000000..9cf628bef4767 --- /dev/null +++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json @@ -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" + } + } + } +}