diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.test.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.test.ts index eede588249688..0ec056998e1b7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.test.ts @@ -12,7 +12,7 @@ * We use Jest for assertions and mocking. We also use Jest’s fake timers to simulate the polling loop. */ -import type { CoreStart, Toast } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import { firstValueFrom } from 'rxjs'; import { SiemMigrationTaskStatus } from '../../../../common/siem_migrations/constants'; import type { StartPluginsDependencies } from '../../../types'; @@ -329,88 +329,4 @@ describe('SiemMigrationsServiceBase', () => { }); }); }); - - describe('removeFinishedMigrationsNotification', () => { - let mockToastRemove: jest.Mock; - - beforeEach(() => { - mockToastRemove = jest.fn(); - mockNotifications.toasts.remove = mockToastRemove; - }); - - it('should remove specific migration notification when migrationId is provided', () => { - const migrationId = 'mig-1'; - const mockToast = { id: 'toast-1', title: 'Migration Complete' }; - - service.toastsByMigrationId[migrationId] = mockToast as Toast; - - service.removeFinishedMigrationsNotification(migrationId); - - expect(mockToastRemove).toHaveBeenCalledWith(mockToast); - expect(service.toastsByMigrationId[migrationId]).toBeUndefined(); - }); - - it('should not call remove when migrationId is provided but no toast exists', () => { - const migrationId = 'mig-1'; - - service.toastsByMigrationId = {}; - - service.removeFinishedMigrationsNotification(migrationId); - - expect(mockToastRemove).not.toHaveBeenCalled(); - }); - - it('should remove all migration notifications when no migrationId is provided', () => { - const mockToast1 = { id: 'toast-1', title: 'Migration 1 Complete' }; - const mockToast2 = { id: 'toast-2', title: 'Migration 2 Complete' }; - - service.toastsByMigrationId = { - 'mig-1': mockToast1 as Toast, - 'mig-2': mockToast2 as Toast, - }; - - service.removeFinishedMigrationsNotification(); - - expect(mockToastRemove).toHaveBeenCalledTimes(2); - expect(mockToastRemove).toHaveBeenCalledWith(mockToast1); - expect(mockToastRemove).toHaveBeenCalledWith(mockToast2); - expect(service.toastsByMigrationId).toEqual({}); - }); - - it('should not call remove when no migrationId is provided and no toasts exist', () => { - service.toastsByMigrationId = {}; - - service.removeFinishedMigrationsNotification(); - - expect(mockToastRemove).not.toHaveBeenCalled(); - }); - - it('should handle empty toastsByMigrationId object when no migrationId is provided', () => { - service.toastsByMigrationId = {}; - - service.removeFinishedMigrationsNotification(); - - expect(mockToastRemove).not.toHaveBeenCalled(); - expect(service.toastsByMigrationId).toEqual({}); - }); - - it('should only remove the specific migration toast when migrationId is provided, leaving others intact', () => { - const migrationId = 'mig-1'; - const mockToast1 = { id: 'toast-1', title: 'Migration 1 Complete' }; - const mockToast2 = { id: 'toast-2', title: 'Migration 2 Complete' }; - - service.toastsByMigrationId = { - 'mig-1': mockToast1 as Toast, - 'mig-2': mockToast2 as Toast, - }; - - service.removeFinishedMigrationsNotification(migrationId); - - expect(mockToastRemove).toHaveBeenCalledTimes(1); - expect(mockToastRemove).toHaveBeenCalledWith(mockToast1); - expect(service.toastsByMigrationId).toEqual({ - 'mig-2': mockToast2, - }); - }); - }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.ts index f574ae573dce1..b4a8c0f57450b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/service/migrations_service_base.ts @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import { BehaviorSubject, distinctUntilChanged, type Observable } from 'rxjs'; -import type { CoreStart, Toast } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import type { TraceOptions } from '@kbn/elastic-assistant/impl/assistant/types'; import { DEFAULT_ASSISTANT_NAMESPACE, @@ -43,7 +43,6 @@ export abstract class SiemMigrationsServiceBase { private isPolling = false; public connectorIdStorage: MigrationsStorage; public traceOptionsStorage: MigrationsStorage; - public toastsByMigrationId: Record; constructor( protected readonly core: CoreStart, @@ -56,7 +55,7 @@ export abstract class SiemMigrationsServiceBase { }); this.latestStats$ = new BehaviorSubject(null); - this.toastsByMigrationId = {}; + this.plugins.spaces.getActiveSpace().then((space) => { this.connectorIdStorage.setSpaceId(space.id); this.startPolling(); @@ -204,18 +203,4 @@ export abstract class SiemMigrationsServiceBase { } } while (pendingMigrationIds.length > 0); } - - public removeFinishedMigrationsNotification(migrationId?: string) { - if (migrationId && this.toastsByMigrationId[migrationId]) { - this.core.notifications.toasts.remove(this.toastsByMigrationId[migrationId]); - delete this.toastsByMigrationId[migrationId]; - } else { - if (Object.values(this.toastsByMigrationId).length > 0) { - Object.values(this.toastsByMigrationId).forEach((toast) => { - this.core.notifications.toasts.remove(toast); - }); - this.toastsByMigrationId = {}; - } - } - } } diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/dashboards/service/dashboard_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/dashboards/service/dashboard_migrations_service.ts index 3374f4076da1a..3e4ff858d65ba 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/dashboards/service/dashboard_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/dashboards/service/dashboard_migrations_service.ts @@ -21,7 +21,7 @@ import * as api from '../api'; import { getMissingCapabilitiesToast } from '../../common/service/notifications/missing_capabilities_notification'; import { getNoConnectorToast } from '../../common/service/notifications/no_connector_notification'; import { SiemMigrationTaskStatus } from '../../../../common/siem_migrations/constants'; -import { getSuccessToast } from './notification/success_notification'; +import { raiseSuccessToast } from './notification/success_notification'; import type { CapabilitiesLevel, MissingCapability } from '../../common/service/capabilities'; import { getMissingCapabilitiesChecker } from '../../common/service/capabilities'; import { requiredDashboardMigrationCapabilities } from './capabilities'; @@ -221,9 +221,7 @@ export class SiemDashboardMigrationsService extends SiemMigrationsServiceBase ({ - color: 'success', - iconType: 'check', - toastLifeTimeMs: 1000 * 60 * 30, // 30 minutes - title: i18n.translate( - 'xpack.securitySolution.siemMigrations.dashboardsService.polling.successTitle', - { - defaultMessage: 'Dashboards translation complete.', - } - ), - text: toMountPoint( - - - , - core - ), -}); + core: CoreStart +): void => { + const toast = core.notifications.toasts.addSuccess({ + color: 'success', + iconType: 'check', + toastLifeTimeMs: 1000 * 60 * 30, // 30 minutes + title: i18n.translate( + 'xpack.securitySolution.siemMigrations.dashboardsService.polling.successTitle', + { + defaultMessage: 'Dashboards translation complete.', + } + ), + text: toMountPoint( + + core.notifications.toasts.remove(toast)} + /> + , + core + ), + }); +}; const SuccessToastContent: React.FC<{ migrationStats: DashboardMigrationTaskStats; - service: SiemDashboardMigrationsService; -}> = ({ migrationStats, service }) => { + dismissHandler: () => void; +}> = ({ migrationStats, dismissHandler }) => { const { navigateTo, getAppUrl } = useNavigation(); const navParams = useMemo(() => { @@ -59,9 +61,9 @@ const SuccessToastContent: React.FC<{ deepLinkId: SecurityPageName.siemMigrationsDashboards, path: migrationStats.id, }); - service.removeFinishedMigrationsNotification(migrationStats.id); + dismissHandler(); }, - [navigateTo, migrationStats.id, service] + [navigateTo, migrationStats.id, dismissHandler] ); const url = useMemo(() => getAppUrl(navParams), [getAppUrl, navParams]); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.test.tsx index 902fbd6ba1ef4..7b6dd8b04357c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { coreMock } from '@kbn/core/public/mocks'; import { useNavigation } from '@kbn/security-solution-navigation'; -import { SuccessToastContent, getSuccessToast } from './success_notification'; +import { SuccessToastContent, raiseSuccessToast } from './success_notification'; import { getRuleMigrationStatsMock } from '../../__mocks__'; import { TestProviders } from '../../../../common/mock'; @@ -69,7 +69,7 @@ describe('Success Notification', () => { describe('getSuccessToast', () => { it('returns a toast object with the correct properties', () => { const migration = getRuleMigrationStatsMock(); - const toast = getSuccessToast(migration, coreMock.createStart()); + const toast = raiseSuccessToast(migration, coreMock.createStart()); expect(toast).toEqual({ color: 'success', diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.tsx index f670844e2625e..63df4b4750c51 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/notification/success_notification.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useCallback } from 'react'; +import React from 'react'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; import { i18n } from '@kbn/i18n'; import { @@ -13,54 +13,47 @@ import { useNavigation, NavigationProvider, } from '@kbn/security-solution-navigation'; -import type { ToastInput } from '@kbn/core-notifications-browser'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { RuleMigrationStats } from '../../types'; -import type { SiemRulesMigrationsService } from '../rule_migrations_service'; -export const getSuccessToast = ( - migration: RuleMigrationStats, - core: CoreStart, - service: SiemRulesMigrationsService -): ToastInput => ({ - color: 'success', - iconType: 'check', - toastLifeTimeMs: 1000 * 60 * 30, // 30 minutes - title: i18n.translate('xpack.securitySolution.siemMigrations.rulesService.polling.successTitle', { - defaultMessage: 'Rules translation complete.', - }), - text: toMountPoint( - - - , - core - ), -}); +export const raiseSuccessToast = (migration: RuleMigrationStats, core: CoreStart) => { + const toast = core.notifications.toasts.addSuccess({ + color: 'success', + iconType: 'check', + toastLifeTimeMs: 1000 * 60 * 30, // 30 minutes + title: i18n.translate( + 'xpack.securitySolution.siemMigrations.rulesService.polling.successTitle', + { + defaultMessage: 'Rules translation complete.', + } + ), + text: toMountPoint( + + core.notifications.toasts.remove(toast)} + /> + , + core + ), + }); +}; export const SuccessToastContent: React.FC<{ migration: RuleMigrationStats; - service: SiemRulesMigrationsService; -}> = ({ migration, service }) => { - const { navigateTo, getAppUrl } = useNavigation(); + dismissHandler: () => void; +}> = ({ migration, dismissHandler }) => { + const navigation = { deepLinkId: SecurityPageName.siemMigrationsRules, path: migration.id }; - const navParams = useMemo(() => { - return { deepLinkId: SecurityPageName.siemMigrationsRules, path: migration.id }; - }, [migration.id]); - - const onClick: React.MouseEventHandler = useCallback( - (ev) => { - ev.preventDefault(); - navigateTo({ - deepLinkId: SecurityPageName.siemMigrationsRules, - path: migration.id, - }); - service.removeFinishedMigrationsNotification(migration.id); - }, - [navigateTo, migration.id, service] - ); - const url = useMemo(() => getAppUrl(navParams), [getAppUrl, navParams]); + const { navigateTo, getAppUrl } = useNavigation(); + const onClick: React.MouseEventHandler = (ev) => { + ev.preventDefault(); + navigateTo(navigation); + dismissHandler(); + }; + const url = getAppUrl(navigation); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts index e5b3c6cd0ce86..1de46051cd29c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts @@ -26,7 +26,7 @@ import { getNoConnectorToast, } from '../../common/service'; import type { GetMigrationStatsParams, GetMigrationsStatsAllParams } from '../../common/types'; -import { getSuccessToast } from './notification/success_notification'; +import { raiseSuccessToast } from './notification/success_notification'; import { START_STOP_POLLING_SLEEP_SECONDS } from '../../common/constants'; const CREATE_MIGRATION_BODY_BATCH_SIZE = 50; @@ -238,8 +238,6 @@ export class SiemRulesMigrationsService extends SiemMigrationsServiceBase