{!isConnectorsCardComplete && (
<>
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.test.tsx
new file mode 100644
index 0000000000000..373fa5e3a1ffd
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.test.tsx
@@ -0,0 +1,238 @@
+/*
+ * 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 type { ComponentProps } from 'react';
+import React from 'react';
+import { act, render, screen, waitFor } from '@testing-library/react';
+import * as useLatestStatsModule from '../../../../../../siem_migrations/rules/service/hooks/use_latest_stats';
+import StartMigrationCard from './start_migration_card';
+import * as useUpsellingComponentModule from '../../../../../../common/hooks/use_upselling';
+import { TestProviders } from '../../../../../../common/mock';
+import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants';
+import type { RuleMigrationStats } from '../../../../../../siem_migrations/rules/types';
+import { OnboardingCardId } from '../../../../../constants';
+import * as useGetMigrationTranslationStatsModule from '../../../../../../siem_migrations/rules/logic/use_get_migration_translation_stats';
+import * as useGetMissingResourcesModule from '../../../../../../siem_migrations/rules/service/hooks/use_get_missing_resources';
+
+const useLatestStatsSpy = jest.spyOn(useLatestStatsModule, 'useLatestStats');
+
+const useUpsellingComponentMock = jest.spyOn(useUpsellingComponentModule, 'useUpsellingComponent');
+
+const useGetMigrationTranslationStatsSpy = jest.spyOn(
+ useGetMigrationTranslationStatsModule,
+ 'useGetMigrationTranslationStats'
+);
+
+const useGetMissingResourcesMock = jest.spyOn(
+ useGetMissingResourcesModule,
+ 'useGetMissingResources'
+);
+
+const MockUpsellingComponent = () => {
+ return {`Start Migrations Upselling Component`}
;
+};
+
+const mockedLatestStats = {
+ data: [],
+ isLoading: false,
+ refreshStats: jest.fn(),
+};
+
+const mockTranslationStats = {
+ isLoading: false,
+ data: {
+ id: '1',
+ rules: {
+ total: 1,
+ failed: 0,
+ success: {
+ result: {
+ full: 1,
+ partial: 0,
+ failed: 0,
+ },
+ },
+ },
+ },
+} as unknown as ReturnType<
+ typeof useGetMigrationTranslationStatsModule.useGetMigrationTranslationStats
+>;
+
+const mockMissingResources = {
+ getMissingResources: jest.fn(() => []),
+ isLoading: false,
+} as unknown as ReturnType;
+
+type TestComponentProps = ComponentProps;
+
+const defaultProps: TestComponentProps = {
+ setComplete: jest.fn(),
+ isCardComplete: jest.fn(
+ (cardId: OnboardingCardId) => cardId === OnboardingCardId.siemMigrationsAiConnectors
+ ),
+ setExpandedCardId: jest.fn(),
+ checkComplete: jest.fn(),
+ isCardAvailable: () => true,
+ checkCompleteMetadata: {
+ missingCapabilities: [],
+ },
+};
+
+const renderTestComponent = (props: Partial> = {}) => {
+ const finalProps: TestComponentProps = {
+ ...defaultProps,
+ ...props,
+ };
+
+ return render(
+
+
+
+ );
+};
+
+describe('StartMigrationsBody', () => {
+ beforeEach(() => {
+ useLatestStatsSpy.mockReturnValue(mockedLatestStats);
+ useUpsellingComponentMock.mockReturnValue(null);
+ useGetMigrationTranslationStatsSpy.mockReturnValue(mockTranslationStats);
+ useGetMissingResourcesMock.mockReturnValue(mockMissingResources);
+ });
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render upsell correctly when available', () => {
+ useUpsellingComponentMock.mockReturnValue(MockUpsellingComponent);
+
+ renderTestComponent();
+
+ expect(screen.getByTestId('mockUpsellSection')).toBeVisible();
+ expect(screen.getByTestId('startMigrationUploadRulesButton')).toBeVisible();
+ expect(screen.getByTestId('startMigrationUploadRulesButton')).toBeDisabled();
+ });
+
+ it('should render missing Privileges Callout when there are missing capabilities but NO Upsell', () => {
+ renderTestComponent({
+ checkCompleteMetadata: {
+ missingCapabilities: ['missingPrivileges'],
+ },
+ });
+
+ expect(screen.getByTestId('missingPrivilegesGroup')).toBeVisible();
+ });
+
+ it('should render component correctly when no upsell and no missing capabilities', () => {
+ renderTestComponent();
+
+ expect(screen.getByTestId('StartMigrationsCardBody')).toBeVisible();
+ expect(screen.getByTestId('StartMigrationsCardBody')).not.toBeEmptyDOMElement();
+ });
+
+ it('should mark card as complete when migration is finished', async () => {
+ useLatestStatsSpy.mockReturnValue({
+ ...mockedLatestStats,
+ data: [
+ {
+ id: '1',
+ status: SiemMigrationTaskStatus.FINISHED,
+ } as unknown as RuleMigrationStats,
+ ],
+ });
+
+ await act(async () => {
+ renderTestComponent();
+ });
+
+ await waitFor(() => {
+ expect(defaultProps.setComplete).toHaveBeenCalledWith(true);
+ });
+ });
+
+ it('should render loader when migration handler is loading', async () => {
+ const latestStatus = {
+ ...mockedLatestStats,
+ isLoading: true,
+ data: [
+ {
+ id: '1',
+ status: SiemMigrationTaskStatus.RUNNING,
+ rules: {
+ total: 1,
+ pending: 1,
+ processing: 1,
+ completed: 0,
+ failed: 0,
+ },
+ } as unknown as RuleMigrationStats,
+ ],
+ };
+
+ useLatestStatsSpy.mockReturnValue(latestStatus);
+
+ renderTestComponent();
+
+ expect(screen.getByTestId('centeredLoadingSpinner')).toBeVisible();
+ });
+
+ it('should render progress bar when migration is running', async () => {
+ const latestStats = {
+ ...mockedLatestStats,
+ isLoading: false,
+ data: [
+ {
+ id: '1',
+ status: SiemMigrationTaskStatus.RUNNING,
+ rules: {
+ total: 1,
+ pending: 1,
+ processing: 1,
+ completed: 0,
+ failed: 0,
+ },
+ } as unknown as RuleMigrationStats,
+ ],
+ };
+ useLatestStatsSpy.mockReturnValue(latestStats);
+
+ await act(async () => {
+ renderTestComponent();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('migrationProgressPanel')).toBeVisible();
+ });
+ });
+
+ it('should render result panel when migration is finished', async () => {
+ const latestStats = {
+ ...mockedLatestStats,
+ isLoading: false,
+ data: [
+ {
+ id: '1',
+ status: SiemMigrationTaskStatus.FINISHED,
+ rules: {
+ total: 1,
+ pending: 0,
+ processing: 0,
+ completed: 1,
+ failed: 0,
+ },
+ } as unknown as RuleMigrationStats,
+ ],
+ };
+
+ useLatestStatsSpy.mockReturnValue(latestStats);
+
+ await act(async () => {
+ renderTestComponent();
+ });
+
+ expect(screen.getByTestId('ruleMigrationPanelGroup')).toBeVisible();
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx
index 34e7a25d9a125..10b91e57ed2c1 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx
@@ -6,7 +6,8 @@
*/
import React, { useCallback, useEffect, useMemo } from 'react';
-import { EuiSpacer } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import { useUpsellingComponent } from '../../../../../../common/hooks/use_upselling';
import { PanelText } from '../../../../../../common/components/panel_text';
import { RuleMigrationDataInputWrapper } from '../../../../../../siem_migrations/rules/components/data_input_flyout/data_input_wrapper';
import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants';
@@ -23,6 +24,7 @@ import {
MissingPrivilegesCallOut,
MissingPrivilegesDescription,
} from '../../common/missing_privileges';
+import { UploadRulesSectionPanel } from './upload_rules_panel';
const StartMigrationsBody: OnboardingCardComponent = React.memo(
({ setComplete, isCardComplete, setExpandedCardId }) => {
@@ -49,7 +51,11 @@ const StartMigrationsBody: OnboardingCardComponent = React.memo(
return (
-
+
{isLoading ? (
) : (
@@ -72,10 +78,26 @@ StartMigrationsBody.displayName = 'StartMigrationsBody';
export const StartMigrationCard: OnboardingCardComponent = React.memo(
({ checkCompleteMetadata, ...props }) => {
+ const UpsellSectionComp = useUpsellingComponent('siem_migrations_start');
if (!checkCompleteMetadata) {
return ;
}
+ if (UpsellSectionComp) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
const { missingCapabilities } = checkCompleteMetadata;
if (missingCapabilities.length > 0) {
return (
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.test.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.test.ts
new file mode 100644
index 0000000000000..bd30310d054ae
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.test.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants';
+import { createStartServicesMock } from '../../../../../../common/lib/kibana/kibana_react.mock';
+import type { SiemMigrationsService } from '../../../../../../siem_migrations/service';
+import { checkStartMigrationCardComplete } from './start_migration_check_complete';
+
+describe('startMigrationCheckComplete', () => {
+ test('should return default values if siem migrations are not available', async () => {
+ // Arrange
+ const siemMigrations = {
+ rules: {
+ getMissingCapabilities: jest.fn().mockReturnValue([]),
+ isAvailable: jest.fn().mockReturnValue(false),
+ },
+ } as unknown as SiemMigrationsService;
+
+ const services = {
+ ...createStartServicesMock(),
+ siemMigrations,
+ };
+ const result = await checkStartMigrationCardComplete(services);
+
+ expect(result).toEqual({ isComplete: false, metadata: { missingCapabilities: [] } });
+ });
+
+ test('should query Stats if siem migrations are available', async () => {
+ const siemMigrations = {
+ rules: {
+ getMissingCapabilities: jest.fn().mockReturnValue([]),
+ isAvailable: jest.fn().mockReturnValue(true),
+ getRuleMigrationsStats: jest.fn().mockReturnValue([
+ {
+ status: SiemMigrationTaskStatus.FINISHED,
+ },
+ ]),
+ },
+ } as unknown as SiemMigrationsService;
+
+ const services = {
+ ...createStartServicesMock(),
+ siemMigrations,
+ };
+
+ const result = await checkStartMigrationCardComplete(services);
+
+ expect(siemMigrations.rules.getRuleMigrationsStats).toHaveBeenCalled();
+
+ expect(result).toEqual({
+ isComplete: true,
+ metadata: { missingCapabilities: [] },
+ });
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.ts
index 2e3a8f238ac43..ec5d100ff61fb 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_check_complete.ts
@@ -19,12 +19,11 @@ export const checkStartMigrationCardComplete: OnboardingCardCheckComplete<
let isComplete = false;
- if (missingCapabilities.length === 0) {
+ if (siemMigrations.rules.isAvailable()) {
const migrationsStats = await siemMigrations.rules.getRuleMigrationsStats();
isComplete = migrationsStats.some(
(migrationStats) => migrationStats.status === SiemMigrationTaskStatus.FINISHED
);
}
-
return { isComplete, metadata: { missingCapabilities } };
};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx
index ffa425f8e7df6..f2e93c3796944 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx
@@ -27,16 +27,14 @@ export interface UploadRulesPanelProps {
isUploadMore?: boolean;
isDisabled?: boolean;
}
-export const UploadRulesPanel = React.memo(
- ({ isUploadMore = false, isDisabled = false }) => {
- const styles = useStyles(isUploadMore);
- const { telemetry } = useKibana().services.siemMigrations.rules;
- const { openFlyout } = useRuleMigrationDataInputContext();
- const onOpenFlyout = useCallback(() => {
- openFlyout();
- telemetry.reportSetupMigrationOpen({ isFirstMigration: !isUploadMore });
- }, [openFlyout, telemetry, isUploadMore]);
+export interface UploadRulesSectionPanelProps extends UploadRulesPanelProps {
+ onOpenFlyout?: React.MouseEventHandler;
+}
+
+export const UploadRulesSectionPanel = React.memo(
+ function UploadRulesSectionPanel({ isUploadMore = false, isDisabled = false, onOpenFlyout }) {
+ const styles = useStyles(isUploadMore);
return (
@@ -75,6 +73,7 @@ export const UploadRulesPanel = React.memo(
{isUploadMore ? (
(
) : (
(
);
}
);
+
+export const UploadRulesPanel = React.memo(function UploadRulesPanel({
+ isUploadMore = false,
+ isDisabled = false,
+}: UploadRulesPanelProps) {
+ const { telemetry } = useKibana().services.siemMigrations.rules;
+ const { openFlyout } = useRuleMigrationDataInputContext();
+
+ const onOpenFlyout = useCallback(() => {
+ openFlyout();
+ telemetry.reportSetupMigrationOpen({ isFirstMigration: !isUploadMore });
+ }, [openFlyout, telemetry, isUploadMore]);
+
+ return (
+
+ );
+});
+
UploadRulesPanel.displayName = 'UploadRulesPanel';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/config.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/config.ts
index c424f28cef71b..612e7ccba2f93 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/config.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/config.ts
@@ -6,7 +6,6 @@
*/
import { i18n } from '@kbn/i18n';
-import { SIEM_MIGRATIONS_FEATURE_ID } from '@kbn/security-solution-features/constants';
import { OnboardingTopicId } from './constants';
import {
defaultBodyConfig,
@@ -28,8 +27,6 @@ export const onboardingConfig: TopicConfig[] = [
defaultMessage: 'SIEM Rule migration',
}),
body: siemMigrationsBodyConfig,
- licenseTypeRequired: 'enterprise',
- capabilitiesRequired: `${SIEM_MIGRATIONS_FEATURE_ID}.all`,
disabledExperimentalFlagRequired: 'siemMigrationsDisabled',
},
];
diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx
index 9fca25e88f916..37b8a0fed66c0 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx
@@ -33,7 +33,7 @@ export const MigrationProgressPanel = React.memo(
const preparing = migrationStats.rules.pending === migrationStats.rules.total;
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 0c86ca3a8e615..6f80eea2275b9 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
@@ -96,6 +96,14 @@ export class SiemRulesMigrationsService {
return this.getMissingCapabilities(level).length > 0;
}
+ /**
+ * checks if the service is available based on
+ *
+ * - the license
+ * - capabilities
+ * - feature flag
+ *
+ */
public isAvailable() {
return (
!ExperimentalFeaturesService.get().siemMigrationsDisabled &&
diff --git a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/lazy_upselling.tsx b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/lazy_upselling.tsx
index 56775b6c4433e..f77c5a32efe6f 100644
--- a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/lazy_upselling.tsx
+++ b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/lazy_upselling.tsx
@@ -19,6 +19,14 @@ export const EntityAnalyticsUpsellingSectionLazy = withSuspenseUpsell(
)
);
+export const SiemMigrationsStartUpsellSectionLazy = withSuspenseUpsell(
+ lazy(() =>
+ import('./sections/siem_migration_start').then(({ SiemMigrationStartUpsellSection }) => ({
+ default: SiemMigrationStartUpsellSection,
+ }))
+ )
+);
+
export const EntityAnalyticsUpsellingPageLazy = lazy(() =>
import('./pages/entity_analytics_upselling').then(({ EntityAnalyticsUpsellingPageESS }) => ({
default: EntityAnalyticsUpsellingPageESS,
diff --git a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/register_upsellings.tsx b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/register_upsellings.tsx
index 36a3fc13e240e..eb7124cb1c942 100644
--- a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/register_upsellings.tsx
+++ b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/register_upsellings.tsx
@@ -31,6 +31,7 @@ import {
AttackDiscoveryUpsellingPageLazy,
EntityAnalyticsUpsellingPageLazy,
EntityAnalyticsUpsellingSectionLazy,
+ SiemMigrationsStartUpsellSectionLazy,
} from './lazy_upselling';
interface UpsellingsConfig {
@@ -111,6 +112,11 @@ export const upsellingSections: UpsellingSections = [
minimumLicenseRequired: 'platinum',
component: EntityAnalyticsUpsellingSectionLazy,
},
+ {
+ id: 'siem_migrations_start',
+ minimumLicenseRequired: 'enterprise',
+ component: SiemMigrationsStartUpsellSectionLazy,
+ },
];
// Upsellings for sections, linked by arbitrary ids
diff --git a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/sections/siem_migration_start.tsx b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/sections/siem_migration_start.tsx
new file mode 100644
index 0000000000000..3d0a370146ef9
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/sections/siem_migration_start.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 { SiemMigrationStartUpsellSection as SiemMigrationStartUpsellSectionCommon } from '@kbn/security-solution-upselling/sections/siem_migrations_start';
+import { useKibana } from '../../common/services';
+import * as i18n from '../translations';
+
+export const SiemMigrationStartUpsellSection = () => {
+ const { services } = useKibana();
+ return (
+
+ );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/translations.ts b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/translations.ts
index 9af27d3ca5242..867755b9ba9d5 100644
--- a/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/translations.ts
+++ b/x-pack/solutions/security/plugins/security_solution_ess/public/upselling/translations.ts
@@ -14,3 +14,18 @@ export const UPGRADE_LICENSE_MESSAGE = (requiredLicense: string) =>
requiredLicense,
},
});
+
+export const SIEM_MIGRATION_UPSELLING_TITLE = (requiredLicense: string) =>
+ i18n.translate('xpack.securitySolutionEss.upselling.siemMigrations.title', {
+ defaultMessage: '{requiredLicense} license required',
+ values: {
+ requiredLicense,
+ },
+ });
+
+export const SIEM_MIGRATION_UPGRADE_LICENSE_MESSAGE = i18n.translate(
+ 'xpack.securitySolutionEss.upselling.siemMigrations.upgradeLicenseMessage',
+ {
+ defaultMessage: 'To use this feature, upgrade your Elastic subscription level.',
+ }
+);
diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/lazy_upselling.tsx b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/lazy_upselling.tsx
index 526654a6f4509..014ba27debec4 100644
--- a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/lazy_upselling.tsx
+++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/lazy_upselling.tsx
@@ -41,6 +41,14 @@ export const EntityAnalyticsUpsellingSectionLazy = withSuspenseUpsell(
)
);
+export const SiemMigrationsStartUpsellSectionLazy = withSuspenseUpsell(
+ lazy(() =>
+ import('./sections/siem_migrations/siem_migrations_start').then(
+ ({ SiemMigrationStartUpsellSection }) => ({ default: SiemMigrationStartUpsellSection })
+ )
+ )
+);
+
export const AttackDiscoveryUpsellingPageLazy = withSuspenseUpsell(
lazy(() =>
import('./pages/attack_discovery').then(({ AttackDiscoveryUpsellingPageServerless }) => ({
diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/sections/siem_migrations/siem_migrations_start.tsx b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/sections/siem_migrations/siem_migrations_start.tsx
new file mode 100644
index 0000000000000..11d1df1d11510
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/sections/siem_migrations/siem_migrations_start.tsx
@@ -0,0 +1,19 @@
+/*
+ * 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 { SiemMigrationStartUpsellSection as SiemMigrationStartUpsellSectionCommon } from '@kbn/security-solution-upselling/sections/siem_migrations_start';
+import * as i18n from '../../translations';
+
+export const SiemMigrationStartUpsellSection = () => {
+ return (
+
+ );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/translations.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/translations.ts
index f70a36f90348f..39db5f3198b3e 100644
--- a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/translations.ts
+++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/translations.ts
@@ -26,3 +26,19 @@ export const ADDITIONAL_CHARGES_MESSAGE = i18n.translate(
'Please be aware that activating these features may incur additional charges depending on your subscription plan. Review your plan details carefully to avoid unexpected costs before proceeding.',
}
);
+
+export const SIEM_MIGRATION_UPSELLING_TITLE = (requiredTier: string) =>
+ i18n.translate('xpack.securitySolutionServerless.upselling.siemMigrations.title', {
+ defaultMessage: 'Security {requiredTier} tier required',
+ values: {
+ requiredTier,
+ },
+ });
+
+export const SIEM_MIGRATION_UPGRADE_MESSAGE = i18n.translate(
+ 'xpack.securitySolutionServerless.upselling.siemMigrations.upgradeTierMessage',
+ {
+ defaultMessage:
+ 'To use this feature, you need to upgrade your Elastic Cloud Serverless feature tier. Update your subscription or contact your administrator for assistance.',
+ }
+);
diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/upsellings.tsx b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/upsellings.tsx
index 6079a13ff6d2c..e726917d356b7 100644
--- a/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/upsellings.tsx
+++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/upselling/upsellings.tsx
@@ -33,6 +33,7 @@ import {
EntityAnalyticsUpsellingPageLazy,
EntityAnalyticsUpsellingSectionLazy,
OsqueryResponseActionsUpsellingSectionLazy,
+ SiemMigrationsStartUpsellSectionLazy,
ThreatIntelligencePaywallLazy,
} from './lazy_upselling';
import * as i18n from './translations';
@@ -141,6 +142,11 @@ export const upsellingSections: UpsellingSections = [
/>
),
},
+ {
+ id: 'siem_migrations_start',
+ pli: ProductFeatureKey.siemMigrations,
+ component: SiemMigrationsStartUpsellSectionLazy,
+ },
{
id: 'automatic_import',
pli: ProductFeatureKey.automaticImport,