From 554e2333e3fcaa9eac7456e30855ceb410356ccb Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 5 Mar 2025 15:31:17 +0100 Subject: [PATCH 1/6] fix: implementation complete. Tests remaining --- .../siem_migrations/rules/pages/empty.tsx | 58 +++++++++++++++++++ .../siem_migrations/rules/pages/index.tsx | 12 ++-- .../rules/pages/page_title/index.tsx | 2 +- .../pages/{page_title => }/translations.ts | 22 +++++++ 4 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx rename x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/{page_title => }/translations.ts (56%) diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx new file mode 100644 index 0000000000000..38a112037dd70 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx @@ -0,0 +1,58 @@ +/* + * 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 React, { useCallback } from 'react'; +import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import { SecurityPageName } from '@kbn/deeplinks-security'; +import { css } from '@emotion/react'; +import { useNavigation } from '../../../common/lib/kibana'; +import * as i18n from './translations'; + +export const EmptyMigrationRulesPage = () => { + const { navigateTo } = useNavigation(); + + const navigateToStartMigrationCallback = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.landing, path: 'siem_migrations#start' }); + }, [navigateTo]); + + return ( + + + + + {i18n.TRANSLATED_RULES_EMPTY_PAGE_TITLE} + + } + actions={ + + {i18n.TRANSLATED_RULES_EMPTY_PAGE_CTA} + + } + iconType={'logoSecurity'} + body={ + + {i18n.TRANSLATED_RULES_EMPTY_PAGE_MESSAGE} + + } + /> + + + + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx index 01a414b66670b..ae5ac077a0c13 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx @@ -30,6 +30,7 @@ import { useInvalidateGetMigrationTranslationStats } from '../logic/use_get_migr import { useGetIntegrations } from '../service/hooks/use_get_integrations'; import { PageTitle } from './page_title'; import { RuleMigrationsUploadMissingPanel } from '../components/migration_status_panels/upload_missing_panel'; +import { EmptyMigrationRulesPage } from './empty'; type MigrationRulesPageProps = RouteComponentProps<{ migrationId?: string }>; @@ -54,13 +55,7 @@ export const MigrationRulesPage: React.FC = React.memo( }, [getIntegrations]); useEffect(() => { - if (isLoading) { - return; - } - - // Navigate to landing page if there are no migrations - if (!ruleMigrationsStats.length) { - navigateTo({ deepLinkId: SecurityPageName.landing, path: 'siem_migrations' }); + if (isLoading || ruleMigrationsStats.length === 0) { return; } @@ -96,6 +91,9 @@ export const MigrationRulesPage: React.FC = React.memo( const pageTitle = useMemo(() => , []); const content = useMemo(() => { + if (ruleMigrationsStats.length === 0 && !migrationId) { + return ; + } const migrationStats = ruleMigrationsStats.find((stats) => stats.id === migrationId); if (!migrationId || !migrationStats) { return ; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/index.tsx index 4ac2821949f2b..b4f3be4b24c64 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/index.tsx @@ -9,7 +9,7 @@ import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiTitle, useEuiTheme } from ' import { css } from '@emotion/react'; import React from 'react'; -import * as i18n from './translations'; +import * as i18n from '../translations'; export const PageTitle: React.FC = React.memo(() => { const { euiTheme } = useEuiTheme(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.ts similarity index 56% rename from x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts rename to x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.ts index fead59a60e2a0..75f6c057ef9b6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.ts @@ -25,3 +25,25 @@ export const BETA_TOOLTIP = i18n.translate( 'This functionality is in technical preview and is subject to change. Please use SIEM Migrations with caution in production environments.', } ); + +export const TRANSLATED_RULES_EMPTY_PAGE_MESSAGE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.emptyPageMessage', + { + defaultMessage: + 'Translate your existing Splunk Rules with Elastic Automatic Migration. Got to SIEM rule migration for step-by-step guidance.', + } +); + +export const TRANSLATED_RULES_EMPTY_PAGE_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.emptyPageTitle', + { + defaultMessage: 'No migrations to View', + } +); + +export const TRANSLATED_RULES_EMPTY_PAGE_CTA = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.emptyPageCta', + { + defaultMessage: 'Start SIEM rule Migration', + } +); From cae800713c8d513b9a1b99a53210e5f399bfbeb9 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Thu, 6 Mar 2025 18:04:26 +0100 Subject: [PATCH 2/6] tests: integration tests Translated Rules Page --- .../common/mocks/migration_result.data.ts | 146 ++++++++++ .../rules/components/rules_table/index.tsx | 7 +- .../components/rules_table_columns/name.tsx | 2 +- .../components/unknown_migration/index.tsx | 1 + .../rules/pages/index.test.tsx | 252 ++++++++++++++++++ .../siem_migrations/rules/pages/index.tsx | 3 +- 6 files changed, 408 insertions(+), 3 deletions(-) create mode 100644 x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/mocks/migration_result.data.ts create mode 100644 x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.test.tsx diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/mocks/migration_result.data.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/mocks/migration_result.data.ts new file mode 100644 index 0000000000000..f59214beef8e4 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/common/mocks/migration_result.data.ts @@ -0,0 +1,146 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import { SiemMigrationTaskStatus } from '../../../../common/siem_migrations/constants'; +import type { + GetRuleMigrationResponse, + GetRuleMigrationTranslationStatsResponse, +} from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; +import type { RuleMigrationStats } from '../../rules/types'; + +const getMockMigrationResultRule = ({ + migrationId, + translationResult = 'full', + status = 'completed', +}: { + migrationId: string; + status?: GetRuleMigrationResponse['data'][number]['status']; + translationResult?: GetRuleMigrationResponse['data'][number]['translation_result']; +}): GetRuleMigrationResponse['data'][number] => { + const ruleId = uuidv4(); + return { + migration_id: migrationId, + original_rule: { + id: ruleId, + vendor: 'splunk', + title: ` 'Rule Title - ${ruleId}'`, + description: `Rule Title Description - ${ruleId}`, + query: 'some query', + query_language: 'spl', + }, + '@timestamp': '2025-03-06T15:00:01.036Z', + status, + created_by: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + updated_by: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + updated_at: '2025-03-06T15:01:37.321Z', + comments: [ + { + created_at: '2025-03-06T15:01:06.390Z', + message: '## Prebuilt Rule Matching Summary\nNo related prebuilt rule found.', + created_by: 'assistant', + }, + ], + translation_result: translationResult, + elastic_rule: { + severity: 'low', + risk_score: 21, + query: + 'FROM index metadata _id,_version,_index\n| WHERE MATCH(host.hostname, "vendor_sales")', + description: `Converted Splunk Rule to Elastic Rule - ${ruleId}`, + query_language: 'esql', + title: `Converted Splunk Rule - ${ruleId}`, + integration_ids: ['endpoint'], + }, + id: ruleId, + }; +}; + +export const mockedMigrationLatestStatsData: RuleMigrationStats[] = [ + { + id: '1', + number: 1, + status: SiemMigrationTaskStatus.FINISHED, + rules: { + total: 1, + pending: 0, + processing: 0, + completed: 1, + failed: 0, + }, + last_updated_at: '2025-03-06T15:01:37.321Z', + created_at: '2025-03-06T15:01:37.321Z', + }, + { + id: '2', + number: 2, + status: SiemMigrationTaskStatus.FINISHED, + rules: { + total: 2, + pending: 0, + processing: 0, + completed: 2, + failed: 0, + }, + + created_at: '2025-03-06T15:01:37.321Z', + last_updated_at: '2025-03-06T15:01:37.321Z', + }, +]; + +export const mockedMigrationResultsObj: Record = { + '1': { + total: 2, + data: [ + getMockMigrationResultRule({ + migrationId: '1', + translationResult: 'full', + status: 'completed', + }), + getMockMigrationResultRule({ + migrationId: '1', + translationResult: 'untranslatable', + status: 'failed', + }), + ], + }, +}; + +export const mockedMigrationTranslationStats: Record< + string, + GetRuleMigrationTranslationStatsResponse +> = { + '1': { + id: '1', + rules: { + total: 2, + success: { + total: 2, + result: { + full: 1, + partial: 0, + untranslatable: 1, + }, + installable: 1, + prebuilt: 0, + }, + failed: 0, + }, + }, +}; + +const mockRefreshStats = jest.fn(); +export const mockedLatestStatsEmpty = { + data: [], + isLoading: false, + refreshStats: mockRefreshStats, +}; + +export const mockedLatestStats = { + ...mockedLatestStatsEmpty, + data: mockedMigrationLatestStatsData, +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx index db6e76e7a5dee..614b901465a66 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx @@ -320,7 +320,12 @@ export const MigrationRulesTable: React.FC = React.mem ) : ( <> - + diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/name.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/name.tsx index 56f4123403c97..c09e06bff3a8a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/name.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/name.tsx @@ -20,7 +20,7 @@ interface NameProps { const Name = ({ rule, openMigrationRuleDetails }: NameProps) => { if (rule.status === SiemMigrationStatus.FAILED) { return ( - + {rule.original_rule.title} ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx index dd48d3b357e18..f49046d677ced 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx @@ -12,6 +12,7 @@ import * as i18n from './translations'; export const UnknownMigration: React.FC = React.memo(() => { return ( { + return { + SecuritySolutionPageWrapper: jest.fn(({ children }) => { + return
{children}
; + }), + }; +}); + +const useLatestStatsSpy = jest.spyOn(useLatestStatsModule, 'useLatestStats'); +const useNavigationSpy = jest.spyOn(useNavigationModule, 'useNavigation'); +const useGetIntegrationsSpy = jest.spyOn(useGetIntegrationsModule, 'useGetIntegrations'); +const useInvalidateGetMigrationRulesSpy = jest.spyOn( + useGetMigrationRulesModule, + 'useInvalidateGetMigrationRules' +); +const useInvalidateGetMigrationTranslationStatsSpy = jest.spyOn( + useGetMigrationTranslationStatsModule, + 'useInvalidateGetMigrationTranslationStats' +); + +const useGetMigrationRulesSpy = jest.spyOn(useGetMigrationRulesModule, 'useGetMigrationRules'); +// missing detection privileges +const useMissingPrivilegesSpy = jest.spyOn(useMissingPrivilegesModule, 'useMissingPrivileges'); +// missing migration privileges +const useGetMigrationMissingPrivilegesSpy = jest.spyOn( + useGetMigrationMissingPrivilegesModule, + 'useGetMigrationMissingPrivileges' +); +const useCalloutStorageSpy = jest.spyOn(useCallOutStorageModule, 'useCallOutStorage'); +const useGetMissingResourcesSpy = jest.spyOn( + useGetMissingResourcesModule, + 'useGetMissingResources' +); +const useGetMigrationTranslationStatsSpy = jest.spyOn( + useGetMigrationTranslationStatsModule, + 'useGetMigrationTranslationStats' +); + +const defaultProps: MigrationRulesPageProps = { + history: createMemoryHistory(), + location: createMemoryHistory().location, + match: { + isExact: true, + path: '', + url: '', + params: { + migrationId: undefined, + }, + }, +}; + +const mockNavigateTo = jest.fn(); +const mockGetIntegrations = jest.fn(); + +const mockMissingDetectionsPrivileges: useMissingPrivilegesModule.MissingPrivileges = { + featurePrivileges: [], + indexPrivileges: [], +}; + +const mockGetMigrationMissingPrivileges = { + data: [], +}; + +const mockVisibleCallStorageResult = { + isVisible: () => true, + dismiss: jest.fn(), + getVisibleMessageIds: jest.fn(() => []), +}; + +const mockHiddenCallStorageResult = { + isVisible: () => false, + dismiss: jest.fn(), + getVisibleMessageIds: jest.fn(() => []), +}; + +function renderTestComponent(args?: { migrationId?: string; wrapper?: React.ComponentType }) { + const finalProps = { + ...defaultProps, + match: { + ...defaultProps.match, + params: { + migrationId: args?.migrationId ?? defaultProps.match.params.migrationId, + }, + }, + }; + return render(, { + wrapper: args?.wrapper, + }); +} + +const mockUseMigrationRuleTransationStats: typeof useGetMigrationTranslationStatsModule.useGetMigrationTranslationStats = + jest.fn((migrationId: string) => { + const result = structuredClone(mockedMigrationTranslationStats)[migrationId]; + return { + data: result, + isLoading: false, + } as unknown as ReturnType< + typeof useGetMigrationTranslationStatsModule.useGetMigrationTranslationStats + >; + }); + +const mockUseGetMigrationRules: typeof useGetMigrationRulesModule.useGetMigrationRules = jest.fn( + ({ migrationId }) => { + const { data, total } = mockedMigrationResultsObj[migrationId]; + return { + data: { + ruleMigrations: data, + total, + }, + isLoading: false, + } as unknown as ReturnType; + } +); + +describe('Migrations: Translated Rules Page', () => { + beforeEach(() => { + useLatestStatsSpy.mockReturnValue(mockedLatestStatsEmpty); + useNavigationSpy.mockReturnValue({ navigateTo: mockNavigateTo, getAppUrl: jest.fn() }); + useGetIntegrationsSpy.mockReturnValue({ + getIntegrations: mockGetIntegrations, + isLoading: false, + error: null, + }); + useInvalidateGetMigrationTranslationStatsSpy.mockReturnValue(jest.fn()); + useInvalidateGetMigrationRulesSpy.mockReturnValue(jest.fn()); + useMissingPrivilegesSpy.mockReturnValue(mockMissingDetectionsPrivileges); + useGetMigrationMissingPrivilegesSpy.mockReturnValue( + mockGetMigrationMissingPrivileges as unknown as ReturnType< + typeof useGetMigrationMissingPrivilegesModule.useGetMigrationMissingPrivileges + > + ); + useCalloutStorageSpy.mockReturnValue(mockHiddenCallStorageResult); + useGetMigrationRulesSpy.mockImplementation(mockUseGetMigrationRules); + useGetMissingResourcesSpy.mockReturnValue({ + getMissingResources: jest.fn(() => []), + isLoading: false, + } as unknown as ReturnType); + + useGetMigrationTranslationStatsSpy.mockImplementation(mockUseMigrationRuleTransationStats); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('With No MigrationId', () => { + test('should render empty page when no translated rules are available', () => { + renderTestComponent(); + + expect(screen.getByTestId('siemMigrationsTranslatedRulesEmptyPageHeader')).toBeVisible(); + expect(screen.queryByTestId('siemMigrationsRulesTable')).toBeNull(); + }); + + test('should render skeleton when loading', () => { + useLatestStatsSpy.mockReturnValue({ ...mockedLatestStatsEmpty, isLoading: true }); + renderTestComponent(); + + expect(screen.getByTestId('migrationRulesPageLoading')).toBeVisible(); + }); + + test('should redirect to the most recent migration when no migrationId is provided and migrations exists', () => { + useLatestStatsSpy.mockReturnValue(mockedLatestStats); + renderTestComponent(); + + expect(mockNavigateTo).toHaveBeenCalledWith({ + deepLinkId: 'siem_migrations-rules', + path: '2', + }); + }); + + test('should render missing privileges panel', () => { + const mockMissingPrivileges: useMissingPrivilegesModule.MissingPrivileges = { + ...mockMissingDetectionsPrivileges, + indexPrivileges: [['index', ['privilege1', 'privilege2']]], + }; + + useLatestStatsSpy.mockReturnValue(mockedLatestStats); + useMissingPrivilegesSpy.mockReturnValue(mockMissingPrivileges); + useCalloutStorageSpy.mockReturnValue(mockVisibleCallStorageResult); + + const missingPrivilegesHash = hash(mockMissingPrivileges); + + renderTestComponent({ + wrapper: TestProviders, + }); + + expect(useCalloutStorageSpy).toHaveBeenCalled(); + + expect( + screen.getByTestId(`callout-missing-siem-migrations-privileges-${missingPrivilegesHash}`) + ).toBeVisible(); + + expect( + screen.getByTestId(`callout-missing-siem-migrations-privileges-${missingPrivilegesHash}`) + ).toHaveTextContent(/Insufficient privileges/); + }); + + test('should render unknown migration state if no migrationId is provided', () => { + useLatestStatsSpy.mockReturnValue(mockedLatestStats); + renderTestComponent(); + expect(screen.getByTestId('siemMigrationsUnknown')).toBeVisible(); + }); + }); + + describe('With MigrationId', () => { + test('should render migration table successfully if migrationId is provided', async () => { + useLatestStatsSpy.mockReturnValue(mockedLatestStats); + renderTestComponent({ + migrationId: '1', + wrapper: TestProviders, + }); + + await waitFor(() => { + expect(screen.getByTestId('siemMigrationsRulesTable')).toBeVisible(); + }); + + expect(screen.getAllByTestId(/ruleName/)).toHaveLength(2); + expect(screen.getAllByTestId(/ruleName/)[0]).toHaveTextContent(/Converted Splunk Rule -/); + // only successful is selectable + expect(screen.getAllByTitle(/Select row 1/)).toHaveLength(1); + expect(screen.getByTitle(/Not fully translated migration rule/)).toBeDisabled(); + }); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx index ae5ac077a0c13..af48c73a93c6b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx @@ -32,7 +32,7 @@ import { PageTitle } from './page_title'; import { RuleMigrationsUploadMissingPanel } from '../components/migration_status_panels/upload_missing_panel'; import { EmptyMigrationRulesPage } from './empty'; -type MigrationRulesPageProps = RouteComponentProps<{ migrationId?: string }>; +export type MigrationRulesPageProps = RouteComponentProps<{ migrationId?: string }>; export const MigrationRulesPage: React.FC = React.memo( ({ @@ -139,6 +139,7 @@ export const MigrationRulesPage: React.FC = React.memo( From 6f9db6a196cca80628cd7cde9685d93b070b60bc Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Tue, 11 Mar 2025 11:17:00 +0100 Subject: [PATCH 3/6] fix: empty button --- .../public/siem_migrations/rules/pages/empty.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx index 38a112037dd70..87363ccb713cb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx @@ -6,12 +6,14 @@ */ import React, { useCallback } from 'react'; -import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { SecurityPageName } from '@kbn/deeplinks-security'; import { css } from '@emotion/react'; +import { SecuritySolutionLinkButton } from '../../../common/components/links'; import { useNavigation } from '../../../common/lib/kibana'; import * as i18n from './translations'; +import { OnboardingCardId, OnboardingTopicId } from '../../../onboarding/constants'; export const EmptyMigrationRulesPage = () => { const { navigateTo } = useNavigation(); @@ -31,6 +33,7 @@ export const EmptyMigrationRulesPage = () => { min-height: calc(100vh - 240px); `} justifyContent="center" + alignItems="center" > { } actions={ - + {i18n.TRANSLATED_RULES_EMPTY_PAGE_CTA} - + } iconType={'logoSecurity'} body={ From 71f3b3c5bde0cb0710e07824c159b0cabc389248 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Tue, 11 Mar 2025 11:20:04 +0100 Subject: [PATCH 4/6] fix: remove unnecessary code --- .../public/siem_migrations/rules/pages/empty.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx index 87363ccb713cb..cff3bb5310323 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx @@ -5,23 +5,16 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React from 'react'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { SecurityPageName } from '@kbn/deeplinks-security'; import { css } from '@emotion/react'; import { SecuritySolutionLinkButton } from '../../../common/components/links'; -import { useNavigation } from '../../../common/lib/kibana'; import * as i18n from './translations'; import { OnboardingCardId, OnboardingTopicId } from '../../../onboarding/constants'; export const EmptyMigrationRulesPage = () => { - const { navigateTo } = useNavigation(); - - const navigateToStartMigrationCallback = useCallback(() => { - navigateTo({ deepLinkId: SecurityPageName.landing, path: 'siem_migrations#start' }); - }, [navigateTo]); - return ( Date: Tue, 11 Mar 2025 13:50:09 +0100 Subject: [PATCH 5/6] fix: test --- .../public/siem_migrations/rules/pages/index.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.test.tsx index 6e303c9ad770e..be66b66e87161 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/index.test.tsx @@ -173,7 +173,9 @@ describe('Migrations: Translated Rules Page', () => { describe('With No MigrationId', () => { test('should render empty page when no translated rules are available', () => { - renderTestComponent(); + renderTestComponent({ + wrapper: TestProviders, + }); expect(screen.getByTestId('siemMigrationsTranslatedRulesEmptyPageHeader')).toBeVisible(); expect(screen.queryByTestId('siemMigrationsRulesTable')).toBeNull(); From 0e4c0c3f20bd80d60575586952531a0d56907894 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 12 Mar 2025 13:13:10 +0100 Subject: [PATCH 6/6] fix: page path --- .../public/siem_migrations/rules/pages/empty.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx index cff3bb5310323..7697f87ce6e73 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/empty.tsx @@ -38,7 +38,7 @@ export const EmptyMigrationRulesPage = () => { actions={ {i18n.TRANSLATED_RULES_EMPTY_PAGE_CTA}