Skip to content

Commit

Permalink
[Security Solution][SIEM migrations] Onboarding UI improvements (#204320
Browse files Browse the repository at this point in the history
)

## Summary

Part of: elastic/security-team#10667

#### Improvements

- Implementation of the Onboarding card to create migrations using the
flyout
- Migration complete summary panel implemented
- Migration ready panel improved to detect missing resources
- Migration processing improved
- Migration missing resources panel implemented
- All migration panels and refactored to be reusable by translation
table using the
- `RuleMigrationDataInputWrapper` implemented to reuse the Flyout from
the translation table
- Request poll interval increased from 5 to 10 seconds due to event loop
usage.


> [!NOTE]  
> This feature needs `siemMigrationsEnabled` experimental flag enabled
to work.

## Screenshots

Lookups input


![Lookups](https://github.com/user-attachments/assets/73f91e10-7252-44d1-ab0d-89880c78a2b3)

Translation "complete" panel
![Translation
summary](https://github.com/user-attachments/assets/6fbb451d-c7b3-4a23-a2df-083c91948cbd)

Translation "created" panel (w/ and w/o missing macros)
![Ready
panels](https://github.com/user-attachments/assets/f8334af2-ccc1-473c-8548-772a9d656aba)

Translation processing (preparing)
![preparing
panel](https://github.com/user-attachments/assets/0156caba-c6c9-43c1-881a-8bf631f3a8ab)

Translation processing (translating)
![translating
panel](https://github.com/user-attachments/assets/db523e4b-4858-482f-bfe9-1e36f715fa20)

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 17, 2024
1 parent 523372f commit 303eee8
Show file tree
Hide file tree
Showing 68 changed files with 1,852 additions and 640 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export enum SiemMigrationStatus {
FAILED = 'failed',
}

export enum SiemMigrationRuleTranslationResult {
export enum RuleTranslationResult {
FULL = 'full',
PARTIAL = 'partial',
UNTRANSLATABLE = 'untranslatable',
Expand All @@ -60,3 +60,5 @@ export const DEFAULT_TRANSLATION_FIELDS = {
to: 'now',
interval: '5m',
} as const;

export const EMPTY_RESOURCE_PLACEHOLDER = '<empty>';
Original file line number Diff line number Diff line change
Expand Up @@ -285,21 +285,47 @@ export const RuleMigrationTranslationStats = z.object({
*/
rules: z.object({
/**
* The total number of rules to migrate.
* The total number of rules in the migration.
*/
total: z.number().int(),
/**
* The number of rules that matched Elastic prebuilt rules.
*/
prebuilt: z.number().int(),
/**
* The number of rules that did not match Elastic prebuilt rules and will be installed as custom rules.
* The number of rules that have been successfully translated.
*/
custom: z.number().int(),
success: z.object({
/**
* The total number of rules that have been successfully translated.
*/
total: z.number().int(),
/**
* The translation results
*/
result: z.object({
/**
* The number of rules that have been fully translated.
*/
full: z.number().int(),
/**
* The number of rules that have been partially translated.
*/
partial: z.number().int(),
/**
* The number of rules that could not be translated.
*/
untranslatable: z.number().int(),
}),
/**
* The number of rules that have been successfully translated and can be installed.
*/
installable: z.number().int(),
/**
* The number of rules that have been successfully translated and matched Elastic prebuilt rules.
*/
prebuilt: z.number().int(),
}),
/**
* The number of rules that can be installed.
* The number of rules that have failed translation.
*/
installable: z.number().int(),
failed: z.number().int(),
}),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,23 +234,50 @@ components:
description: The rules migration translation stats.
required:
- total
- prebuilt
- custom
- installable
- success
- failed
properties:
total:
type: integer
description: The total number of rules to migrate.
prebuilt:
type: integer
description: The number of rules that matched Elastic prebuilt rules.
custom:
type: integer
description: The number of rules that did not match Elastic prebuilt rules and will be installed as custom rules.
installable:
description: The total number of rules in the migration.
success:
type: object
description: The number of rules that have been successfully translated.
required:
- total
- result
- installable
- prebuilt
properties:
total:
type: integer
description: The total number of rules that have been successfully translated.
result:
type: object
description: The translation results
required:
- full
- partial
- untranslatable
properties:
full:
type: integer
description: The number of rules that have been fully translated.
partial:
type: integer
description: The number of rules that have been partially translated.
untranslatable:
type: integer
description: The number of rules that could not be translated.
installable:
type: integer
description: The number of rules that have been successfully translated and can be installed.
prebuilt:
type: integer
description: The number of rules that have been successfully translated and matched Elastic prebuilt rules.
failed:
type: integer
description: The number of rules that can be installed.

description: The number of rules that have failed translation.
RuleMigrationTranslationResult:
type: string
description: The rule translation result.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { PanelText, type PanelTextProps } from './panel_text';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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, { type PropsWithChildren } from 'react';
import { css, type CSSInterpolation } from '@emotion/css';
import { EuiText, useEuiTheme, COLOR_MODES_STANDARD, type EuiTextProps } from '@elastic/eui';

export interface PanelTextProps extends PropsWithChildren<EuiTextProps> {
subdued?: true;
semiBold?: true;
}
export const PanelText = React.memo<PanelTextProps>(({ children, subdued, semiBold, ...props }) => {
const { euiTheme, colorMode } = useEuiTheme();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark;

let color;
if (subdued && !isDarkMode) {
color = 'subdued';
}

const style: CSSInterpolation = {};
if (semiBold) {
style.fontWeight = euiTheme.font.weight.semiBold;
}

return (
<EuiText {...props} color={color} className={css(style)}>
{children}
</EuiText>
);
});
PanelText.displayName = 'PanelText';
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const getCardHash = (cardId: OnboardingCardId | null) => (cardId ? `#${cardId}`
* This hook manages the expanded card id state in the LocalStorage and the hash in the URL.
*/
export const useUrlDetail = () => {
const { spaceId, telemetry } = useOnboardingContext();
const { config, spaceId, telemetry } = useOnboardingContext();
const topicId = useTopicId();
const [storedUrlDetail, setStoredUrlDetail] = useStoredUrlDetails(spaceId);

Expand Down Expand Up @@ -56,6 +56,14 @@ export const useUrlDetail = () => {

const syncUrlDetails = useCallback(
(pathTopicId: OnboardingTopicId | null, hashCardId: OnboardingCardId | null) => {
if (storedUrlDetail) {
// If the stored topic is not valid, clear it
const [storedTopicId] = storedUrlDetail.split('#');
if (storedTopicId && !config.has(storedTopicId as OnboardingTopicId)) {
setStoredUrlDetail(null);
return;
}
}
const urlDetail = `${pathTopicId || ''}${hashCardId ? `#${hashCardId}` : ''}`;
if (urlDetail && urlDetail !== storedUrlDetail) {
if (hashCardId) {
Expand All @@ -67,7 +75,7 @@ export const useUrlDetail = () => {
navigateTo({ deepLinkId: SecurityPageName.landing, path: storedUrlDetail });
}
},
[navigateTo, setStoredUrlDetail, storedUrlDetail, telemetry]
[config, navigateTo, setStoredUrlDetail, storedUrlDetail, telemetry]
);

return { setTopicDetail, setCardDetail, syncUrlDetails };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import React, { useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { CenteredLoadingSpinner } from '../../../../../../common/components/centered_loading_spinner';
import { useKibana } from '../../../../../../common/lib/kibana/kibana_react';
import { useDefinedLocalStorage } from '../../../../hooks/use_stored_state';
import type { OnboardingCardComponent } from '../../../../../types';
Expand Down Expand Up @@ -35,9 +36,15 @@ export const AIConnectorCard: OnboardingCardComponent<AIConnectorCardMetadata> =
[setComplete, setStoredConnectorId]
);

const connectors = checkCompleteMetadata?.connectors;
const canExecuteConnectors = checkCompleteMetadata?.canExecuteConnectors;
const canCreateConnectors = checkCompleteMetadata?.canCreateConnectors;
if (!checkCompleteMetadata) {
return (
<OnboardingCardContentPanel>
<CenteredLoadingSpinner />
</OnboardingCardContentPanel>
);
}

const { connectors, canExecuteConnectors, canCreateConnectors } = checkCompleteMetadata;

return (
<OnboardingCardContentPanel>
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 303eee8

Please sign in to comment.