diff --git a/x-pack/plugins/security_solution/public/detections/index.ts b/x-pack/plugins/security_solution/public/detections/index.ts index 77d131377f9f5..1f9ce0b6fe012 100644 --- a/x-pack/plugins/security_solution/public/detections/index.ts +++ b/x-pack/plugins/security_solution/public/detections/index.ts @@ -12,6 +12,7 @@ import { getDataTablesInStorageByIds } from '../timelines/containers/local_stora import { routes } from './routes'; import type { SecuritySubPlugin } from '../app/types'; import { runDetectionMigrations } from './migrations'; +import type { StartPlugins } from '../types'; export const DETECTIONS_TABLE_IDS: TableIdLiteral[] = [ TableId.alertsOnRuleDetailsPage, @@ -21,8 +22,8 @@ export const DETECTIONS_TABLE_IDS: TableIdLiteral[] = [ export class Detections { public setup() {} - public start(storage: Storage): SecuritySubPlugin { - runDetectionMigrations(); + public async start(storage: Storage, plugins: StartPlugins): Promise { + await runDetectionMigrations(storage, plugins); return { storageDataTables: { diff --git a/x-pack/plugins/security_solution/public/detections/migrations.ts b/x-pack/plugins/security_solution/public/detections/migrations.ts index 81009f63747a6..324abc443cb04 100644 --- a/x-pack/plugins/security_solution/public/detections/migrations.ts +++ b/x-pack/plugins/security_solution/public/detections/migrations.ts @@ -5,16 +5,21 @@ * 2.0. */ -import { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { Storage } from '@kbn/kibana-utils-plugin/public'; import { migrateAlertPageControlsTo816 } from '../timelines/containers/local_storage/migrate_alert_page_controls'; +import type { StartPlugins } from '../types'; -type LocalStorageMigrator = (storage: Storage) => void; +/* Migrator could be sync or async */ +type LocalStorageMigrator = (storage: Storage, plugins: StartPlugins) => void | Promise; -const runLocalStorageMigration = (fn: LocalStorageMigrator) => { - const storage = new Storage(localStorage); - fn(storage); +const getLocalStorageMigrationRunner = (storage: Storage, plugins: StartPlugins) => { + const runLocalStorageMigration = async (fn: LocalStorageMigrator) => { + await fn(storage, plugins); + }; + return runLocalStorageMigration; }; -export const runDetectionMigrations = () => { - runLocalStorageMigration(migrateAlertPageControlsTo816); +export const runDetectionMigrations = async (storage: Storage, plugins: StartPlugins) => { + const runLocalStorageMigration = getLocalStorageMigrationRunner(storage, plugins); + await runLocalStorageMigration(migrateAlertPageControlsTo816); }; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 17f1ba842f8cb..b20e645d71c2c 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -256,8 +256,9 @@ export class Plugin implements IPlugin { const subPlugins = await this.createSubPlugins(); + const alerts = await subPlugins.alerts.start(storage, plugins); return { - alerts: subPlugins.alerts.start(storage), + alerts, attackDiscovery: subPlugins.attackDiscovery.start(), cases: subPlugins.cases.start(), cloudDefend: subPlugins.cloudDefend.start(), diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_contorls.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_contorls.test.ts index 575d5bcd6dab3..de0e085fb4ed9 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_contorls.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_contorls.test.ts @@ -7,9 +7,10 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { - PAGE_FILTER_STORAGE_KEY, + GET_PAGE_FILTER_STORAGE_KEY, migrateAlertPageControlsTo816, } from './migrate_alert_page_controls'; +import type { StartPlugins } from '../../../types'; const OLD_FORMAT = { viewMode: 'view', @@ -216,40 +217,94 @@ const NEW_FORMAT = { }; const storage = new Storage(localStorage); +const mockPlugins = { + spaces: { + getActiveSpace: jest.fn().mockResolvedValue({ id: 'default' }), + }, +} as unknown as StartPlugins; + describe('migrateAlertPageControlsTo816', () => { beforeEach(() => { storage.clear(); }); - it('should migrate the old format to the new format', () => { - storage.set(PAGE_FILTER_STORAGE_KEY, OLD_FORMAT); - migrateAlertPageControlsTo816(storage); - const migrated = storage.get(PAGE_FILTER_STORAGE_KEY); - expect(migrated).toMatchObject(NEW_FORMAT); - }); + describe('Default space', () => { + beforeEach(() => { + if (mockPlugins.spaces?.getActiveSpace) { + mockPlugins.spaces.getActiveSpace = jest.fn().mockResolvedValue({ id: 'default' }); + } + }); + it('should migrate the old format to the new format', async () => { + storage.set(GET_PAGE_FILTER_STORAGE_KEY(), OLD_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY()); + expect(migrated).toMatchObject(NEW_FORMAT); + }); - it('should be a no-op if the new format already exists', () => { - storage.set(PAGE_FILTER_STORAGE_KEY, NEW_FORMAT); - migrateAlertPageControlsTo816(storage); - const migrated = storage.get(PAGE_FILTER_STORAGE_KEY); - expect(migrated).toMatchObject(NEW_FORMAT); - }); + it('should be a no-op if the new format already exists', async () => { + storage.set(GET_PAGE_FILTER_STORAGE_KEY(), NEW_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY()); + expect(migrated).toMatchObject(NEW_FORMAT); + }); - it('should be a no-op if no value is present in localstorage for page filters ', () => { - migrateAlertPageControlsTo816(storage); - const migrated = storage.get(PAGE_FILTER_STORAGE_KEY); - expect(migrated).toBeNull(); + it('should be a no-op if no value is present in localstorage for page filters ', async () => { + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY()); + expect(migrated).toBeNull(); + }); + + it('should convert custom old format correctly', async () => { + const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT); + MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true; + MODIFIED_OLD_FORMAT.chainingSystem = 'NONE'; + storage.set(GET_PAGE_FILTER_STORAGE_KEY(), MODIFIED_OLD_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY()); + const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT); + EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true; + EXPECTED_NEW_FORMAT.chainingSystem = 'NONE'; + expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT); + }); }); - it('should convert custom old format correctly', () => { - const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT); - MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true; - MODIFIED_OLD_FORMAT.chainingSystem = 'NONE'; - storage.set(PAGE_FILTER_STORAGE_KEY, MODIFIED_OLD_FORMAT); - migrateAlertPageControlsTo816(storage); - const migrated = storage.get(PAGE_FILTER_STORAGE_KEY); - const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT); - EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true; - EXPECTED_NEW_FORMAT.chainingSystem = 'NONE'; - expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT); + describe('Non Default space', () => { + const nonDefaultSpaceId = 'space1'; + beforeEach(() => { + if (mockPlugins.spaces?.getActiveSpace) { + mockPlugins.spaces.getActiveSpace = jest.fn().mockResolvedValue({ id: nonDefaultSpaceId }); + } + }); + it('should migrate the old format to the new format', async () => { + storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), OLD_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId)); + expect(migrated).toMatchObject(NEW_FORMAT); + }); + + it('should be a no-op if the new format already exists', async () => { + storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), NEW_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId)); + expect(migrated).toMatchObject(NEW_FORMAT); + }); + + it('should be a no-op if no value is present in localstorage for page filters ', async () => { + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId)); + expect(migrated).toBeNull(); + }); + + it('should convert custom old format correctly', async () => { + const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT); + MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true; + MODIFIED_OLD_FORMAT.chainingSystem = 'NONE'; + storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), MODIFIED_OLD_FORMAT); + await migrateAlertPageControlsTo816(storage, mockPlugins); + const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId)); + const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT); + EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true; + EXPECTED_NEW_FORMAT.chainingSystem = 'NONE'; + expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_controls.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_controls.ts index ac46d42035155..7152ef7738bcc 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_controls.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/migrate_alert_page_controls.ts @@ -7,8 +7,10 @@ import type { DefaultControlState, ControlGroupRuntimeState } from '@kbn/controls-plugin/common'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { StartPlugins } from '../../../types'; -export const PAGE_FILTER_STORAGE_KEY = 'siem.default.pageFilters'; +export const GET_PAGE_FILTER_STORAGE_KEY = (spaceId: string = 'default') => + `siem.${spaceId}.pageFilters`; interface OldFormat { viewMode: string; @@ -96,8 +98,11 @@ interface NewFormatExplicitInput { * This migration script is to migrate the old format to the new format. * */ -export function migrateAlertPageControlsTo816(storage: Storage) { - const oldFormat: OldFormat = storage.get(PAGE_FILTER_STORAGE_KEY); +export async function migrateAlertPageControlsTo816(storage: Storage, plugins: StartPlugins) { + const space = await plugins.spaces?.getActiveSpace(); + const spaceId = space?.id ?? 'default'; + const storageKey = GET_PAGE_FILTER_STORAGE_KEY(spaceId); + const oldFormat: OldFormat = storage.get(GET_PAGE_FILTER_STORAGE_KEY(spaceId)); if (oldFormat && Object.keys(oldFormat).includes('panels')) { // Only run when it is old format const newFormat: ControlGroupRuntimeState = { @@ -131,6 +136,6 @@ export function migrateAlertPageControlsTo816(storage: Storage) { }; } - storage.set(PAGE_FILTER_STORAGE_KEY, newFormat); + storage.set(storageKey, newFormat); } } diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 55fce6a46dba8..6642e02d5ecd6 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -248,7 +248,7 @@ export interface SubPlugins { // TODO: find a better way to defined these types export interface StartedSubPlugins { [CASES_SUB_PLUGIN_KEY]: ReturnType; - alerts: ReturnType; + alerts: Awaited>; attackDiscovery: ReturnType; cloudDefend: ReturnType; cloudSecurityPosture: ReturnType;