diff --git a/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx b/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
index 610eb7e2a72d8..cc2b9fbad3451 100644
--- a/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
+++ b/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
@@ -6,6 +6,7 @@
*/
import React from 'react';
+import { Routes, Route } from '@kbn/shared-ux-router';
import type { SecuritySubPluginRoutes } from '../app/types';
import { SIEM_MIGRATIONS_RULES_PATH, SecurityPageName } from '../../common/constants';
@@ -17,7 +18,9 @@ export const RulesRoutes = () => {
return (
-
+
+
+
);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx
deleted file mode 100644
index e4b3701d94c73..0000000000000
--- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import React from 'react';
-import { SecurityPageName } from '../../../../../common';
-import { useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
-import * as i18n from './translations';
-
-const NoMigrationsComponent = () => {
- const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
- const { onClick: onClickLink } = getSecuritySolutionLinkProps({
- deepLinkId: SecurityPageName.landing,
- path: 'siem_migrations',
- });
-
- return (
-
-
- {i18n.NO_MIGRATIONS_AVAILABLE}}
- titleSize="s"
- body={i18n.NO_MIGRATIONS_AVAILABLE_BODY}
- data-test-subj="noMigrationsAvailable"
- />
-
-
-
- {i18n.GO_BACK_TO_RULES_TABLE_BUTTON}
-
-
-
- );
-};
-
-export const NoMigrations = React.memo(NoMigrationsComponent);
-NoMigrations.displayName = 'NoMigrations';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts
deleted file mode 100644
index 77ec0454fb5a5..0000000000000
--- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 { i18n } from '@kbn/i18n';
-
-export const NO_MIGRATIONS_AVAILABLE = i18n.translate(
- 'xpack.securitySolution.siemMigrations.rules.noMigrationsTitle',
- {
- defaultMessage: 'No migrations',
- }
-);
-
-export const NO_MIGRATIONS_AVAILABLE_BODY = i18n.translate(
- 'xpack.securitySolution.siemMigrations.rules.noMigrationsBodyTitle',
- {
- defaultMessage: 'There are no migrations available',
- }
-);
-
-export const GO_BACK_TO_RULES_TABLE_BUTTON = i18n.translate(
- 'xpack.securitySolution.siemMigrations.rules.table.goToMigrationsPageButton',
- {
- defaultMessage: 'Go back to SIEM Migrations',
- }
-);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx
new file mode 100644
index 0000000000000..0a33869eff418
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/index.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import * as i18n from './translations';
+
+const UnknownMigrationComponent = () => {
+ return (
+
+
+ {i18n.UNKNOWN_MIGRATION}}
+ titleSize="s"
+ body={i18n.UNKNOWN_MIGRATION_BODY}
+ data-test-subj="noMigrationsAvailable"
+ />
+
+
+ );
+};
+
+export const UnknownMigration = React.memo(UnknownMigrationComponent);
+UnknownMigration.displayName = 'UnknownMigration';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/translations.ts
new file mode 100644
index 0000000000000..8720640858b94
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/unknown_migration/translations.ts
@@ -0,0 +1,23 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const UNKNOWN_MIGRATION = i18n.translate(
+ 'xpack.securitySolution.siemMigrations.rules.unknownMigrationTitle',
+ {
+ defaultMessage: 'Unknown migration',
+ }
+);
+
+export const UNKNOWN_MIGRATION_BODY = i18n.translate(
+ 'xpack.securitySolution.siemMigrations.rules.unknownMigrationBodyTitle',
+ {
+ defaultMessage:
+ 'Selected migration does not exist. Please select one of the available migraitons',
+ }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
index 83392f0a70d2a..26199616b3777 100644
--- a/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
@@ -5,12 +5,15 @@
* 2.0.
*/
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo } from 'react';
import { EuiSkeletonLoading, EuiSkeletonText, EuiSkeletonTitle } from '@elastic/eui';
+import type { RouteComponentProps } from 'react-router-dom';
+import { useNavigation } from '../../../common/lib/kibana';
import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen';
import { HeaderPage } from '../../../common/components/header_page';
import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper';
+import { SecurityPageName } from '../../../app/types';
import * as i18n from './translations';
import { RulesTable } from '../components/rules_table';
@@ -18,80 +21,97 @@ import { NeedAdminForUpdateRulesCallOut } from '../../../detections/components/c
import { MissingPrivilegesCallOut } from '../../../detections/components/callouts/missing_privileges_callout';
import { HeaderButtons } from '../components/header_buttons';
import { useRulePreviewFlyout } from '../hooks/use_rule_preview_flyout';
-import { NoMigrations } from '../components/no_migrations';
+import { UnknownMigration } from '../components/unknown_migration';
import { useLatestStats } from '../hooks/use_latest_stats';
-export const RulesPage = React.memo(() => {
- const { data: ruleMigrationsStatsAll, isLoading: isLoadingMigrationsStats } = useLatestStats();
-
- const migrationsIds = useMemo(() => {
- if (isLoadingMigrationsStats || !ruleMigrationsStatsAll?.length) {
- return [];
- }
- return ruleMigrationsStatsAll
- .filter((migration) => migration.status === 'finished')
- .map((migration) => migration.id);
- }, [isLoadingMigrationsStats, ruleMigrationsStatsAll]);
-
- const [selectedMigrationId, setSelectedMigrationId] = useState();
- const onMigrationIdChange = (selectedId?: string) => {
- setSelectedMigrationId(selectedId);
- };
-
- useEffect(() => {
- if (!migrationsIds.length) {
- return;
- }
- const index = migrationsIds.findIndex((id) => id === selectedMigrationId);
- if (index === -1) {
- setSelectedMigrationId(migrationsIds[0]);
- }
- }, [migrationsIds, selectedMigrationId]);
-
- const ruleActionsFactory = useCallback(
- (ruleMigration: RuleMigration, closeRulePreview: () => void) => {
- // TODO: Add flyout action buttons
- return null;
+type RulesMigrationPageProps = RouteComponentProps<{ migrationId?: string }>;
+
+export const RulesPage: React.FC = React.memo(
+ ({
+ match: {
+ params: { migrationId },
},
- []
- );
-
- const { rulePreviewFlyout, openRulePreview } = useRulePreviewFlyout({
- ruleActionsFactory,
- });
-
- return (
- <>
-
-
-
-
-
- {
+ const { navigateTo } = useNavigation();
+
+ const { data: ruleMigrationsStatsAll, isLoading: isLoadingMigrationsStats } = useLatestStats();
+
+ const migrationsIds = useMemo(() => {
+ if (isLoadingMigrationsStats || !ruleMigrationsStatsAll?.length) {
+ return [];
+ }
+ return ruleMigrationsStatsAll
+ .filter((migration) => migration.status === 'finished')
+ .map((migration) => migration.id);
+ }, [isLoadingMigrationsStats, ruleMigrationsStatsAll]);
+
+ useEffect(() => {
+ if (isLoadingMigrationsStats) {
+ return;
+ }
+
+ // Navigate to landing page if there are no migrations
+ if (!migrationsIds.length) {
+ navigateTo({ deepLinkId: SecurityPageName.landing, path: 'siem_migrations' });
+ return;
+ }
+
+ // Navigate to the most recent migration if none is selected
+ if (!migrationId) {
+ navigateTo({ deepLinkId: SecurityPageName.siemMigrationsRules, path: migrationsIds[0] });
+ }
+ }, [isLoadingMigrationsStats, migrationId, migrationsIds, navigateTo]);
+
+ const onMigrationIdChange = (selectedId?: string) => {
+ navigateTo({ deepLinkId: SecurityPageName.siemMigrationsRules, path: selectedId });
+ };
+
+ const ruleActionsFactory = useCallback(
+ (ruleMigration: RuleMigration, closeRulePreview: () => void) => {
+ // TODO: Add flyout action buttons
+ return null;
+ },
+ []
+ );
+
+ const { rulePreviewFlyout, openRulePreview } = useRulePreviewFlyout({
+ ruleActionsFactory,
+ });
+
+ const content = useMemo(() => {
+ if (!migrationId || !migrationsIds.includes(migrationId)) {
+ return ;
+ }
+ return ;
+ }, [migrationId, migrationsIds, openRulePreview]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ }
+ loadedContent={content}
/>
-
-
-
-
- >
- }
- loadedContent={
- selectedMigrationId ? (
-
- ) : (
-
- )
- }
- />
- {rulePreviewFlyout}
-
- >
- );
-});
+ {rulePreviewFlyout}
+
+ >
+ );
+ }
+);
RulesPage.displayName = 'RulesPage';