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 f2094eb658726..d230f8c037700 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 @@ -18,9 +18,6 @@ import type { SearchQuery, } from '@kbn/content-management-plugin/common'; import type { 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 type { DashboardSavedObjectAttributes } from '../dashboard_saved_object'; @@ -35,10 +32,6 @@ import type { } from './latest'; import type { DashboardCreateOut, DashboardSearchOut, DashboardUpdateOut } from './v1/types'; -const getRandomColor = (): string => { - return '#' + String(Math.floor(Math.random() * 16777215).toString(16)).padStart(6, '0'); -}; - const searchArgsToSOFindOptions = ( query: SearchQuery, options: DashboardSearchOptions @@ -69,117 +62,20 @@ 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, @@ -188,10 +84,7 @@ export class DashboardStorage { outcome, } = await soClient.resolve(DASHBOARD_SAVED_OBJECT_TYPE, id); - const { item, error: itemError } = savedObjectToItem(savedObject, false, { - getTagNamesFromReferences: (references: SavedObjectReference[]) => - this.getTagNamesFromReferences(references, allTags), - }); + const { item, error: itemError } = savedObjectToItem(savedObject, false); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -237,8 +130,6 @@ 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< @@ -261,10 +152,8 @@ export class DashboardStorage { attributes: soAttributes, references: soReferences, error: transformDashboardError, - } = await transformDashboardIn({ + } = transformDashboardIn({ dashboardState: dataToLatest, - replaceTagReferencesByName: ({ references, newTagNames }) => - this.replaceTagReferencesByName(references, newTagNames, allTags, tagsClient), incomingReferences: options.references, }); if (transformDashboardError) { @@ -278,10 +167,7 @@ export class DashboardStorage { { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(savedObject, false, { - getTagNamesFromReferences: (references: SavedObjectReference[]) => - this.getTagNamesFromReferences(references, allTags), - }); + const { item, error: itemError } = savedObjectToItem(savedObject, false); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); @@ -320,8 +206,6 @@ 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< @@ -344,10 +228,8 @@ export class DashboardStorage { attributes: soAttributes, references: soReferences, error: transformDashboardError, - } = await transformDashboardIn({ + } = transformDashboardIn({ dashboardState: dataToLatest, - replaceTagReferencesByName: ({ references, newTagNames }) => - this.replaceTagReferencesByName(references, newTagNames, allTags, tagsClient), incomingReferences: options.references, }); if (transformDashboardError) { @@ -362,10 +244,7 @@ export class DashboardStorage { { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(partialSavedObject, true, { - getTagNamesFromReferences: (references: SavedObjectReference[]) => - this.getTagNamesFromReferences(references, allTags), - }); + const { item, error: itemError } = savedObjectToItem(partialSavedObject, true); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -415,8 +294,6 @@ 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< @@ -435,8 +312,6 @@ export class DashboardStorage { const { item } = savedObjectToItem(so, false, { allowedAttributes: soQuery.fields, allowedReferences: optionsToLatest?.includeReferences, - getTagNamesFromReferences: (references: SavedObjectReference[]) => - this.getTagNamesFromReferences(references, allTags), }); return item; }) diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/schema/common.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/schema/common.ts index 3393078395411..337c9d4cbd786 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/schema/common.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/schema/common.ts @@ -306,7 +306,7 @@ export const searchResultsAttributes = { }), tags: schema.maybe( schema.arrayOf( - schema.string({ meta: { description: 'An array of tags applied to this dashboard' } }) + schema.string({ meta: { description: 'An array of tags ids applied to this dashboard' } }) ) ), }; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.test.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.test.ts index b28caed78d5d9..056e801a7f44a 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.test.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.test.ts @@ -32,8 +32,6 @@ describe('savedObjectToItem', () => { }; }; - const getTagNamesFromReferences = jest.fn(); - beforeEach(() => { jest.resetAllMocks(); }); @@ -101,51 +99,6 @@ 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/v1/transform_utils.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.ts index 05ced98c112f7..4beb3e2b2ee74 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transform_utils.ts @@ -36,7 +36,6 @@ export function savedObjectToItem( { allowedAttributes, allowedReferences, - getTagNamesFromReferences, }: { /** * attributes to include in the output item @@ -46,7 +45,6 @@ export function savedObjectToItem( * references to include in the output item */ allowedReferences?: string[]; - getTagNamesFromReferences?: (references: SavedObjectReference[]) => string[]; } = {} ): SavedObjectToItemReturn { const { @@ -63,11 +61,8 @@ export function savedObjectToItem( managed, } = savedObject; try { - const dashboardState = transformDashboardOut( - attributes, - savedObject.references, - getTagNamesFromReferences - ); + const dashboardState = transformDashboardOut(attributes, savedObject.references); + const references = transformReferencesOut(savedObject.references ?? []); return { diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.test.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.test.ts index 83ade34871a8d..32c23ebcbb3e3 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.test.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.test.ts @@ -12,7 +12,7 @@ import type { DashboardAttributes } from '../../types'; import { transformDashboardIn } from './transform_dashboard_in'; describe('transformDashboardIn', () => { - test('should transform dashboard state to saved object', async () => { + test('should transform dashboard state to saved object', () => { const dashboardState: DashboardAttributes = { controlGroupInput: { chainingSystem: 'NONE', @@ -65,7 +65,7 @@ describe('transformDashboardIn', () => { timeTo: 'now', }; - const output = await transformDashboardIn({ dashboardState }); + const output = transformDashboardIn({ dashboardState }); expect(output).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -97,7 +97,7 @@ describe('transformDashboardIn', () => { `); }); - it('should handle missing optional state keys', async () => { + it('should handle missing optional state keys', () => { const dashboardState: DashboardAttributes = { title: 'title', description: 'my description', @@ -107,7 +107,7 @@ describe('transformDashboardIn', () => { kibanaSavedObjectMeta: {}, }; - const output = await transformDashboardIn({ dashboardState }); + const output = transformDashboardIn({ dashboardState }); expect(output).toMatchInlineSnapshot(` Object { "attributes": Object { diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.ts index 6ff48690b7e48..d5e7b881b2f45 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_dashboard_in.ts @@ -8,27 +8,21 @@ */ import type { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; +import { tagSavedObjectTypeName } from '@kbn/saved-objects-tagging-plugin/common'; import type { DashboardAttributes } from '../../types'; import type { DashboardSavedObjectAttributes } from '../../../../dashboard_saved_object'; import { transformPanelsIn } from './transform_panels_in'; import { transformControlGroupIn } from './transform_control_group_in'; import { transformSearchSourceIn } from './transform_search_source_in'; +import { transformTagsIn } from './transform_tags_in'; -export const transformDashboardIn = async ({ +export const transformDashboardIn = ({ dashboardState, - replaceTagReferencesByName, incomingReferences = [], }: { dashboardState: DashboardAttributes; incomingReferences?: SavedObjectReference[]; - replaceTagReferencesByName?: ({ - references, - newTagNames, - }: { - references: SavedObjectReference[]; - newTagNames: string[]; - }) => Promise; -}): Promise< +}): | { attributes: DashboardSavedObjectAttributes; references: SavedObjectReference[]; @@ -38,19 +32,21 @@ export const transformDashboardIn = async ({ attributes: null; references: null; error: Error; - } -> => { + } => { try { - const tagReferences = - replaceTagReferencesByName && dashboardState.tags && dashboardState.tags.length - ? await replaceTagReferencesByName({ - references: incomingReferences, - newTagNames: dashboardState.tags, - }) - : incomingReferences; - const { controlGroupInput, kibanaSavedObjectMeta, options, panels, tags, ...rest } = dashboardState; + + const tagReferences = transformTagsIn({ + tags, + references: incomingReferences, + }); + + // TODO - remove once all references are provided server side + const nonTagIncomingReferences = incomingReferences.filter( + ({ type }) => type !== tagSavedObjectTypeName + ); + const { panelsJSON, sections, references: panelReferences } = transformPanelsIn(panels); const { searchSourceJSON, references: searchSourceReferences } = @@ -74,7 +70,12 @@ export const transformDashboardIn = async ({ }; return { attributes, - references: [...tagReferences, ...panelReferences, ...searchSourceReferences], + references: [ + ...tagReferences, + ...nonTagIncomingReferences, + ...panelReferences, + ...searchSourceReferences, + ], error: null, }; } catch (e) { diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.test.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.test.ts new file mode 100644 index 0000000000000..6dc913b48b34a --- /dev/null +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.test.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { transformTagsIn } from './transform_tags_in'; + +describe('transformTagsIn', () => { + test('Should merge tags from attributes and references', () => { + const tagRefs = transformTagsIn({ + tags: ['tag1'], + references: [ + { + id: 'tag2', + type: 'tag', + name: 'tag-ref-tag2', + }, + ], + }); + + expect(tagRefs).toMatchInlineSnapshot(` + Array [ + Object { + "id": "tag2", + "name": "tag-ref-tag2", + "type": "tag", + }, + Object { + "id": "tag1", + "name": "tag-ref-tag1", + "type": "tag", + }, + ] + `); + }); + + test('Should exclude duplicate tags', () => { + const tagRefs = transformTagsIn({ + tags: ['tag2', 'tag2'], + references: [ + { + id: 'tag2', + type: 'tag', + name: 'tag-ref-tag2', + }, + { + id: 'tag2', + type: 'tag', + name: 'tag-ref-tag2', + }, + ], + }); + + expect(tagRefs).toMatchInlineSnapshot(` + Array [ + Object { + "id": "tag2", + "name": "tag-ref-tag2", + "type": "tag", + }, + ] + `); + }); + + test('Should exclude non-tag references', () => { + const tagRefs = transformTagsIn({ + tags: [], + references: [ + { + id: 'dataView1', + type: 'index-pattern', + name: 'ref-dataView1', + }, + ], + }); + + expect(tagRefs.length).toBe(0); + }); +}); diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.ts new file mode 100644 index 0000000000000..ab133e8bef18e --- /dev/null +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/in/transform_tags_in.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; +import { tagSavedObjectTypeName } from '@kbn/saved-objects-tagging-plugin/common'; +import type { DashboardAttributes } from '../../types'; + +export function transformTagsIn({ + tags, + references, +}: { + tags: DashboardAttributes['tags']; + references?: SavedObjectReference[]; +}) { + const uniqueTagIds = new Set([]); + (references ?? []).forEach(({ type, id }) => { + if (type === tagSavedObjectTypeName) uniqueTagIds.add(id); + }); + (tags ?? []).forEach((tagId) => { + uniqueTagIds.add(tagId); + }); + + return Array.from(uniqueTagIds).map((tagId) => ({ + type: tagSavedObjectTypeName, + id: tagId, + name: `tag-ref-${tagId}`, + })); +} diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.test.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.test.ts index 08407d27cd37e..cc5cdae3c16db 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.test.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.test.ts @@ -129,7 +129,24 @@ describe('transformDashboardOut', () => { timeTo: 'now', title: 'title', }; - expect(transformDashboardOut(input)).toEqual({ + const 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', + }, + ]; + expect(transformDashboardOut(input, references)).toEqual({ controlGroupInput: { chainingSystem: 'NONE', labelPosition: 'twoLine', @@ -187,6 +204,7 @@ describe('transformDashboardOut', () => { pause: true, value: 1000, }, + tags: ['tag1', 'tag2'], timeFrom: 'now-15m', timeRestore: true, timeTo: 'now', diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.ts b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.ts index 80015c15b9495..4e467bb92acd7 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v1/transforms/out/transform_dashboard_out.ts @@ -8,6 +8,7 @@ */ import type { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; +import { tagSavedObjectTypeName } from '@kbn/saved-objects-tagging-plugin/common'; import type { DashboardSavedObjectAttributes } from '../../../../dashboard_saved_object'; import type { DashboardAttributes } from '../../types'; import { transformControlGroupOut } from './transform_control_group_out'; @@ -17,8 +18,7 @@ import { transformPanelsOut } from './transform_panels_out'; export function transformDashboardOut( attributes: DashboardSavedObjectAttributes | Partial, - references?: SavedObjectReference[], - getTagNamesFromReferences?: (references: SavedObjectReference[]) => string[] + references?: SavedObjectReference[] ): DashboardAttributes | Partial { const { controlGroupInput, @@ -34,11 +34,10 @@ export function transformDashboardOut( title, version, } = attributes; - // Inject any tag names from references into the attributes - let tags: string[] | undefined; - if (getTagNamesFromReferences && references && references.length) { - tags = getTagNamesFromReferences(references); - } + // Extract tag references + const tags: string[] = references + ? references.filter(({ type }) => type === tagSavedObjectTypeName).map(({ id }) => id) + : []; // try to maintain a consistent (alphabetical) order of keys return { diff --git a/src/platform/plugins/shared/dashboard/server/plugin.ts b/src/platform/plugins/shared/dashboard/server/plugin.ts index 72f5707a26409..6e42d6471da06 100644 --- a/src/platform/plugins/shared/dashboard/server/plugin.ts +++ b/src/platform/plugins/shared/dashboard/server/plugin.ts @@ -85,20 +85,17 @@ export class DashboardPlugin }) ); - 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; + 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, + }, }); + this.contentClient = contentClient; plugins.contentManagement.favorites.registerFavoriteType('dashboard'); 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 a996576040b43..127ac7b68eecc 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,7 +8,6 @@ */ 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-constants'; import type { FtrProviderContext } from '../../../ftr_provider_context'; @@ -168,148 +167,6 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - title, - tags: ['foo'], - references: [ - { - name: 'bizz:panel_bizz', - type: 'visualization', - id: 'my-saved-object', - }, - ], - }); - - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo'); - expect(response.body.data.tags).to.have.length(1); - // adds tag reference to existing references - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-1'); - expect(referenceIds).to.contain('my-saved-object'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - title, - tags: ['foo', 'not-found-tag'], - }); - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo', 'not-found-tag'); - expect(response.body.data.tags).to.have.length(2); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-1'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - title, - references: [ - { - type: 'tag', - id: 'tag-3', - name: 'tag-ref-tag-3', - }, - ], - }); - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('buzz'); - expect(response.body.data.tags).to.have.length(1); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-3'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - title, - tags: ['foo'], - references: [ - { - type: 'tag', - id: 'tag-2', - name: 'tag-ref-tag-2', - }, - ], - }); - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo'); - expect(response.body.data.tags).to.contain('bar'); - expect(response.body.data.tags).to.have.length(2); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-1'); - expect(referenceIds).to.contain('tag-2'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - title, - tags: ['foo', 'buzz'], - references: [ - { - type: 'tag', - id: 'tag-1', - name: 'tag-ref-tag-1', - }, - ], - }); - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo'); - expect(response.body.data.tags).to.contain('buzz'); - expect(response.body.data.tags).to.have.length(2); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-1'); - expect(referenceIds).to.contain('tag-3'); - expect(response.body.data.references).to.have.length(2); - }); - }); - // TODO Maybe move this test to x-pack/platform/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/main.ts b/src/platform/test/api_integration/apis/dashboards/get_dashboard/main.ts index 3e6e17d08b6e7..332a21d21295e 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,7 +8,6 @@ */ import expect from '@kbn/expect'; -import { type SavedObjectReference } from '@kbn/core/server'; import { PUBLIC_API_PATH } from '@kbn/dashboard-plugin/server'; import type { FtrProviderContext } from '../../../ftr_provider_context'; @@ -42,23 +41,5 @@ export default function ({ getService }: FtrProviderContext) { 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') - .set('elastic-api-version', '1') - .send(); - - expect(response.status).to.be(200); - - expect(response.body.data.tags).to.contain('bar'); - expect(response.body.data.tags).to.contain('buzz'); - expect(response.body.data.tags).to.have.length(2); - const referenceIds = response.body.data.references.map((ref: SavedObjectReference) => ref.id); - expect(referenceIds).to.contain('tag-2'); - expect(referenceIds).to.contain('tag-3'); - expect(response.body.data.references).to.have.length(2); - }); }); } 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 666c3d349e69e..715bc8d3ad9a6 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,7 +8,6 @@ */ import expect from '@kbn/expect'; -import { type SavedObjectReference } from '@kbn/core/server'; import { PUBLIC_API_PATH } from '@kbn/dashboard-plugin/server'; import type { FtrProviderContext } from '../../../ftr_provider_context'; @@ -70,99 +69,5 @@ 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') - .set('elastic-api-version', '1') - .send({ - ...updatedDashboard, - tags: ['bar'], - }); - - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('bar'); - expect(response.body.data.tags).to.have.length(1); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-2'); - expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - ...updatedDashboard, - tags: ['foo'], - }); - - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo'); - expect(response.body.data.tags).to.have.length(1); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('tag-1'); - expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - ...updatedDashboard, - tags: [], - }); - - expect(response.status).to.be(200); - expect(response.body.data).not.to.have.property('tags'); - const referenceIds = response.body.data.references.map( - (ref: SavedObjectReference) => ref.id - ); - expect(referenceIds).to.contain('dd7caf20-9efd-11e7-acb3-3dab96693fab'); - expect(response.body.data.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') - .set('elastic-api-version', '1') - .send({ - ...updatedDashboard, - tags: ['foo', 'bar', 'buzz', randomTagName], - }); - - expect(response.status).to.be(200); - expect(response.body.data.tags).to.contain('foo'); - expect(response.body.data.tags).to.contain('bar'); - expect(response.body.data.tags).to.contain('buzz'); - expect(response.body.data.tags).to.contain(randomTagName); - expect(response.body.data.tags).to.have.length(4); - const referenceIds = response.body.data.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.data.references).to.have.length(5); - }); - }); }); }