From 90e46c9c799b2189b7907a4f3c1d7685a866ef54 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 26 Jul 2022 15:37:51 +0200 Subject: [PATCH 01/16] WIP saved object tagging --- x-pack/plugins/fleet/kibana.json | 2 +- x-pack/plugins/fleet/server/plugin.ts | 6 +++ .../fleet/server/services/app_context.ts | 11 +++++ .../services/epm/kibana/assets/install.ts | 42 +++++++++++++++++++ .../services/epm/packages/_install_package.ts | 5 +++ .../server/services/epm/packages/install.ts | 5 +++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index 409e9f2a896ae..635afca2ebfb8 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -9,7 +9,7 @@ "ui": true, "configPath": ["xpack", "fleet"], "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces", "security", "unifiedSearch"], - "optionalPlugins": ["features", "cloud", "usageCollection", "home", "globalSearch", "telemetry", "discover", "ingestPipelines"], + "optionalPlugins": ["features", "cloud", "usageCollection", "home", "globalSearch", "telemetry", "discover", "ingestPipelines", "savedObjectsTagging"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "cloud", "esUiShared", "infra", "kibanaUtils", "usageCollection", "unifiedSearch"] } diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 824cfb091e2c9..b3a7f7af6d412 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -42,6 +42,8 @@ import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server'; + import type { FleetConfigType } from '../common/types'; import type { FleetAuthz } from '../common'; import type { ExperimentalFeatures } from '../common/experimental_features'; @@ -115,6 +117,7 @@ export interface FleetStartDeps { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; security: SecurityPluginStart; telemetry?: TelemetryPluginStart; + savedObjectTaggingStart: SavedObjectTaggingStart; } export interface FleetAppContext { @@ -128,6 +131,7 @@ export interface FleetAppContext { configInitialValue: FleetConfigType; experimentalFeatures: ExperimentalFeatures; savedObjects: SavedObjectsServiceStart; + savedObjectTaggingStart?: SavedObjectTaggingStart; isProductionMode: PluginInitializerContext['env']['mode']['prod']; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch']; @@ -387,6 +391,7 @@ export class FleetPlugin } public start(core: CoreStart, plugins: FleetStartDeps): FleetStartContract { + // console.log(plugins.savedObjectTaggingStart); appContextService.start({ elasticsearch: core.elasticsearch, data: plugins.data, @@ -400,6 +405,7 @@ export class FleetPlugin this.configInitialValue.enableExperimental || [] ), savedObjects: core.savedObjects, + savedObjectTaggingStart: plugins.savedObjectTaggingStart, isProductionMode: this.isProductionMode, kibanaVersion: this.kibanaVersion, kibanaBranch: this.kibanaBranch, diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index da9715d9471f0..93bcfd62cd91b 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -26,6 +26,8 @@ import type { SecurityPluginStart, SecurityPluginSetup } from '@kbn/security-plu import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server'; + import type { FleetConfigType } from '../../common/types'; import type { ExperimentalFeatures } from '../../common/experimental_features'; import type { @@ -58,6 +60,7 @@ class AppContextService { private httpSetup?: HttpServiceSetup; private externalCallbacks: ExternalCallbacksStorage = new Map(); private telemetryEventsSender: TelemetryEventsSender | undefined; + private savedObjectTaggingStart: SavedObjectTaggingStart | undefined; public start(appContext: FleetAppContext) { this.data = appContext.data; @@ -75,6 +78,7 @@ class AppContextService { this.kibanaBranch = appContext.kibanaBranch; this.httpSetup = appContext.httpSetup; this.telemetryEventsSender = appContext.telemetryEventsSender; + this.savedObjectTaggingStart = appContext.savedObjectTaggingStart; if (appContext.config$) { this.config$ = appContext.config$; @@ -143,6 +147,13 @@ class AppContextService { return this.savedObjects; } + public getSavedObjectTaggingStart() { + if (!this.savedObjectTaggingStart) { + throw new Error('Saved object tagging start service not set.'); + } + return this.savedObjectTaggingStart; + } + public getInternalUserSOClient(request: KibanaRequest) { // soClient as kibana internal users, be careful on how you use it, security is not enabled return appContextService.getSavedObjects().getScopedClient(request, { diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 110fb4535ef92..71e37ec04049c 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -18,6 +18,10 @@ import type { SavedObjectsImportSuccess, SavedObjectsImportFailure } from '@kbn/ import { createListStream } from '@kbn/utils'; import { partition } from 'lodash'; +import type { IAssignmentService } from '@kbn/saved-objects-tagging-plugin/server'; + +import { taggableTypes } from '@kbn/saved-objects-tagging-plugin/common/constants'; + import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common'; import { getAsset, getPathParts } from '../../archive'; import { KibanaAssetType, KibanaSavedObjectType } from '../../../../types'; @@ -128,6 +132,7 @@ export async function installKibanaAssets(options: { export async function installKibanaAssetsAndReferences({ savedObjectsClient, savedObjectsImporter, + savedObjectTagAssignmentService, logger, pkgName, paths, @@ -135,6 +140,7 @@ export async function installKibanaAssetsAndReferences({ }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; + savedObjectTagAssignmentService: IAssignmentService; logger: Logger; pkgName: string; paths: string[]; @@ -156,9 +162,45 @@ export async function installKibanaAssetsAndReferences({ kibanaAssets, }); + // console.log(pkgName); + // console.log(installedPkg); + + await tagKibanaAssets({ savedObjectTagAssignmentService, kibanaAssets, pkgName }); + return installedKibanaAssetsRefs; } +export async function tagKibanaAssets({ + savedObjectTagAssignmentService, + kibanaAssets, + pkgName, +}: { + savedObjectTagAssignmentService: IAssignmentService; + kibanaAssets: Record; + pkgName: string; +}) { + const taggableAssets = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { + if (!taggableTypes.includes(assetType as KibanaAssetType)) { + return []; + } + + if (!assets.length) { + return []; + } + + return assets; + }); + + // console.log(taggableAssets); + + // TODO use readable name for pkg name tag + savedObjectTagAssignmentService.updateTagAssignments({ + tags: ['Managed', pkgName], + assign: taggableAssets, + unassign: [], + }); +} + export const deleteKibanaInstalledRefs = async ( savedObjectsClient: SavedObjectsClientContract, pkgName: string, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 35b5f490e538b..cf94f4a6acb68 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -14,6 +14,8 @@ import type { } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import type { IAssignmentService } from '@kbn/saved-objects-tagging-plugin/server'; + import { MAX_TIME_COMPLETE_INSTALL, ASSETS_SAVED_OBJECT_TYPE, @@ -56,6 +58,7 @@ import { withPackageSpan } from './utils'; export async function _installPackage({ savedObjectsClient, savedObjectsImporter, + savedObjectTagAssignmentService, esClient, logger, installedPkg, @@ -68,6 +71,7 @@ export async function _installPackage({ }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; + savedObjectTagAssignmentService: IAssignmentService; esClient: ElasticsearchClient; logger: Logger; installedPkg?: SavedObject; @@ -120,6 +124,7 @@ export async function _installPackage({ installKibanaAssetsAndReferences({ savedObjectsClient, savedObjectsImporter, + savedObjectTagAssignmentService, pkgName, paths, installedPkg, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 0794f43ea79e6..7b85c9bece6aa 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -362,11 +362,16 @@ async function installPackageFromRegistry({ .getSavedObjects() .createImporter(savedObjectsClient); + const savedObjectTagAssignmentService = appContextService + .getSavedObjectTaggingStart() + .createInternalAssignmentService({ client: savedObjectsClient }); + // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' return await _installPackage({ savedObjectsClient, savedObjectsImporter, + savedObjectTagAssignmentService, esClient, logger, installedPkg, From cfe84a8dcf53daaef6d1b19c65e744c21b25fdba Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 26 Jul 2022 16:14:41 +0200 Subject: [PATCH 02/16] fixing plugin usage --- x-pack/plugins/fleet/kibana.json | 4 ++-- x-pack/plugins/fleet/server/plugin.ts | 7 +++---- x-pack/plugins/fleet/server/services/app_context.ts | 10 +++++----- .../fleet/server/services/epm/packages/install.ts | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index 635afca2ebfb8..462e68a097962 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -8,8 +8,8 @@ "server": true, "ui": true, "configPath": ["xpack", "fleet"], - "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces", "security", "unifiedSearch"], - "optionalPlugins": ["features", "cloud", "usageCollection", "home", "globalSearch", "telemetry", "discover", "ingestPipelines", "savedObjectsTagging"], + "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces", "security", "unifiedSearch", "savedObjectsTagging"], + "optionalPlugins": ["features", "cloud", "usageCollection", "home", "globalSearch", "telemetry", "discover", "ingestPipelines"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "cloud", "esUiShared", "infra", "kibanaUtils", "usageCollection", "unifiedSearch"] } diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index b3a7f7af6d412..a2e2208d0e8f1 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -117,7 +117,7 @@ export interface FleetStartDeps { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; security: SecurityPluginStart; telemetry?: TelemetryPluginStart; - savedObjectTaggingStart: SavedObjectTaggingStart; + savedObjectsTagging: SavedObjectTaggingStart; } export interface FleetAppContext { @@ -131,7 +131,7 @@ export interface FleetAppContext { configInitialValue: FleetConfigType; experimentalFeatures: ExperimentalFeatures; savedObjects: SavedObjectsServiceStart; - savedObjectTaggingStart?: SavedObjectTaggingStart; + savedObjectsTagging?: SavedObjectTaggingStart; isProductionMode: PluginInitializerContext['env']['mode']['prod']; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch']; @@ -391,7 +391,6 @@ export class FleetPlugin } public start(core: CoreStart, plugins: FleetStartDeps): FleetStartContract { - // console.log(plugins.savedObjectTaggingStart); appContextService.start({ elasticsearch: core.elasticsearch, data: plugins.data, @@ -405,7 +404,7 @@ export class FleetPlugin this.configInitialValue.enableExperimental || [] ), savedObjects: core.savedObjects, - savedObjectTaggingStart: plugins.savedObjectTaggingStart, + savedObjectsTagging: plugins.savedObjectsTagging, isProductionMode: this.isProductionMode, kibanaVersion: this.kibanaVersion, kibanaBranch: this.kibanaBranch, diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index 93bcfd62cd91b..86f9408bef3f3 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -60,7 +60,7 @@ class AppContextService { private httpSetup?: HttpServiceSetup; private externalCallbacks: ExternalCallbacksStorage = new Map(); private telemetryEventsSender: TelemetryEventsSender | undefined; - private savedObjectTaggingStart: SavedObjectTaggingStart | undefined; + private savedObjectsTagging: SavedObjectTaggingStart | undefined; public start(appContext: FleetAppContext) { this.data = appContext.data; @@ -78,7 +78,7 @@ class AppContextService { this.kibanaBranch = appContext.kibanaBranch; this.httpSetup = appContext.httpSetup; this.telemetryEventsSender = appContext.telemetryEventsSender; - this.savedObjectTaggingStart = appContext.savedObjectTaggingStart; + this.savedObjectsTagging = appContext.savedObjectsTagging; if (appContext.config$) { this.config$ = appContext.config$; @@ -147,11 +147,11 @@ class AppContextService { return this.savedObjects; } - public getSavedObjectTaggingStart() { - if (!this.savedObjectTaggingStart) { + public getSavedObjectsTagging() { + if (!this.savedObjectsTagging) { throw new Error('Saved object tagging start service not set.'); } - return this.savedObjectTaggingStart; + return this.savedObjectsTagging; } public getInternalUserSOClient(request: KibanaRequest) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 7b85c9bece6aa..f1ad4fc9b170a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -363,7 +363,7 @@ async function installPackageFromRegistry({ .createImporter(savedObjectsClient); const savedObjectTagAssignmentService = appContextService - .getSavedObjectTaggingStart() + .getSavedObjectsTagging() .createInternalAssignmentService({ client: savedObjectsClient }); // try installing the package, if there was an error, call error handler and rethrow From 77ec521f3440c6b24596f47b9232cb851fa74469 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 26 Jul 2022 16:54:48 +0200 Subject: [PATCH 03/16] added logic to create tags before assigning --- .../services/epm/kibana/assets/install.ts | 46 +++++++++++++++---- .../services/epm/packages/_install_package.ts | 8 +++- .../server/services/epm/packages/install.ts | 5 ++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 71e37ec04049c..7c651d458dbfc 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -18,7 +18,7 @@ import type { SavedObjectsImportSuccess, SavedObjectsImportFailure } from '@kbn/ import { createListStream } from '@kbn/utils'; import { partition } from 'lodash'; -import type { IAssignmentService } from '@kbn/saved-objects-tagging-plugin/server'; +import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; import { taggableTypes } from '@kbn/saved-objects-tagging-plugin/common/constants'; @@ -133,16 +133,20 @@ export async function installKibanaAssetsAndReferences({ savedObjectsClient, savedObjectsImporter, savedObjectTagAssignmentService, + savedObjectTagClient, logger, pkgName, + pkgTitle, paths, installedPkg, }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; savedObjectTagAssignmentService: IAssignmentService; + savedObjectTagClient: ITagsClient; logger: Logger; pkgName: string; + pkgTitle: string; paths: string[]; installedPkg?: SavedObject; }) { @@ -162,22 +166,26 @@ export async function installKibanaAssetsAndReferences({ kibanaAssets, }); - // console.log(pkgName); - // console.log(installedPkg); - - await tagKibanaAssets({ savedObjectTagAssignmentService, kibanaAssets, pkgName }); + await tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle, + }); return installedKibanaAssetsRefs; } export async function tagKibanaAssets({ savedObjectTagAssignmentService, + savedObjectTagClient, kibanaAssets, - pkgName, + pkgTitle, }: { savedObjectTagAssignmentService: IAssignmentService; + savedObjectTagClient: ITagsClient; kibanaAssets: Record; - pkgName: string; + pkgTitle: string; }) { const taggableAssets = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { if (!taggableTypes.includes(assetType as KibanaAssetType)) { @@ -191,11 +199,29 @@ export async function tagKibanaAssets({ return assets; }); - // console.log(taggableAssets); + const managedTagName = 'Managed'; + const tagColor = '#FFFFFF'; + const allTags = await savedObjectTagClient.getAll(); + let managedTag = allTags.find((tag) => tag.name === managedTagName); + if (!managedTag) { + managedTag = await savedObjectTagClient.create({ + name: managedTagName, + description: '', + color: tagColor, + }); + } + + let packageTag = allTags.find((tag) => tag.name === pkgTitle); + if (!packageTag) { + packageTag = await savedObjectTagClient.create({ + name: pkgTitle, + description: '', + color: tagColor, + }); + } - // TODO use readable name for pkg name tag savedObjectTagAssignmentService.updateTagAssignments({ - tags: ['Managed', pkgName], + tags: [managedTag.id, packageTag.id], assign: taggableAssets, unassign: [], }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index cf94f4a6acb68..00eb71e918bd2 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -14,7 +14,7 @@ import type { } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; -import type { IAssignmentService } from '@kbn/saved-objects-tagging-plugin/server'; +import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; import { MAX_TIME_COMPLETE_INSTALL, @@ -59,6 +59,7 @@ export async function _installPackage({ savedObjectsClient, savedObjectsImporter, savedObjectTagAssignmentService, + savedObjectTagClient, esClient, logger, installedPkg, @@ -72,6 +73,7 @@ export async function _installPackage({ savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; savedObjectTagAssignmentService: IAssignmentService; + savedObjectTagClient: ITagsClient; esClient: ElasticsearchClient; logger: Logger; installedPkg?: SavedObject; @@ -82,7 +84,7 @@ export async function _installPackage({ spaceId: string; verificationResult?: PackageVerificationResult; }): Promise { - const { name: pkgName, version: pkgVersion } = packageInfo; + const { name: pkgName, version: pkgVersion, title: pkgTitle } = packageInfo; try { // if some installation already exists @@ -125,7 +127,9 @@ export async function _installPackage({ savedObjectsClient, savedObjectsImporter, savedObjectTagAssignmentService, + savedObjectTagClient, pkgName, + pkgTitle, paths, installedPkg, logger, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index f1ad4fc9b170a..97d248ed7a049 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -366,12 +366,17 @@ async function installPackageFromRegistry({ .getSavedObjectsTagging() .createInternalAssignmentService({ client: savedObjectsClient }); + const savedObjectTagClient = appContextService + .getSavedObjectsTagging() + .createTagClient({ client: savedObjectsClient }); + // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' return await _installPackage({ savedObjectsClient, savedObjectsImporter, savedObjectTagAssignmentService, + savedObjectTagClient, esClient, logger, installedPkg, From d7cbcf40f9302d9a550f919064d19dbc448c0820 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 26 Jul 2022 17:31:32 +0200 Subject: [PATCH 04/16] moved constants out --- .../server/services/epm/kibana/assets/install.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 7c651d458dbfc..515f6a9c935ca 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -176,6 +176,9 @@ export async function installKibanaAssetsAndReferences({ return installedKibanaAssetsRefs; } +const TAG_COLOR = '#FFFFFF'; +const MANAGED_TAG_NAME = 'Managed'; + export async function tagKibanaAssets({ savedObjectTagAssignmentService, savedObjectTagClient, @@ -199,15 +202,13 @@ export async function tagKibanaAssets({ return assets; }); - const managedTagName = 'Managed'; - const tagColor = '#FFFFFF'; const allTags = await savedObjectTagClient.getAll(); - let managedTag = allTags.find((tag) => tag.name === managedTagName); + let managedTag = allTags.find((tag) => tag.name === MANAGED_TAG_NAME); if (!managedTag) { managedTag = await savedObjectTagClient.create({ - name: managedTagName, + name: MANAGED_TAG_NAME, description: '', - color: tagColor, + color: TAG_COLOR, }); } @@ -216,7 +217,7 @@ export async function tagKibanaAssets({ packageTag = await savedObjectTagClient.create({ name: pkgTitle, description: '', - color: tagColor, + color: TAG_COLOR, }); } From c4373f985128410f00d3c56a08ae86beb9d7beb3 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 09:13:44 +0200 Subject: [PATCH 05/16] fixed tests, added span --- .../services/epm/kibana/assets/install.ts | 72 ++++--------------- .../services/epm/kibana/assets/tag_assets.ts | 65 +++++++++++++++++ .../services/epm/packages/install.test.ts | 4 ++ .../security_and_spaces/apis/get_all.ts | 2 +- .../functional/tests/bulk_actions.ts | 2 +- 5 files changed, 83 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 515f6a9c935ca..a3f882c7a7436 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -20,8 +20,6 @@ import { partition } from 'lodash'; import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; -import { taggableTypes } from '@kbn/saved-objects-tagging-plugin/common/constants'; - import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common'; import { getAsset, getPathParts } from '../../archive'; import { KibanaAssetType, KibanaSavedObjectType } from '../../../../types'; @@ -31,6 +29,10 @@ import { indexPatternTypes, getIndexPatternSavedObjects } from '../index_pattern import { saveKibanaAssetsRefs } from '../../packages/install'; import { deleteKibanaSavedObjectsAssets } from '../../packages/remove'; +import { withPackageSpan } from '../../packages/utils'; + +import { tagKibanaAssets } from './tag_assets'; + type SavedObjectsImporterContract = Pick; const formatImportErrorsForLog = (errors: SavedObjectsImportFailure[]) => JSON.stringify( @@ -166,68 +168,18 @@ export async function installKibanaAssetsAndReferences({ kibanaAssets, }); - await tagKibanaAssets({ - savedObjectTagAssignmentService, - savedObjectTagClient, - kibanaAssets, - pkgTitle, - }); + await withPackageSpan('Create and assign package tags', () => + tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle, + }) + ); return installedKibanaAssetsRefs; } -const TAG_COLOR = '#FFFFFF'; -const MANAGED_TAG_NAME = 'Managed'; - -export async function tagKibanaAssets({ - savedObjectTagAssignmentService, - savedObjectTagClient, - kibanaAssets, - pkgTitle, -}: { - savedObjectTagAssignmentService: IAssignmentService; - savedObjectTagClient: ITagsClient; - kibanaAssets: Record; - pkgTitle: string; -}) { - const taggableAssets = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { - if (!taggableTypes.includes(assetType as KibanaAssetType)) { - return []; - } - - if (!assets.length) { - return []; - } - - return assets; - }); - - const allTags = await savedObjectTagClient.getAll(); - let managedTag = allTags.find((tag) => tag.name === MANAGED_TAG_NAME); - if (!managedTag) { - managedTag = await savedObjectTagClient.create({ - name: MANAGED_TAG_NAME, - description: '', - color: TAG_COLOR, - }); - } - - let packageTag = allTags.find((tag) => tag.name === pkgTitle); - if (!packageTag) { - packageTag = await savedObjectTagClient.create({ - name: pkgTitle, - description: '', - color: TAG_COLOR, - }); - } - - savedObjectTagAssignmentService.updateTagAssignments({ - tags: [managedTag.id, packageTag.id], - assign: taggableAssets, - unassign: [], - }); -} - export const deleteKibanaInstalledRefs = async ( savedObjectsClient: SavedObjectsClientContract, pkgName: string, diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts new file mode 100644 index 0000000000000..8bb8c65cf28ca --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts @@ -0,0 +1,65 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { taggableTypes } from '@kbn/saved-objects-tagging-plugin/common/constants'; +import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; + +import type { KibanaAssetType } from '../../../../../common'; + +import type { ArchiveAsset } from './install'; + +const TAG_COLOR = '#FFFFFF'; +const MANAGED_TAG_NAME = 'Managed'; + +export async function tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle, +}: { + savedObjectTagAssignmentService: IAssignmentService; + savedObjectTagClient: ITagsClient; + kibanaAssets: Record; + pkgTitle: string; +}) { + const taggableAssets = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { + if (!taggableTypes.includes(assetType as KibanaAssetType)) { + return []; + } + + if (!assets.length) { + return []; + } + + return assets; + }); + + const allTags = await savedObjectTagClient.getAll(); + let managedTag = allTags.find((tag) => tag.name === MANAGED_TAG_NAME); + if (!managedTag) { + managedTag = await savedObjectTagClient.create({ + name: MANAGED_TAG_NAME, + description: '', + color: TAG_COLOR, + }); + } + + let packageTag = allTags.find((tag) => tag.name === pkgTitle); + if (!packageTag) { + packageTag = await savedObjectTagClient.create({ + name: pkgTitle, + description: '', + color: TAG_COLOR, + }); + } + + await savedObjectTagAssignmentService.updateTagAssignments({ + tags: [managedTag.id, packageTag.id], + assign: taggableAssets, + unassign: [], + }); +} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index ac53717bb2b1a..c58700716d18a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -32,6 +32,10 @@ jest.mock('../../app_context', () => { getSavedObjects: jest.fn(() => ({ createImporter: jest.fn(), })), + getSavedObjectsTagging: jest.fn(() => ({ + createInternalAssignmentService: jest.fn(), + createTagClient: jest.fn(), + })), }, }; }); diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts index 32d0a3a57e775..0300d7c0577fc 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts @@ -35,7 +35,7 @@ export default function (ftrContext: FtrProviderContext) { authorized: { httpCode: 200, expectResponse: ({ body }) => { - expect(body).to.eql({ + expect(body.tags.slice(0, 1)).to.eql({ tags: [ { id: 'default-space-tag-1', diff --git a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts index f0f2d3aa980ac..2fb347dc6c19f 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts @@ -38,7 +38,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagManagementPage.waitUntilTableIsLoaded(); const displayedTags = await tagManagementPage.getDisplayedTagNames(); - expect(displayedTags.length).to.be(3); + expect(displayedTags.length).to.be.greaterThan(2); expect(displayedTags).to.eql(['my-favorite-tag', 'tag with whitespace', 'tag-2']); }); }); From c7512b28b1cbc759fa2ee689a675237be9244473 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 10:14:04 +0200 Subject: [PATCH 06/16] added unit test to tagKibanaAssets --- .../epm/kibana/assets/tag_assets.test.ts | 115 ++++++++++++++++++ .../services/epm/kibana/assets/tag_assets.ts | 5 + 2 files changed, 120 insertions(+) create mode 100644 x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts new file mode 100644 index 0000000000000..88eebe90a2c6a --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts @@ -0,0 +1,115 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { tagKibanaAssets } from './tag_assets'; + +describe('tagKibanaAssets', () => { + const savedObjectTagAssignmentService = { + updateTagAssignments: jest.fn(), + }; + const savedObjectTagClient = { + getAll: jest.fn(), + create: jest.fn(), + }; + + beforeEach(() => { + savedObjectTagAssignmentService.updateTagAssignments.mockReset(); + savedObjectTagClient.getAll.mockReset(); + savedObjectTagClient.create.mockReset(); + }); + + it('should create Managed and System tags when tagKibanaAssets with System package', async () => { + savedObjectTagClient.getAll.mockResolvedValue([]); + savedObjectTagClient.create.mockImplementation(({ name }: { name: string }) => + Promise.resolve({ id: name.toLowerCase(), name }) + ); + const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] }; + + await tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle: 'System', + }); + + expect(savedObjectTagClient.create).toHaveBeenCalledWith({ + name: 'Managed', + description: '', + color: '#FFFFFF', + }); + expect(savedObjectTagClient.create).toHaveBeenCalledWith({ + name: 'System', + description: '', + color: '#FFFFFF', + }); + expect(savedObjectTagAssignmentService.updateTagAssignments).toHaveBeenCalledWith({ + tags: ['managed', 'system'], + assign: kibanaAssets.dashboard, + unassign: [], + }); + }); + + it('should only assign Managed and System tags when tags already exist', async () => { + savedObjectTagClient.getAll.mockResolvedValue([ + { id: 'managed', name: 'Managed' }, + { id: 'system', name: 'System' }, + ]); + const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] }; + + await tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle: 'System', + }); + + expect(savedObjectTagClient.create).not.toHaveBeenCalled(); + expect(savedObjectTagAssignmentService.updateTagAssignments).toHaveBeenCalledWith({ + tags: ['managed', 'system'], + assign: kibanaAssets.dashboard, + unassign: [], + }); + }); + + it('should skip non taggable asset types', async () => { + savedObjectTagClient.getAll.mockResolvedValue([]); + savedObjectTagClient.create.mockImplementation(({ name }: { name: string }) => + Promise.resolve({ id: name.toLowerCase(), name }) + ); + const kibanaAssets = { + dashboard: [{ id: 'dashboard1', type: 'dashboard' }], + search: [{ id: 's1', type: 'search' }], + visualization: [{ id: 'v1', type: 'visualization' }], + }; + + await tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle: 'System', + }); + + expect(savedObjectTagAssignmentService.updateTagAssignments).toHaveBeenCalledWith({ + tags: ['managed', 'system'], + assign: [...kibanaAssets.dashboard, ...kibanaAssets.visualization], + unassign: [], + }); + }); + + it('should do nothing if no taggable assets', async () => { + const kibanaAssets = { search: [{ id: 's1', type: 'search' }] }; + + await tagKibanaAssets({ + savedObjectTagAssignmentService, + savedObjectTagClient, + kibanaAssets, + pkgTitle: 'System', + }); + + expect(savedObjectTagAssignmentService.updateTagAssignments).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts index 8bb8c65cf28ca..5a0d82b9f7f8c 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts @@ -38,6 +38,11 @@ export async function tagKibanaAssets({ return assets; }); + // no assets to tag + if (taggableAssets.length === 0) { + return; + } + const allTags = await savedObjectTagClient.getAll(); let managedTag = allTags.find((tag) => tag.name === MANAGED_TAG_NAME); if (!managedTag) { From 7706aaba2c04ac69a627b79328ab109ccad46607 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 10:31:59 +0200 Subject: [PATCH 07/16] fix test --- .../api_integration/security_and_spaces/apis/get_all.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts index 0300d7c0577fc..b892b39ea6754 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts @@ -35,7 +35,7 @@ export default function (ftrContext: FtrProviderContext) { authorized: { httpCode: 200, expectResponse: ({ body }) => { - expect(body.tags.slice(0, 1)).to.eql({ + expect(body.tags.slice(0, 2)).to.eql({ tags: [ { id: 'default-space-tag-1', From d00478b28980683d0563784c7a2980785f2ffcbe Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 11:22:45 +0200 Subject: [PATCH 08/16] fix types --- .../server/services/epm/kibana/assets/tag_assets.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts index 88eebe90a2c6a..9af9a526f3d38 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts @@ -4,17 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; import { tagKibanaAssets } from './tag_assets'; describe('tagKibanaAssets', () => { const savedObjectTagAssignmentService = { updateTagAssignments: jest.fn(), - }; + } as IAssignmentService; const savedObjectTagClient = { getAll: jest.fn(), create: jest.fn(), - }; + } as ITagsClient; beforeEach(() => { savedObjectTagAssignmentService.updateTagAssignments.mockReset(); From a5fa279f475577c13e091971f26ae2cb166379d8 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 13:31:59 +0200 Subject: [PATCH 09/16] fixed tests --- .../services/epm/kibana/assets/tag_assets.test.ts | 6 ++---- .../fleet/server/services/epm/packages/install.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts index 9af9a526f3d38..7e8ed3c013723 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts @@ -4,18 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; - import { tagKibanaAssets } from './tag_assets'; describe('tagKibanaAssets', () => { const savedObjectTagAssignmentService = { updateTagAssignments: jest.fn(), - } as IAssignmentService; + } as any; const savedObjectTagClient = { getAll: jest.fn(), create: jest.fn(), - } as ITagsClient; + } as any; beforeEach(() => { savedObjectTagAssignmentService.updateTagAssignments.mockReset(); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 97d248ed7a049..d66550570d1fe 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -487,10 +487,20 @@ async function installPackageByUpload({ .getSavedObjects() .createImporter(savedObjectsClient); + const savedObjectTagAssignmentService = appContextService + .getSavedObjectsTagging() + .createInternalAssignmentService({ client: savedObjectsClient }); + + const savedObjectTagClient = appContextService + .getSavedObjectsTagging() + .createTagClient({ client: savedObjectsClient }); + // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' return await _installPackage({ savedObjectsClient, savedObjectsImporter, + savedObjectTagAssignmentService, + savedObjectTagClient, esClient, logger, installedPkg, From 3f92ebec1b1e551d4c6d6d14062ed018b9f0108c Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 13:34:02 +0200 Subject: [PATCH 10/16] fixed tests --- .../security_and_spaces/apis/get_all.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts index b892b39ea6754..9364079a4d292 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts @@ -35,22 +35,20 @@ export default function (ftrContext: FtrProviderContext) { authorized: { httpCode: 200, expectResponse: ({ body }) => { - expect(body.tags.slice(0, 2)).to.eql({ - tags: [ - { - id: 'default-space-tag-1', - name: 'tag-1', - description: 'Tag 1 in default space', - color: '#FF00FF', - }, - { - id: 'default-space-tag-2', - name: 'tag-2', - description: 'Tag 2 in default space', - color: '#77CC11', - }, - ], - }); + expect(body.tags.slice(0, 2)).to.eql([ + { + id: 'default-space-tag-1', + name: 'tag-1', + description: 'Tag 1 in default space', + color: '#FF00FF', + }, + { + id: 'default-space-tag-2', + name: 'tag-2', + description: 'Tag 2 in default space', + color: '#77CC11', + }, + ]); }, }, unauthorized: { From 43828947599bc9b7a4b0d773b3b385a89bef1f16 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 13:43:23 +0200 Subject: [PATCH 11/16] fix types --- .../server/services/epm/kibana/assets/tag_assets.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts index 7e8ed3c013723..2a63690bd3029 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts @@ -26,7 +26,7 @@ describe('tagKibanaAssets', () => { savedObjectTagClient.create.mockImplementation(({ name }: { name: string }) => Promise.resolve({ id: name.toLowerCase(), name }) ); - const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] }; + const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] } as any; await tagKibanaAssets({ savedObjectTagAssignmentService, @@ -57,7 +57,7 @@ describe('tagKibanaAssets', () => { { id: 'managed', name: 'Managed' }, { id: 'system', name: 'System' }, ]); - const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] }; + const kibanaAssets = { dashboard: [{ id: 'dashboard1', type: 'dashboard' }] } as any; await tagKibanaAssets({ savedObjectTagAssignmentService, @@ -83,7 +83,7 @@ describe('tagKibanaAssets', () => { dashboard: [{ id: 'dashboard1', type: 'dashboard' }], search: [{ id: 's1', type: 'search' }], visualization: [{ id: 'v1', type: 'visualization' }], - }; + } as any; await tagKibanaAssets({ savedObjectTagAssignmentService, @@ -100,7 +100,7 @@ describe('tagKibanaAssets', () => { }); it('should do nothing if no taggable assets', async () => { - const kibanaAssets = { search: [{ id: 's1', type: 'search' }] }; + const kibanaAssets = { search: [{ id: 's1', type: 'search' }] } as any; await tagKibanaAssets({ savedObjectTagAssignmentService, From 766ee1a5a2a253459071a294dc92822940921e2f Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 15:30:56 +0200 Subject: [PATCH 12/16] fix sot tests by loading empty kibana archive --- .../security_and_spaces/apis/get_all.ts | 33 +++++++++++-------- .../functional/tests/bulk_actions.ts | 5 ++- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts index 9364079a4d292..9414f11264a78 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts @@ -13,14 +13,17 @@ import { createTestSpaces, deleteTestSpaces, createTags, deleteTags } from './te // eslint-disable-next-line import/no-default-export export default function (ftrContext: FtrProviderContext) { const supertest = ftrContext.getService('supertestWithoutAuth'); + const esArchiver = ftrContext.getService('esArchiver'); describe('GET /api/saved_objects_tagging/tags', () => { before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); await createTestSpaces(ftrContext); }); after(async () => { await deleteTestSpaces(ftrContext); + await esArchiver.unload('test/functional/fixtures/es_archiver/empty_kibana'); }); beforeEach(async () => { @@ -35,20 +38,22 @@ export default function (ftrContext: FtrProviderContext) { authorized: { httpCode: 200, expectResponse: ({ body }) => { - expect(body.tags.slice(0, 2)).to.eql([ - { - id: 'default-space-tag-1', - name: 'tag-1', - description: 'Tag 1 in default space', - color: '#FF00FF', - }, - { - id: 'default-space-tag-2', - name: 'tag-2', - description: 'Tag 2 in default space', - color: '#77CC11', - }, - ]); + expect(body).to.eql({ + tags: [ + { + id: 'default-space-tag-1', + name: 'tag-1', + description: 'Tag 1 in default space', + color: '#FF00FF', + }, + { + id: 'default-space-tag-2', + name: 'tag-2', + description: 'Tag 2 in default space', + color: '#77CC11', + }, + ], + }); }, }, unauthorized: { diff --git a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts index 2fb347dc6c19f..505f2ef1baacc 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts @@ -13,9 +13,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'security', 'savedObjects', 'tagManagement']); const tagManagementPage = PageObjects.tagManagement; + const esArchiver = getService('esArchiver'); describe('table bulk actions', () => { beforeEach(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); await kibanaServer.importExport.load( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/data.json' ); @@ -25,6 +27,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/data.json' ); + await esArchiver.unload('test/functional/fixtures/es_archiver/empty_kibana'); }); describe('bulk delete', () => { @@ -38,7 +41,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagManagementPage.waitUntilTableIsLoaded(); const displayedTags = await tagManagementPage.getDisplayedTagNames(); - expect(displayedTags.length).to.be.greaterThan(2); + expect(displayedTags.length).to.be(3); expect(displayedTags).to.eql(['my-favorite-tag', 'tag with whitespace', 'tag-2']); }); }); From f74e91d858bf37a40e2fd9ce68900eb944cc0976 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 27 Jul 2022 17:45:00 +0200 Subject: [PATCH 13/16] added tag checking to api integration test --- .../test/fleet_api_integration/apis/package_policy/create.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts index de1b562b578b0..5753329ac34a5 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts @@ -140,6 +140,11 @@ export default function (providerContext: FtrProviderContext) { }, }) .expect(200); + const { body } = await supertest + .get(`/internal/saved_objects_tagging/tags/_find?page=1&perPage=10000`) + .expect(200); + expect(body.tags.find((tag: any) => tag.name === 'Managed').relationCount).to.be(6); + expect(body.tags.find((tag: any) => tag.name === 'For File Tests').relationCount).to.be(6); }); it('should return a 400 with an empty namespace', async function () { From 76606138b6eb5c25beda583a5a8347ec018526ed Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Thu, 28 Jul 2022 14:04:29 +0200 Subject: [PATCH 14/16] added refresh option to speed up tagging assets --- .../saved_objects_tagging_oss/common/types.ts | 8 ++++- .../services/epm/kibana/assets/install.ts | 1 + .../epm/kibana/assets/tag_assets.test.ts | 33 +++++++++++++------ .../services/epm/kibana/assets/tag_assets.ts | 30 +++++++++++------ .../common/assignments.ts | 1 + .../assignments/assignment_service.ts | 9 +++-- .../server/services/tags/tags_client.ts | 5 +-- 7 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/plugins/saved_objects_tagging_oss/common/types.ts b/src/plugins/saved_objects_tagging_oss/common/types.ts index 205f6984ed618..db7e71431a09f 100644 --- a/src/plugins/saved_objects_tagging_oss/common/types.ts +++ b/src/plugins/saved_objects_tagging_oss/common/types.ts @@ -23,8 +23,14 @@ export interface GetAllTagsOptions { asSystemRequest?: boolean; } +export interface CreateTagOptions { + id?: string; + overwrite?: boolean; + refresh?: boolean | 'wait_for'; +} + export interface ITagsClient { - create(attributes: TagAttributes): Promise; + create(attributes: TagAttributes, options?: CreateTagOptions): Promise; get(id: string): Promise; getAll(options?: GetAllTagsOptions): Promise; delete(id: string): Promise; diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index a3f882c7a7436..b4a9564e1199e 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -174,6 +174,7 @@ export async function installKibanaAssetsAndReferences({ savedObjectTagClient, kibanaAssets, pkgTitle, + pkgName, }) ); diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts index 2a63690bd3029..4d3a4e8557a68 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.test.ts @@ -33,22 +33,30 @@ describe('tagKibanaAssets', () => { savedObjectTagClient, kibanaAssets, pkgTitle: 'System', + pkgName: 'system', }); - expect(savedObjectTagClient.create).toHaveBeenCalledWith({ - name: 'Managed', - description: '', - color: '#FFFFFF', - }); - expect(savedObjectTagClient.create).toHaveBeenCalledWith({ - name: 'System', - description: '', - color: '#FFFFFF', - }); + expect(savedObjectTagClient.create).toHaveBeenCalledWith( + { + name: 'Managed', + description: '', + color: '#FFFFFF', + }, + { id: 'managed', overwrite: true, refresh: false } + ); + expect(savedObjectTagClient.create).toHaveBeenCalledWith( + { + name: 'System', + description: '', + color: '#FFFFFF', + }, + { id: 'system', overwrite: true, refresh: false } + ); expect(savedObjectTagAssignmentService.updateTagAssignments).toHaveBeenCalledWith({ tags: ['managed', 'system'], assign: kibanaAssets.dashboard, unassign: [], + refresh: false, }); }); @@ -64,6 +72,7 @@ describe('tagKibanaAssets', () => { savedObjectTagClient, kibanaAssets, pkgTitle: 'System', + pkgName: 'system', }); expect(savedObjectTagClient.create).not.toHaveBeenCalled(); @@ -71,6 +80,7 @@ describe('tagKibanaAssets', () => { tags: ['managed', 'system'], assign: kibanaAssets.dashboard, unassign: [], + refresh: false, }); }); @@ -90,12 +100,14 @@ describe('tagKibanaAssets', () => { savedObjectTagClient, kibanaAssets, pkgTitle: 'System', + pkgName: 'system', }); expect(savedObjectTagAssignmentService.updateTagAssignments).toHaveBeenCalledWith({ tags: ['managed', 'system'], assign: [...kibanaAssets.dashboard, ...kibanaAssets.visualization], unassign: [], + refresh: false, }); }); @@ -107,6 +119,7 @@ describe('tagKibanaAssets', () => { savedObjectTagClient, kibanaAssets, pkgTitle: 'System', + pkgName: 'system', }); expect(savedObjectTagAssignmentService.updateTagAssignments).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts index 5a0d82b9f7f8c..53a6df568a8c0 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/tag_assets.ts @@ -14,17 +14,20 @@ import type { ArchiveAsset } from './install'; const TAG_COLOR = '#FFFFFF'; const MANAGED_TAG_NAME = 'Managed'; +const MANAGED_TAG_ID = 'managed'; export async function tagKibanaAssets({ savedObjectTagAssignmentService, savedObjectTagClient, kibanaAssets, pkgTitle, + pkgName, }: { savedObjectTagAssignmentService: IAssignmentService; savedObjectTagClient: ITagsClient; kibanaAssets: Record; pkgTitle: string; + pkgName: string; }) { const taggableAssets = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { if (!taggableTypes.includes(assetType as KibanaAssetType)) { @@ -46,25 +49,32 @@ export async function tagKibanaAssets({ const allTags = await savedObjectTagClient.getAll(); let managedTag = allTags.find((tag) => tag.name === MANAGED_TAG_NAME); if (!managedTag) { - managedTag = await savedObjectTagClient.create({ - name: MANAGED_TAG_NAME, - description: '', - color: TAG_COLOR, - }); + managedTag = await savedObjectTagClient.create( + { + name: MANAGED_TAG_NAME, + description: '', + color: TAG_COLOR, + }, + { id: MANAGED_TAG_ID, overwrite: true, refresh: false } + ); } let packageTag = allTags.find((tag) => tag.name === pkgTitle); if (!packageTag) { - packageTag = await savedObjectTagClient.create({ - name: pkgTitle, - description: '', - color: TAG_COLOR, - }); + packageTag = await savedObjectTagClient.create( + { + name: pkgTitle, + description: '', + color: TAG_COLOR, + }, + { id: pkgName, overwrite: true, refresh: false } + ); } await savedObjectTagAssignmentService.updateTagAssignments({ tags: [managedTag.id, packageTag.id], assign: taggableAssets, unassign: [], + refresh: false, }); } diff --git a/x-pack/plugins/saved_objects_tagging/common/assignments.ts b/x-pack/plugins/saved_objects_tagging/common/assignments.ts index 7692a9151c47e..c69419b75871a 100644 --- a/x-pack/plugins/saved_objects_tagging/common/assignments.ts +++ b/x-pack/plugins/saved_objects_tagging/common/assignments.ts @@ -26,6 +26,7 @@ export interface UpdateTagAssignmentsOptions { tags: string[]; assign: ObjectReference[]; unassign: ObjectReference[]; + refresh?: boolean | 'wait_for'; } export interface FindAssignableObjectsOptions { diff --git a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts index 09a726db856c2..9aeef11d606e8 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts @@ -106,7 +106,12 @@ export class AssignmentService { }); } - public async updateTagAssignments({ tags, assign, unassign }: UpdateTagAssignmentsOptions) { + public async updateTagAssignments({ + tags, + assign, + unassign, + refresh, + }: UpdateTagAssignmentsOptions) { const updatedTypes = uniq([...assign, ...unassign].map(({ type }) => type)); const untaggableTypes = difference(updatedTypes, taggableTypes); @@ -149,7 +154,7 @@ export class AssignmentService { }; }); - await this.soClient.bulkUpdate(updatedObjects); + await this.soClient.bulkUpdate(updatedObjects, { refresh }); } } diff --git a/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.ts b/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.ts index 5535e334c06fb..23e1da56d6143 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsClientContract } from '@kbn/core/server'; +import { CreateTagOptions } from '@kbn/saved-objects-tagging-oss-plugin/common/types'; import { TagSavedObject, TagAttributes, ITagsClient } from '../../../common/types'; import { tagSavedObjectTypeName } from '../../../common/constants'; import { TagValidationError } from './errors'; @@ -24,12 +25,12 @@ export class TagsClient implements ITagsClient { this.soClient = client; } - public async create(attributes: TagAttributes) { + public async create(attributes: TagAttributes, options?: CreateTagOptions) { const validation = validateTag(attributes); if (!validation.valid) { throw new TagValidationError('Error validating tag attributes', validation); } - const raw = await this.soClient.create(this.type, attributes); + const raw = await this.soClient.create(this.type, attributes, options); return savedObjectToTag(raw); } From d288690f0f0fc5369b13210f37494c36c633e848 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Thu, 28 Jul 2022 15:04:52 +0200 Subject: [PATCH 15/16] fixed tests --- .../assignments/assignment_service.test.ts | 53 +++++++++++++++---- .../server/services/tags/tags_client.test.ts | 15 +++++- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts index 61e4fbc2fb651..ec1a6575d2e3d 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts @@ -154,21 +154,56 @@ describe('AssignmentService', () => { }); expect(savedObjectClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(savedObjectClient.bulkUpdate).toHaveBeenCalledWith([ + expect(savedObjectClient.bulkUpdate).toHaveBeenCalledWith( + [ + { + type: 'dashboard', + id: 'dash-1', + attributes: {}, + references: [createReference('tag', 'tag-1'), createReference('tag', 'tag-2')], + }, + { + type: 'map', + id: 'map-1', + attributes: {}, + references: [createReference('dashboard', 'dash-1')], + }, + ], + { refresh: undefined } + ); + }); + }); + + it('calls `soClient.bulkUpdate` to update the references with refresh false', async () => { + savedObjectClient.bulkGet.mockResolvedValue({ + saved_objects: [ + createSavedObject({ + type: 'dashboard', + id: 'dash-1', + references: [], + }), + ], + }); + getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + + await service.updateTagAssignments({ + tags: ['tag-1', 'tag-2'], + assign: [{ type: 'dashboard', id: 'dash-1' }], + unassign: [], + refresh: false, + }); + + expect(savedObjectClient.bulkUpdate).toHaveBeenCalledWith( + [ { type: 'dashboard', id: 'dash-1', attributes: {}, references: [createReference('tag', 'tag-1'), createReference('tag', 'tag-2')], }, - { - type: 'map', - id: 'map-1', - attributes: {}, - references: [createReference('dashboard', 'dash-1')], - }, - ]); - }); + ], + { refresh: false } + ); }); describe('#findAssignableObjects', () => { diff --git a/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.test.ts b/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.test.ts index 6d3b6011cac99..7743b109dbd61 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.test.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/tags/tags_client.test.ts @@ -58,7 +58,20 @@ describe('TagsClient', () => { await tagsClient.create(attributes); expect(soClient.create).toHaveBeenCalledTimes(1); - expect(soClient.create).toHaveBeenCalledWith('tag', attributes); + expect(soClient.create).toHaveBeenCalledWith('tag', attributes, undefined); + }); + + it('calls `soClient.create` with options', async () => { + const attributes = createAttributes(); + + await tagsClient.create(attributes, { id: '1', overwrite: true, refresh: false }); + + expect(soClient.create).toHaveBeenCalledTimes(1); + expect(soClient.create).toHaveBeenCalledWith('tag', attributes, { + id: '1', + overwrite: true, + refresh: false, + }); }); it('converts the object returned from the soClient to a `Tag`', async () => { From 0c9f93e0a2611a99d3ff52c02300a1e751354fd7 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Thu, 28 Jul 2022 16:19:27 +0200 Subject: [PATCH 16/16] workaround to prevent installing fleet packages in SOT functional tests --- .../api_integration/security_and_spaces/apis/get_all.ts | 3 --- .../api_integration/security_and_spaces/config.ts | 1 + x-pack/test/saved_object_tagging/functional/config.ts | 5 ++++- .../saved_object_tagging/functional/tests/bulk_actions.ts | 3 --- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts index 9414f11264a78..32d0a3a57e775 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/get_all.ts @@ -13,17 +13,14 @@ import { createTestSpaces, deleteTestSpaces, createTags, deleteTags } from './te // eslint-disable-next-line import/no-default-export export default function (ftrContext: FtrProviderContext) { const supertest = ftrContext.getService('supertestWithoutAuth'); - const esArchiver = ftrContext.getService('esArchiver'); describe('GET /api/saved_objects_tagging/tags', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); await createTestSpaces(ftrContext); }); after(async () => { await deleteTestSpaces(ftrContext); - await esArchiver.unload('test/functional/fixtures/es_archiver/empty_kibana'); }); beforeEach(async () => { diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/config.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/config.ts index 0e2ecc8c48ebf..e52664d02df64 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/config.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/config.ts @@ -31,6 +31,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { serverArgs: [ ...apiIntegrationConfig.get('kbnTestServer.serverArgs'), '--server.xsrf.disableProtection=true', + `--xpack.fleet.registryUrl=http://localhost:12345`, // setting to invalid registry url to prevent installing preconfigured packages ], }, }; diff --git a/x-pack/test/saved_object_tagging/functional/config.ts b/x-pack/test/saved_object_tagging/functional/config.ts index 1c40864f2fa02..cdf637cb40c66 100644 --- a/x-pack/test/saved_object_tagging/functional/config.ts +++ b/x-pack/test/saved_object_tagging/functional/config.ts @@ -33,7 +33,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { kbnTestServer: { ...kibanaFunctionalConfig.get('kbnTestServer'), - serverArgs: [...kibanaFunctionalConfig.get('kbnTestServer.serverArgs')], + serverArgs: [ + ...kibanaFunctionalConfig.get('kbnTestServer.serverArgs'), + `--xpack.fleet.registryUrl=http://localhost:12345`, // setting to invalid registry url to prevent installing preconfigured packages + ], }, }; } diff --git a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts index 505f2ef1baacc..f0f2d3aa980ac 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts @@ -13,11 +13,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'security', 'savedObjects', 'tagManagement']); const tagManagementPage = PageObjects.tagManagement; - const esArchiver = getService('esArchiver'); describe('table bulk actions', () => { beforeEach(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); await kibanaServer.importExport.load( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/data.json' ); @@ -27,7 +25,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/data.json' ); - await esArchiver.unload('test/functional/fixtures/es_archiver/empty_kibana'); }); describe('bulk delete', () => {