diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index 2c7bb980fb721..070d45787fff4 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -6738,6 +6738,13 @@ "description": "A short description.", "type": "string" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeRestore": { "default": false, "description": "Whether to restore time upon viewing this dashboard", @@ -7384,6 +7391,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -8038,6 +8052,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -8576,6 +8597,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -9202,6 +9230,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -9734,6 +9769,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index a541fee0dc9f9..acd91ee213f9d 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -6738,6 +6738,13 @@ "description": "A short description.", "type": "string" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeRestore": { "default": false, "description": "Whether to restore time upon viewing this dashboard", @@ -7384,6 +7391,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -8038,6 +8052,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -8576,6 +8597,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -9202,6 +9230,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" @@ -9734,6 +9769,13 @@ ], "type": "object" }, + "tags": { + "items": { + "description": "An array of tags applied to this dashboard", + "type": "string" + }, + "type": "array" + }, "timeFrom": { "description": "An ISO string indicating when to restore time from", "type": "string" diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 18b3a66628b56..715c77123bcd1 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -6168,6 +6168,11 @@ paths: default: '' description: A short description. type: string + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeRestore: default: false description: Whether to restore time upon viewing this dashboard @@ -6628,6 +6633,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -7093,6 +7103,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -7481,6 +7496,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -7926,6 +7946,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -8310,6 +8335,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 2b750d305ce13..e42718f6b0446 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -7703,6 +7703,11 @@ paths: default: '' description: A short description. type: string + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeRestore: default: false description: Whether to restore time upon viewing this dashboard @@ -8163,6 +8168,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -8628,6 +8638,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -9016,6 +9031,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -9461,6 +9481,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string @@ -9845,6 +9870,11 @@ paths: required: - pause - value + tags: + items: + description: An array of tags applied to this dashboard + type: string + type: array timeFrom: description: An ISO string indicating when to restore time from type: string diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_serialized_state.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_serialized_state.ts index bb7699b078d6d..854e3fb6717e1 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_serialized_state.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_serialized_state.ts @@ -138,6 +138,8 @@ export const getSerializedState = ({ { embeddablePersistableStateService: embeddableService } ); + // TODO Provide tags as an array of tag names in the attribute. In that case, tag references + // will be extracted by the server. const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi(); const references = savedObjectsTaggingApi?.ui.updateTagsReferences ? savedObjectsTaggingApi?.ui.updateTagsReferences(dashboardReferences, tags) diff --git a/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts b/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts index 2c1875979ad2c..9ba230ffd5bc7 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts @@ -14,10 +14,13 @@ import type { Logger } from '@kbn/logging'; import { CreateResult, DeleteResult, SearchQuery } from '@kbn/content-management-plugin/common'; import { StorageContext } from '@kbn/content-management-plugin/server'; +import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server'; +import type { SavedObjectReference } from '@kbn/core/server'; +import type { ITagsClient, Tag } from '@kbn/saved-objects-tagging-oss-plugin/common'; import { DASHBOARD_SAVED_OBJECT_TYPE } from '../dashboard_saved_object'; import { cmServicesDefinition } from './cm_services'; import { DashboardSavedObjectAttributes } from '../dashboard_saved_object'; -import { itemAttrsToSavedObjectAttrs, savedObjectToItem } from './latest'; +import { itemAttrsToSavedObjectWithTags, savedObjectToItem } from './latest'; import type { DashboardAttributes, DashboardItem, @@ -28,8 +31,13 @@ import type { DashboardUpdateOptions, DashboardUpdateOut, DashboardSearchOptions, + ReplaceTagReferencesByNameParams, } from './latest'; +const getRandomColor = (): string => { + return '#' + String(Math.floor(Math.random() * 16777215).toString(16)).padStart(6, '0'); +}; + const searchArgsToSOFindOptions = ( query: SearchQuery, options: DashboardSearchOptions @@ -60,21 +68,117 @@ export class DashboardStorage { constructor({ logger, throwOnResultValidationError, + savedObjectsTagging, }: { logger: Logger; throwOnResultValidationError: boolean; + savedObjectsTagging?: SavedObjectTaggingStart; }) { + this.savedObjectsTagging = savedObjectsTagging; this.logger = logger; this.throwOnResultValidationError = throwOnResultValidationError ?? false; } private logger: Logger; + private savedObjectsTagging?: SavedObjectTaggingStart; private throwOnResultValidationError: boolean; + private getTagNamesFromReferences(references: SavedObjectReference[], allTags: Tag[]) { + return Array.from( + new Set( + this.savedObjectsTagging + ? this.savedObjectsTagging + .getTagsFromReferences(references, allTags) + .tags.map((tag) => tag.name) + : [] + ) + ); + } + + private getUniqueTagNames( + references: SavedObjectReference[], + newTagNames: string[], + allTags: Tag[] + ) { + const referenceTagNames = this.getTagNamesFromReferences(references, allTags); + return new Set([...referenceTagNames, ...newTagNames]); + } + + private async replaceTagReferencesByName( + references: SavedObjectReference[], + newTagNames: string[], + allTags: Tag[], + tagsClient?: ITagsClient + ) { + const combinedTagNames = this.getUniqueTagNames(references, newTagNames, allTags); + const newTagIds = await this.convertTagNamesToIds(combinedTagNames, allTags, tagsClient); + return this.savedObjectsTagging?.replaceTagReferences(references, newTagIds) ?? references; + } + + private async convertTagNamesToIds( + tagNames: Set, + allTags: Tag[], + tagsClient?: ITagsClient + ): Promise { + const combinedTagNames = await this.createTagsIfNeeded(tagNames, allTags, tagsClient); + + return Array.from(combinedTagNames).flatMap( + (tagName) => this.savedObjectsTagging?.convertTagNameToId(tagName, allTags) ?? [] + ); + } + + private async createTagsIfNeeded( + tagNames: Set, + allTags: Tag[], + tagsClient?: ITagsClient + ) { + const tagsToCreate = Array.from(tagNames).filter( + (tagName) => !allTags.some((tag) => tag.name === tagName) + ); + const tagCreationResults = await Promise.allSettled( + tagsToCreate.flatMap( + (tagName) => + tagsClient?.create({ + name: tagName, + description: '', + color: getRandomColor(), + }) ?? [] + ) + ); + + for (const result of tagCreationResults) { + if (result.status === 'rejected') { + this.logger.error(`Error creating tag: ${result.reason}`); + } else { + this.logger.info(`Tag created: ${result.value.name}`); + } + } + + const createdTags = tagCreationResults + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + + // Remove tags that were not created + const invalidTagNames = tagsToCreate.filter( + (tagName) => !createdTags.some((tag) => tag.name === tagName) + ); + invalidTagNames.forEach((tagName) => tagNames.delete(tagName)); + + // Add newly created tags to allTags + allTags.push(...createdTags); + + const combinedTagNames = new Set([ + ...tagNames, + ...createdTags.map((createdTag) => createdTag.name), + ]); + return combinedTagNames; + } + async get(ctx: StorageContext, id: string): Promise { const transforms = ctx.utils.getTransforms(cmServicesDefinition); const soClient = await savedObjectClientFromRequest(ctx); - + const tagsClient = this.savedObjectsTagging?.createTagClient({ client: soClient }); + const allTags = (await tagsClient?.getAll()) ?? []; // Save data in DB const { saved_object: savedObject, @@ -83,7 +187,10 @@ export class DashboardStorage { outcome, } = await soClient.resolve(DASHBOARD_SAVED_OBJECT_TYPE, id); - const { item, error: itemError } = savedObjectToItem(savedObject, false); + const { item, error: itemError } = savedObjectToItem(savedObject, false, { + getTagNamesFromReferences: (references: SavedObjectReference[]) => + this.getTagNamesFromReferences(references, allTags), + }); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -128,6 +235,8 @@ export class DashboardStorage { ): Promise { const transforms = ctx.utils.getTransforms(cmServicesDefinition); const soClient = await savedObjectClientFromRequest(ctx); + const tagsClient = this.savedObjectsTagging?.createTagClient({ client: soClient }); + const allTags = tagsClient ? await tagsClient?.getAll() : []; // Validate input (data & options) & UP transform them to the latest version const { value: dataToLatest, error: dataError } = transforms.create.in.data.up< @@ -146,8 +255,16 @@ export class DashboardStorage { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); } - const { attributes: soAttributes, error: attributesError } = - itemAttrsToSavedObjectAttrs(dataToLatest); + const { + attributes: soAttributes, + references: soReferences, + error: attributesError, + } = await itemAttrsToSavedObjectWithTags({ + attributes: dataToLatest, + replaceTagReferencesByName: ({ references, newTagNames }: ReplaceTagReferencesByNameParams) => + this.replaceTagReferencesByName(references, newTagNames, allTags, tagsClient), + incomingReferences: options.references, + }); if (attributesError) { throw Boom.badRequest(`Invalid data. ${attributesError.message}`); } @@ -156,10 +273,13 @@ export class DashboardStorage { const savedObject = await soClient.create( DASHBOARD_SAVED_OBJECT_TYPE, soAttributes, - optionsToLatest + { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(savedObject, false); + const { item, error: itemError } = savedObjectToItem(savedObject, false, { + getTagNamesFromReferences: (references: SavedObjectReference[]) => + this.getTagNamesFromReferences(references, allTags), + }); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -197,6 +317,8 @@ export class DashboardStorage { ): Promise { const transforms = ctx.utils.getTransforms(cmServicesDefinition); const soClient = await savedObjectClientFromRequest(ctx); + const tagsClient = this.savedObjectsTagging?.createTagClient({ client: soClient }); + const allTags = (await tagsClient?.getAll()) ?? []; // Validate input (data & options) & UP transform them to the latest version const { value: dataToLatest, error: dataError } = transforms.update.in.data.up< @@ -215,8 +337,16 @@ export class DashboardStorage { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); } - const { attributes: soAttributes, error: attributesError } = - itemAttrsToSavedObjectAttrs(dataToLatest); + const { + attributes: soAttributes, + references: soReferences, + error: attributesError, + } = await itemAttrsToSavedObjectWithTags({ + attributes: dataToLatest, + replaceTagReferencesByName: ({ references, newTagNames }: ReplaceTagReferencesByNameParams) => + this.replaceTagReferencesByName(references, newTagNames, allTags, tagsClient), + incomingReferences: options.references, + }); if (attributesError) { throw Boom.badRequest(`Invalid data. ${attributesError.message}`); } @@ -226,10 +356,13 @@ export class DashboardStorage { DASHBOARD_SAVED_OBJECT_TYPE, id, soAttributes, - optionsToLatest + { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(partialSavedObject, true); + const { item, error: itemError } = savedObjectToItem(partialSavedObject, true, { + getTagNamesFromReferences: (references: SavedObjectReference[]) => + this.getTagNamesFromReferences(references, allTags), + }); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -278,6 +411,8 @@ export class DashboardStorage { ): Promise { const transforms = ctx.utils.getTransforms(cmServicesDefinition); const soClient = await savedObjectClientFromRequest(ctx); + const tagsClient = this.savedObjectsTagging?.createTagClient({ client: soClient }); + const allTags = (await tagsClient?.getAll()) ?? []; // Validate and UP transform the options const { value: optionsToLatest, error: optionsError } = transforms.search.in.options.up< @@ -291,16 +426,20 @@ export class DashboardStorage { const soQuery = searchArgsToSOFindOptions(query, optionsToLatest); // Execute the query in the DB const soResponse = await soClient.find(soQuery); - const hits = soResponse.saved_objects - .map((so) => { - const { item } = savedObjectToItem(so, false, { - allowedAttributes: soQuery.fields, - allowedReferences: optionsToLatest?.includeReferences, - }); - return item; - }) - // Ignore any saved objects that failed to convert to items. - .filter((item) => item !== null); + const hits = await Promise.all( + soResponse.saved_objects + .map(async (so) => { + const { item } = savedObjectToItem(so, false, { + allowedAttributes: soQuery.fields, + allowedReferences: optionsToLatest?.includeReferences, + getTagNamesFromReferences: (references: SavedObjectReference[]) => + this.getTagNamesFromReferences(references, allTags), + }); + return item; + }) + // Ignore any saved objects that failed to convert to items. + .filter((item) => item !== null) + ); const response = { hits, pagination: { diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/cm_services.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/cm_services.ts index 0cee0bb23f450..9ccf05a510b88 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/cm_services.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/cm_services.ts @@ -84,11 +84,4 @@ export const serviceDefinition: ServicesDefinition = { }, }, }, - mSearch: { - out: { - result: { - schema: dashboardSavedObjectSchema, - }, - }, - }, }; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v2/cm_services.ts b/src/platform/plugins/shared/dashboard/server/content_management/v2/cm_services.ts index 3b560b8416731..28da0236e5567 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v2/cm_services.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v2/cm_services.ts @@ -51,18 +51,11 @@ export const serviceDefinition: ServicesDefinition = { ...serviceDefinitionV1.update?.in, data: { schema: dashboardAttributesSchema, - up: (data: DashboardCrudTypes['UpdateIn']['data']) => attributesTov3(data), + up: (data: DashboardCrudTypes['UpdateIn']['data']) => attributesTov3(data, [], () => []), }, }, }, search: { in: serviceDefinitionV1.search?.in, }, - mSearch: { - out: { - result: { - schema: dashboardSavedObjectSchema, - }, - }, - }, }; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/cm_services.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/cm_services.ts index b2d0c55e1f9d3..36aa421ade3a0 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/cm_services.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/cm_services.ts @@ -333,6 +333,11 @@ export const searchResultsAttributesSchema = schema.object({ defaultValue: false, meta: { description: 'Whether to restore time upon viewing this dashboard' }, }), + tags: schema.maybe( + schema.arrayOf( + schema.string({ meta: { description: 'An array of tags applied to this dashboard' } }) + ) + ), }); export const dashboardAttributesSchema = searchResultsAttributesSchema.extends({ @@ -553,11 +558,4 @@ export const serviceDefinition: ServicesDefinition = { }, }, }, - mSearch: { - out: { - result: { - schema: dashboardItemSchema, - }, - }, - }, }; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts index 7be9313c3210e..5c26a0d7fed99 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts @@ -25,6 +25,7 @@ export type { DashboardUpdateOut, DashboardUpdateOptions, DashboardOptions, + ReplaceTagReferencesByNameParams, } from './types'; export { serviceDefinition, @@ -37,6 +38,7 @@ export { } from './cm_services'; export { dashboardAttributesOut, - itemAttrsToSavedObjectAttrs, + itemAttrsToSavedObject, + itemAttrsToSavedObjectWithTags, savedObjectToItem, } from './transform_utils'; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.test.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.test.ts index 94b5f247dd832..f20935cbbe107 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.test.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.test.ts @@ -8,17 +8,6 @@ */ import type { SavedObject } from '@kbn/core-saved-objects-api-server'; -import type { - DashboardSavedObjectAttributes, - SavedDashboardPanel, -} from '../../dashboard_saved_object'; -import type { DashboardAttributes, DashboardItem } from './types'; -import { - dashboardAttributesOut, - getResultV3ToV2, - itemAttrsToSavedObjectAttrs, - savedObjectToItem, -} from './transform_utils'; import { DEFAULT_AUTO_APPLY_SELECTIONS, DEFAULT_CONTROL_CHAINING, @@ -30,6 +19,19 @@ import { ControlGroupChainingSystem, ControlWidth, } from '@kbn/controls-plugin/common'; + +import type { + DashboardSavedObjectAttributes, + SavedDashboardPanel, +} from '../../dashboard_saved_object'; +import type { DashboardAttributes, DashboardItem } from './types'; + +import { + dashboardAttributesOut, + getResultV3ToV2, + itemAttrsToSavedObject, + savedObjectToItem, +} from './transform_utils'; import { DEFAULT_DASHBOARD_OPTIONS } from '../../../common/content_management'; describe('dashboardAttributesOut', () => { @@ -205,8 +207,8 @@ describe('dashboardAttributesOut', () => { }); }); -describe('itemAttrsToSavedObjectAttrs', () => { - it('should transform item attributes to saved object attributes correctly', () => { +describe('itemAttrsToSavedObject', () => { + it('should transform item attributes to saved object correctly', () => { const input: DashboardAttributes = { controlGroupInput: { chainingSystem: 'NONE', @@ -250,6 +252,7 @@ describe('itemAttrsToSavedObjectAttrs', () => { version: '2', }, ], + tags: [], timeRestore: true, title: 'title', refreshInterval: { pause: true, value: 1000 }, @@ -257,7 +260,7 @@ describe('itemAttrsToSavedObjectAttrs', () => { timeTo: 'now', }; - const output = itemAttrsToSavedObjectAttrs(input); + const output = itemAttrsToSavedObject({ attributes: input }); expect(output).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -284,6 +287,7 @@ describe('itemAttrsToSavedObjectAttrs', () => { "title": "title", }, "error": null, + "references": Array [], } `); }); @@ -298,7 +302,7 @@ describe('itemAttrsToSavedObjectAttrs', () => { kibanaSavedObjectMeta: {}, }; - const output = itemAttrsToSavedObjectAttrs(input); + const output = itemAttrsToSavedObject({ attributes: input }); expect(output).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -312,6 +316,7 @@ describe('itemAttrsToSavedObjectAttrs', () => { "title": "title", }, "error": null, + "references": Array [], } `); }); @@ -333,6 +338,13 @@ describe('savedObjectToItem', () => { attributes, }; }; + + const getTagNamesFromReferences = jest.fn(); + + beforeEach(() => { + jest.resetAllMocks(); + }); + it('should convert saved object to item with all attributes', () => { const input = getSavedObjectForAttributes({ title: 'title', @@ -396,6 +408,51 @@ describe('savedObjectToItem', () => { }); }); + it('should pass references to getTagNamesFromReferences', () => { + getTagNamesFromReferences.mockReturnValue(['tag1', 'tag2']); + const input = { + ...getSavedObjectForAttributes({ + title: 'dashboard with tags', + description: 'I have some tags!', + timeRestore: true, + kibanaSavedObjectMeta: {}, + panelsJSON: JSON.stringify([]), + }), + references: [ + { + type: 'tag', + id: 'tag1', + name: 'tag-ref-tag1', + }, + { + type: 'tag', + id: 'tag2', + name: 'tag-ref-tag2', + }, + { + type: 'index-pattern', + id: 'index-pattern1', + name: 'index-pattern-ref-index-pattern1', + }, + ], + }; + const { item, error } = savedObjectToItem(input, false, { getTagNamesFromReferences }); + expect(getTagNamesFromReferences).toHaveBeenCalledWith(input.references); + expect(error).toBeNull(); + expect(item).toEqual({ + ...commonSavedObject, + references: [...input.references], + attributes: { + title: 'dashboard with tags', + description: 'I have some tags!', + panels: [], + timeRestore: true, + kibanaSavedObjectMeta: {}, + tags: ['tag1', 'tag2'], + }, + }); + }); + it('should handle missing optional attributes', () => { const input = getSavedObjectForAttributes({ title: 'title', diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts index 0ea8b883305c9..5a44857657275 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts @@ -14,7 +14,9 @@ import type { DashboardAttributes, DashboardGetOut, DashboardItem, - ItemAttrsToSavedObjectAttrsReturn, + ItemAttrsToSavedObjectParams, + ItemAttrsToSavedObjectReturn, + ItemAttrsToSavedObjectWithTagsParams, PartialDashboardItem, SavedObjectToItemReturn, } from './types'; @@ -34,7 +36,9 @@ import { } from './transforms'; export function dashboardAttributesOut( - attributes: DashboardSavedObjectAttributes | Partial + attributes: DashboardSavedObjectAttributes | Partial, + references?: SavedObjectReference[], + getTagNamesFromReferences?: (references: SavedObjectReference[]) => string[] ): DashboardAttributes | Partial { const { controlGroupInput, @@ -49,6 +53,13 @@ export function dashboardAttributesOut( title, version, } = attributes; + + // Inject any tag names from references into the attributes + let tags: string[] | undefined; + if (getTagNamesFromReferences && references && references.length) { + tags = getTagNamesFromReferences(references); + } + // try to maintain a consistent (alphabetical) order of keys return { ...(controlGroupInput && { controlGroupInput: transformControlGroupOut(controlGroupInput) }), @@ -61,6 +72,7 @@ export function dashboardAttributesOut( ...(refreshInterval && { refreshInterval: { pause: refreshInterval.pause, value: refreshInterval.value }, }), + ...(tags && tags.length && { tags }), ...(timeFrom && { timeFrom }), timeRestore: timeRestore ?? false, ...(timeTo && { timeTo }), @@ -112,11 +124,12 @@ export const getResultV3ToV2 = (result: DashboardGetOut): DashboardCrudTypesV2[' }; }; -export const itemAttrsToSavedObjectAttrs = ( - attributes: DashboardAttributes -): ItemAttrsToSavedObjectAttrsReturn => { +export const itemAttrsToSavedObject = ({ + attributes, + incomingReferences = [], +}: ItemAttrsToSavedObjectParams): ItemAttrsToSavedObjectReturn => { try { - const { controlGroupInput, kibanaSavedObjectMeta, options, panels, ...rest } = attributes; + const { controlGroupInput, kibanaSavedObjectMeta, options, panels, tags, ...rest } = attributes; const soAttributes = { ...rest, ...(controlGroupInput && { @@ -132,17 +145,34 @@ export const itemAttrsToSavedObjectAttrs = ( kibanaSavedObjectMeta: transformSearchSourceIn(kibanaSavedObjectMeta), }), }; - return { attributes: soAttributes, error: null }; + return { attributes: soAttributes, references: incomingReferences, error: null }; } catch (e) { - return { attributes: null, error: e }; + return { attributes: null, references: null, error: e }; } }; +export const itemAttrsToSavedObjectWithTags = async ({ + attributes, + replaceTagReferencesByName, + incomingReferences = [], +}: ItemAttrsToSavedObjectWithTagsParams): Promise => { + const { tags, ...restAttributes } = attributes; + // Tags can be specified as an attribute or in the incomingReferences. + const soReferences = + replaceTagReferencesByName && tags && tags.length + ? await replaceTagReferencesByName({ references: incomingReferences, newTagNames: tags }) + : incomingReferences; + return itemAttrsToSavedObject({ + attributes: restAttributes, + incomingReferences: soReferences, + }); +}; + type PartialSavedObject = Omit>, 'references'> & { references: SavedObjectReference[] | undefined; }; -export interface SavedObjectToItemOptions { +interface SavedObjectToItemOptions { /** * attributes to include in the output item */ @@ -151,6 +181,7 @@ export interface SavedObjectToItemOptions { * references to include in the output item */ allowedReferences?: string[]; + getTagNamesFromReferences?: (references: SavedObjectReference[]) => string[]; } export function savedObjectToItem( @@ -170,7 +201,7 @@ export function savedObjectToItem( | SavedObject | PartialSavedObject, partial: boolean /* partial arg is used to enforce the correct savedObject type */, - { allowedAttributes, allowedReferences }: SavedObjectToItemOptions = {} + { allowedAttributes, allowedReferences, getTagNamesFromReferences }: SavedObjectToItemOptions = {} ): SavedObjectToItemReturn { const { id, @@ -189,8 +220,11 @@ export function savedObjectToItem( try { const attributesOut = allowedAttributes - ? pick(dashboardAttributesOut(attributes), allowedAttributes) - : dashboardAttributesOut(attributes); + ? pick( + dashboardAttributesOut(attributes, references, getTagNamesFromReferences), + allowedAttributes + ) + : dashboardAttributesOut(attributes, references, getTagNamesFromReferences); // if includeReferences is provided, only include references of those types const referencesOut = allowedReferences diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts index 0c7144569aba2..0c55985162e3d 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts @@ -81,12 +81,30 @@ export type SavedObjectToItemReturn = error: Error; }; -export type ItemAttrsToSavedObjectAttrsReturn = +export interface ItemAttrsToSavedObjectParams { + attributes: DashboardAttributes; + incomingReferences?: SavedObjectReference[]; +} + +export type ItemAttrsToSavedObjectReturn = | { attributes: DashboardSavedObjectAttributes; + references: SavedObjectReference[]; error: null; } | { attributes: null; + references: null; error: Error; }; + +export interface ItemAttrsToSavedObjectWithTagsParams extends ItemAttrsToSavedObjectParams { + replaceTagReferencesByName?: ( + params: ReplaceTagReferencesByNameParams + ) => Promise; +} + +export interface ReplaceTagReferencesByNameParams { + references: SavedObjectReference[]; + newTagNames: string[]; +} diff --git a/src/platform/plugins/shared/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts b/src/platform/plugins/shared/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts index 091ef21322671..4654e763a7460 100644 --- a/src/platform/plugins/shared/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts +++ b/src/platform/plugins/shared/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts @@ -12,7 +12,7 @@ import { SavedObject, SavedObjectMigrationFn } from '@kbn/core/server'; import { extractReferences, injectReferences } from '../../../common'; import type { DashboardSavedObjectTypeMigrationsDeps } from './dashboard_saved_object_migrations'; import type { DashboardSavedObjectAttributes } from '../schema'; -import { itemAttrsToSavedObjectAttrs, savedObjectToItem } from '../../content_management/latest'; +import { itemAttrsToSavedObject, savedObjectToItem } from '../../content_management/latest'; /** * In 7.8.0 we introduced dashboard drilldowns which are stored inside dashboard saved object as part of embeddable state @@ -60,7 +60,9 @@ export function createExtractPanelReferencesMigration( { embeddablePersistableStateService: deps.embeddable } ); - const { attributes, error: attributesError } = itemAttrsToSavedObjectAttrs(extractedAttributes); + const { attributes, error: attributesError } = itemAttrsToSavedObject({ + attributes: extractedAttributes, + }); if (attributesError) throw attributesError; return { diff --git a/src/platform/plugins/shared/dashboard/server/plugin.ts b/src/platform/plugins/shared/dashboard/server/plugin.ts index 37183897c0a1c..d8704dc740840 100644 --- a/src/platform/plugins/shared/dashboard/server/plugin.ts +++ b/src/platform/plugins/shared/dashboard/server/plugin.ts @@ -17,6 +17,7 @@ import { ContentManagementServerSetup } from '@kbn/content-management-plugin/ser import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server'; import { registerContentInsights } from '@kbn/content-management-content-insights-server'; +import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server'; import { initializeDashboardTelemetryTask, scheduleDashboardTelemetry, @@ -42,6 +43,7 @@ interface SetupDeps { interface StartDeps { taskManager: TaskManagerStartContract; usageCollection?: UsageCollectionStart; + savedObjectsTagging?: SavedObjectTaggingStart; } export class DashboardPlugin @@ -65,17 +67,20 @@ export class DashboardPlugin }) ); - const { contentClient } = plugins.contentManagement.register({ - id: CONTENT_ID, - storage: new DashboardStorage({ - throwOnResultValidationError: this.initializerContext.env.mode.dev, - logger: this.logger.get('storage'), - }), - version: { - latest: LATEST_VERSION, - }, + void core.getStartServices().then(([_, { savedObjectsTagging }]) => { + const { contentClient } = plugins.contentManagement.register({ + id: CONTENT_ID, + storage: new DashboardStorage({ + throwOnResultValidationError: this.initializerContext.env.mode.dev, + logger: this.logger.get('storage'), + savedObjectsTagging, + }), + version: { + latest: LATEST_VERSION, + }, + }); + this.contentClient = contentClient; }); - this.contentClient = contentClient; plugins.contentManagement.favorites.registerFavoriteType('dashboard'); diff --git a/src/platform/plugins/shared/dashboard/tsconfig.json b/src/platform/plugins/shared/dashboard/tsconfig.json index 7f2c5c3a85b61..f97d1d3aeeb0d 100644 --- a/src/platform/plugins/shared/dashboard/tsconfig.json +++ b/src/platform/plugins/shared/dashboard/tsconfig.json @@ -83,7 +83,8 @@ "@kbn/core-rendering-browser", "@kbn/grid-layout", "@kbn/ui-actions-browser", - "@kbn/esql-types" + "@kbn/esql-types", + "@kbn/saved-objects-tagging-plugin" ], "exclude": ["target/**/*"] } diff --git a/src/platform/test/api_integration/apis/dashboards/create_dashboard/index.ts b/src/platform/test/api_integration/apis/dashboards/create_dashboard/index.ts index 5a32c8ca968a4..8b78f9659fe13 100644 --- a/src/platform/test/api_integration/apis/dashboards/create_dashboard/index.ts +++ b/src/platform/test/api_integration/apis/dashboards/create_dashboard/index.ts @@ -16,6 +16,9 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.importExport.load( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.load( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); after(async () => { @@ -23,6 +26,9 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.importExport.unload( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.unload( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); loadTestFile(require.resolve('./main')); loadTestFile(require.resolve('./validation')); diff --git a/src/platform/test/api_integration/apis/dashboards/create_dashboard/main.ts b/src/platform/test/api_integration/apis/dashboards/create_dashboard/main.ts index 3b8b71f827deb..54c3fbafad401 100644 --- a/src/platform/test/api_integration/apis/dashboards/create_dashboard/main.ts +++ b/src/platform/test/api_integration/apis/dashboards/create_dashboard/main.ts @@ -8,6 +8,7 @@ */ import expect from '@kbn/expect'; +import { type SavedObjectReference } from '@kbn/core/server'; import { PUBLIC_API_PATH } from '@kbn/dashboard-plugin/server'; import { DEFAULT_IGNORE_PARENT_SETTINGS } from '@kbn/controls-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -171,6 +172,153 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.item.attributes.panels).to.be.an('array'); }); + describe('create a dashboard with tags', () => { + it('with tags specified as an array of names', async () => { + const title = `foo-${Date.now()}-${Math.random()}`; + + const response = await supertest + .post(PUBLIC_API_PATH) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + attributes: { + title, + tags: ['foo'], + }, + references: [ + { + name: 'bizz:panel_bizz', + type: 'visualization', + id: 'my-saved-object', + }, + ], + }); + + expect(response.status).to.be(200); + expect(response.body.item.attributes.tags).to.contain('foo'); + expect(response.body.item.attributes.tags).to.have.length(1); + // adds tag reference to existing references + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(referenceIds).to.contain('my-saved-object'); + expect(response.body.item.references).to.have.length(2); + }); + + it('creates tags if a saved object matching a tag name is not found', async () => { + const title = `foo-${Date.now()}-${Math.random()}`; + const response = await supertest + .post(PUBLIC_API_PATH) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + attributes: { + title, + tags: ['foo', 'not-found-tag'], + }, + }); + expect(response.status).to.be(200); + expect(response.body.item.attributes.tags).to.contain('foo', 'not-found-tag'); + expect(response.body.item.attributes.tags).to.have.length(2); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(response.body.item.references).to.have.length(2); + }); + + it('with tags specified as references', async () => { + const title = `foo-${Date.now()}-${Math.random()}`; + const response = await supertest + .post(PUBLIC_API_PATH) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + attributes: { + title, + }, + references: [ + { + type: 'tag', + id: 'tag-3', + name: 'tag-ref-tag-3', + }, + ], + }); + expect(response.status).to.be(200); + expect(response.body.item.attributes.tags).to.contain('buzz'); + expect(response.body.item.attributes.tags).to.have.length(1); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-3'); + expect(response.body.item.references).to.have.length(1); + }); + + it('with tags specified using both tags array and references', async () => { + const title = `foo-${Date.now()}-${Math.random()}`; + const response = await supertest + .post(PUBLIC_API_PATH) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + attributes: { + title, + tags: ['foo'], + }, + references: [ + { + type: 'tag', + id: 'tag-2', + name: 'tag-ref-tag-2', + }, + ], + }); + expect(response.status).to.be(200); + expect(response.body.item.attributes.tags).to.contain('foo'); + expect(response.body.item.attributes.tags).to.contain('bar'); + expect(response.body.item.attributes.tags).to.have.length(2); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(referenceIds).to.contain('tag-2'); + expect(response.body.item.references).to.have.length(2); + }); + + it('with the same tag specified as a reference and a tag name', async () => { + const title = `foo-${Date.now()}-${Math.random()}`; + const response = await supertest + .post(PUBLIC_API_PATH) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + attributes: { + title, + tags: ['foo', 'buzz'], + }, + references: [ + { + type: 'tag', + id: 'tag-1', + name: 'tag-ref-tag-1', + }, + ], + }); + expect(response.status).to.be(200); + expect(response.body.item.attributes.tags).to.contain('foo'); + expect(response.body.item.attributes.tags).to.contain('buzz'); + expect(response.body.item.attributes.tags).to.have.length(2); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(referenceIds).to.contain('tag-3'); + expect(response.body.item.references).to.have.length(2); + }); + }); + // TODO Maybe move this test to x-pack/test/api_integration/dashboards it('can create a dashboard in a defined space', async () => { const title = `foo-${Date.now()}-${Math.random()}`; diff --git a/src/platform/test/api_integration/apis/dashboards/get_dashboard/index.ts b/src/platform/test/api_integration/apis/dashboards/get_dashboard/index.ts index c0dc629af1f8e..b66375a3cbd37 100644 --- a/src/platform/test/api_integration/apis/dashboards/get_dashboard/index.ts +++ b/src/platform/test/api_integration/apis/dashboards/get_dashboard/index.ts @@ -16,6 +16,9 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.importExport.load( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.load( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); after(async () => { @@ -23,6 +26,9 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.importExport.unload( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.unload( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); loadTestFile(require.resolve('./main')); }); diff --git a/src/platform/test/api_integration/apis/dashboards/get_dashboard/main.ts b/src/platform/test/api_integration/apis/dashboards/get_dashboard/main.ts index b6585c0c4f48a..0470b3db46f96 100644 --- a/src/platform/test/api_integration/apis/dashboards/get_dashboard/main.ts +++ b/src/platform/test/api_integration/apis/dashboards/get_dashboard/main.ts @@ -8,6 +8,7 @@ */ import expect from '@kbn/expect'; +import { type SavedObjectReference } from '@kbn/core/server'; import { PUBLIC_API_PATH } from '@kbn/dashboard-plugin/server'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -30,5 +31,31 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.item.attributes.options).to.not.have.keys(['darkTheme']); expect(response.body.item.attributes.refreshInterval).to.not.have.keys(['display']); }); + + it('should return 404 with a non-existing dashboard', async () => { + const response = await supertest + .get(`${PUBLIC_API_PATH}/does-not-exist`) + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send(); + + expect(response.status).to.be(404); + }); + + it('should inject tag names into attributes', async () => { + const response = await supertest + .get(`${PUBLIC_API_PATH}/8d66658a-f5b7-4482-84dc-f41d317473b8`) + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send(); + + expect(response.status).to.be(200); + + expect(response.body.item.attributes.tags).to.contain('bar'); + expect(response.body.item.attributes.tags).to.contain('buzz'); + expect(response.body.item.attributes.tags).to.have.length(2); + const referenceIds = response.body.item.references.map((ref: SavedObjectReference) => ref.id); + expect(referenceIds).to.contain('tag-2'); + expect(referenceIds).to.contain('tag-3'); + expect(response.body.item.references).to.have.length(2); + }); }); } diff --git a/src/platform/test/api_integration/apis/dashboards/list_dashboards/index.ts b/src/platform/test/api_integration/apis/dashboards/list_dashboards/index.ts index 10f77ad3fee5a..c2b250c6179c2 100644 --- a/src/platform/test/api_integration/apis/dashboards/list_dashboards/index.ts +++ b/src/platform/test/api_integration/apis/dashboards/list_dashboards/index.ts @@ -11,36 +11,18 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); - const supertest = getService('supertest'); describe('dashboards - list', () => { - const createManyDashboards = async (count: number) => { - const fileChunks: string[] = []; - for (let i = 0; i < count; i++) { - const id = `test-dashboard-${i}`; - fileChunks.push( - JSON.stringify({ - type: 'dashboard', - id, - attributes: { - title: `My dashboard (${i})`, - kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, - }, - references: [], - }) - ); - } - - await supertest - .post(`/api/saved_objects/_import`) - .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') - .expect(200); - }; before(async () => { - await createManyDashboards(100); + await kibanaServer.importExport.load( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/many-dashboards.json' + ); }); after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.unload( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/many-dashboards.json' + ); }); loadTestFile(require.resolve('./main')); }); diff --git a/src/platform/test/api_integration/apis/dashboards/list_dashboards/main.ts b/src/platform/test/api_integration/apis/dashboards/list_dashboards/main.ts index c0ef1059169ef..19d61544e2e08 100644 --- a/src/platform/test/api_integration/apis/dashboards/list_dashboards/main.ts +++ b/src/platform/test/api_integration/apis/dashboards/list_dashboards/main.ts @@ -22,7 +22,7 @@ export default function ({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(response.body.total).to.be(100); - expect(response.body.items[0].id).to.be('test-dashboard-0'); + expect(response.body.items[0].id).to.be('test-dashboard-00'); expect(response.body.items.length).to.be(20); }); diff --git a/src/platform/test/api_integration/apis/dashboards/update_dashboard/index.ts b/src/platform/test/api_integration/apis/dashboards/update_dashboard/index.ts index 4fe50df85b090..698c3294b13f0 100644 --- a/src/platform/test/api_integration/apis/dashboards/update_dashboard/index.ts +++ b/src/platform/test/api_integration/apis/dashboards/update_dashboard/index.ts @@ -16,12 +16,18 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.importExport.load( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.load( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); after(async () => { await kibanaServer.importExport.unload( 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' ); + await kibanaServer.importExport.unload( + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json' + ); }); loadTestFile(require.resolve('./main')); loadTestFile(require.resolve('./validation')); diff --git a/src/platform/test/api_integration/apis/dashboards/update_dashboard/main.ts b/src/platform/test/api_integration/apis/dashboards/update_dashboard/main.ts index 18a7d5ca2d3fe..1d8fc895e131d 100644 --- a/src/platform/test/api_integration/apis/dashboards/update_dashboard/main.ts +++ b/src/platform/test/api_integration/apis/dashboards/update_dashboard/main.ts @@ -8,9 +8,36 @@ */ import expect from '@kbn/expect'; +import { type SavedObjectReference } from '@kbn/core/server'; import { PUBLIC_API_PATH } from '@kbn/dashboard-plugin/server'; import { FtrProviderContext } from '../../../ftr_provider_context'; +const updatedDashboard = { + attributes: { + title: 'Refresh Requests (Updated)', + options: { useMargins: false }, + panels: [ + { + type: 'visualization', + gridData: { x: 0, y: 0, w: 48, h: 60, i: '1' }, + panelIndex: '1', + panelRefName: 'panel_1', + version: '7.3.0', + }, + ], + timeFrom: 'Wed Sep 16 2015 22:52:17 GMT-0700', + timeRestore: true, + timeTo: 'Fri Sep 18 2015 12:24:38 GMT-0700', + }, + references: [ + { + id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', + name: '1:panel_1', + type: 'visualization', + }, + ], +}; + export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); describe('main', () => { @@ -19,31 +46,7 @@ export default function ({ getService }: FtrProviderContext) { .put(`${PUBLIC_API_PATH}/be3733a0-9efe-11e7-acb3-3dab96693fab`) .set('kbn-xsrf', 'true') .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') - .send({ - attributes: { - title: 'Refresh Requests (Updated)', - options: { useMargins: false }, - panels: [ - { - type: 'visualization', - gridData: { x: 0, y: 0, w: 48, h: 60, i: '1' }, - panelIndex: '1', - panelRefName: 'panel_1', - version: '7.3.0', - }, - ], - timeFrom: 'Wed Sep 16 2015 22:52:17 GMT-0700', - timeRestore: true, - timeTo: 'Fri Sep 18 2015 12:24:38 GMT-0700', - }, - references: [ - { - id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', - name: '1:panel_1', - type: 'visualization', - }, - ], - }); + .send(updatedDashboard); expect(response.status).to.be(201); @@ -70,5 +73,107 @@ export default function ({ getService }: FtrProviderContext) { message: 'A dashboard with saved object ID not-an-id was not found.', }); }); + + describe('update a dashboard with tags', () => { + it('adds a tag to the dashboard', async () => { + const response = await supertest + .put(`${PUBLIC_API_PATH}/be3733a0-9efe-11e7-acb3-3dab96693fab`) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + ...updatedDashboard, + attributes: { + ...updatedDashboard.attributes, + tags: ['bar'], + }, + }); + + expect(response.status).to.be(201); + expect(response.body.item.attributes.tags).to.contain('bar'); + expect(response.body.item.attributes.tags).to.have.length(1); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-2'); + expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(response.body.item.references).to.have.length(2); + }); + + it('replaces the tags on the dashboard', async () => { + const response = await supertest + .put(`${PUBLIC_API_PATH}/be3733a0-9efe-11e7-acb3-3dab96693fab`) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + ...updatedDashboard, + attributes: { + ...updatedDashboard.attributes, + tags: ['foo'], + }, + }); + + expect(response.status).to.be(201); + expect(response.body.item.attributes.tags).to.contain('foo'); + expect(response.body.item.attributes.tags).to.have.length(1); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(response.body.item.references).to.have.length(2); + }); + + it('empty tags array removes all tags', async () => { + const response = await supertest + .put(`${PUBLIC_API_PATH}/be3733a0-9efe-11e7-acb3-3dab96693fab`) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + ...updatedDashboard, + attributes: { + ...updatedDashboard.attributes, + tags: [], + }, + }); + + expect(response.status).to.be(201); + expect(response.body.item.attributes).not.to.have.property('tags'); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(response.body.item.references).to.have.length(1); + }); + + it('creates tag if a saved object matching a tag name is not found', async () => { + const randomTagName = `tag-${Math.random() * 1000}`; + const response = await supertest + .put(`${PUBLIC_API_PATH}/be3733a0-9efe-11e7-acb3-3dab96693fab`) + .set('kbn-xsrf', 'true') + .set('ELASTIC_HTTP_VERSION_HEADER', '2023-10-31') + .send({ + ...updatedDashboard, + attributes: { + ...updatedDashboard.attributes, + tags: ['foo', 'bar', 'buzz', randomTagName], + }, + }); + + expect(response.status).to.be(201); + expect(response.body.item.attributes.tags).to.contain('foo'); + expect(response.body.item.attributes.tags).to.contain('bar'); + expect(response.body.item.attributes.tags).to.contain('buzz'); + expect(response.body.item.attributes.tags).to.contain(randomTagName); + expect(response.body.item.attributes.tags).to.have.length(4); + const referenceIds = response.body.item.references.map( + (ref: SavedObjectReference) => ref.id + ); + expect(referenceIds).to.contain('tag-1'); + expect(referenceIds).to.contain('tag-2'); + expect(referenceIds).to.contain('tag-3'); + expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(response.body.item.references).to.have.length(5); + }); + }); }); } diff --git a/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/many-dashboards.json b/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/many-dashboards.json new file mode 100644 index 0000000000000..f518aa7ec2fcd --- /dev/null +++ b/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/many-dashboards.json @@ -0,0 +1,1999 @@ +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (0)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-00", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIwNSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (1)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-01", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIwNiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (2)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-02", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIwNywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (3)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-03", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIwOCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (4)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-04", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIwOSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (5)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-05", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxMCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (6)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-06", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxMSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (7)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-07", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxMiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (8)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-08", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxMywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (9)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-09", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxNCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (10)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-10", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxNSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (11)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-11", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxNiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (12)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-12", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxNywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (13)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-13", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxOCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (14)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-14", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIxOSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (15)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-15", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyMCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (16)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-16", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyMSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (17)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-17", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyMiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (18)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-18", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyMywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (19)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-19", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyNCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (20)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-20", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyNSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (21)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-21", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyNiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (22)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-22", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyNywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (23)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-23", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyOCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (24)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-24", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIyOSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (25)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-25", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzMCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (26)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-26", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzMSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (27)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-27", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzMiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (28)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-28", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzMywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (29)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-29", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzNCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (30)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-30", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzNSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (31)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-31", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzNiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (32)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-32", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzNywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (33)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-33", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzOCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (34)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-34", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzIzOSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (35)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-35", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (36)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-36", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (37)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-37", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (38)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-38", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (39)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-39", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (40)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-40", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (41)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-41", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (42)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-42", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (43)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-43", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (44)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-44", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI0OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (45)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-45", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (46)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-46", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (47)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-47", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (48)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-48", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (49)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-49", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (50)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-50", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (51)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-51", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (52)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-52", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (53)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-53", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (54)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-54", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI1OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (55)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-55", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (56)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-56", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (57)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-57", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (58)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-58", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (59)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-59", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (60)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-60", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (61)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-61", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (62)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-62", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (63)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-63", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (64)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-64", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI2OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (65)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-65", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (66)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-66", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (67)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-67", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (68)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-68", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (69)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-69", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (70)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-70", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (71)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-71", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (72)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-72", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (73)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-73", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (74)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-74", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI3OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (75)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-75", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (76)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-76", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (77)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-77", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (78)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-78", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (79)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-79", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (80)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-80", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (81)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-81", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (82)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-82", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (83)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-83", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (84)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-84", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI4OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (85)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-85", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5MCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (86)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-86", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5MSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (87)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-87", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5MiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (88)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-88", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5MywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (89)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-89", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5NCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (90)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-90", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5NSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (91)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-91", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5NiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (92)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-92", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5NywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (93)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-93", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (94)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-94", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzI5OSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (95)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-95", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzMwMCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (96)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-96", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzMwMSwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (97)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-97", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzMwMiwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (98)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-98", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzMwMywxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "panelsJSON": "[]", + "timeRestore": false, + "title": "My dashboard (99)" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-20T18:14:41.618Z", + "id": "test-dashboard-99", + "managed": false, + "references": [], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-20T18:14:41.618Z", + "version": "WzMwNCwxXQ==" +} diff --git a/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json b/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json new file mode 100644 index 0000000000000..3742bc59ced44 --- /dev/null +++ b/src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/tags.json @@ -0,0 +1,91 @@ +{ + "attributes": { + "color": "#123456", + "description": "Another awesome tag", + "name": "bar" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-19T18:17:49.747Z", + "id": "tag-2", + "managed": false, + "references": [], + "type": "tag", + "typeMigrationVersion": "8.0.0", + "updated_at": "2025-03-19T18:17:49.747Z", + "version": "WzYxMywxXQ==" +} + +{ + "attributes": { + "color": "#000000", + "description": "Last but not least", + "name": "buzz" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-19T18:17:49.747Z", + "id": "tag-3", + "managed": false, + "references": [], + "type": "tag", + "typeMigrationVersion": "8.0.0", + "updated_at": "2025-03-19T18:17:49.747Z", + "version": "WzYxNCwxXQ==" +} + +{ + "attributes": { + "controlGroupInput": { + "chainingSystem": "HIERARCHICAL", + "controlStyle": "oneLine", + "ignoreParentSettingsJSON": "{\"ignoreFilters\":false,\"ignoreQuery\":false,\"ignoreTimerange\":false,\"ignoreValidations\":false}", + "panelsJSON": "{}", + "showApplySelections": false + }, + "description": "I have some tags!", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"kuery\"}}" + }, + "optionsJSON": "{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"type\":\"visualization\",\"embeddableConfig\":{\"enhancements\":{\"dynamicActions\":{\"events\":[]}},\"savedVis\":{\"id\":\"\",\"title\":\"\",\"description\":\"\",\"type\":\"markdown\",\"params\":{\"fontSize\":12,\"openLinksInNewTab\":false,\"markdown\":\"# Hello world\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}}},\"panelIndex\":\"b9e32168-d39e-4289-ab07-30cfda9e00d4\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"b9e32168-d39e-4289-ab07-30cfda9e00d4\"}}]", + "timeRestore": false, + "title": "tagged dashboard", + "version": 3 + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-19T18:18:29.671Z", + "id": "8d66658a-f5b7-4482-84dc-f41d317473b8", + "managed": false, + "references": [ + { + "id": "tag-2", + "name": "tag-ref-tag-2", + "type": "tag" + }, + { + "id": "tag-3", + "name": "tag-ref-tag-3", + "type": "tag" + } + ], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-03-19T18:18:58.406Z", + "version": "WzQ1MTQxLDFd" +} + +{ + "attributes": { + "color": "#FF00FF", + "description": "My first tag!", + "name": "foo" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-03-19T18:17:49.747Z", + "id": "tag-1", + "managed": false, + "references": [], + "type": "tag", + "typeMigrationVersion": "8.0.0", + "updated_at": "2025-03-19T18:17:49.747Z", + "version": "WzYxMiwxXQ==" +}