Skip to content

Commit

Permalink
[Siem migrations] Onboarding UI changes (#205734)
Browse files Browse the repository at this point in the history
## Summary

Minor fixes and enhancements

### UI changes

#### Icon
Before
<img width="1166" alt="Captura de pantalla 2025-01-14 a les 19 33 25"
src="https://github.com/user-attachments/assets/e651875e-92f1-40be-93da-e5c5b1fa8cd1"
/>

After
<img width="1166" alt="Captura de pantalla 2025-01-14 a les 19 34 19"
src="https://github.com/user-attachments/assets/017d2a95-6817-4949-b678-9d89d3b0ad90"
/>

#### Connector step missing
Before 
<img width="1209" alt="Captura de pantalla 2025-01-14 a les 16 33 27"
src="https://github.com/user-attachments/assets/55546bc9-4f99-490f-910c-26ec82749f7d"
/>

After
<img width="1209" alt="Captura de pantalla 2025-01-14 a les 16 29 36"
src="https://github.com/user-attachments/assets/9f3c50db-5f53-4ac9-8e41-a0e281b8115d"
/>

#### Flyout upload buttons added
Before
<img width="956" alt="Captura de pantalla 2025-01-14 a les 16 36 44"
src="https://github.com/user-attachments/assets/b8111709-8bfe-4a03-baa7-576ca176feb3"
/>

After
<img width="950" alt="Captura de pantalla 2025-01-14 a les 19 30 01"
src="https://github.com/user-attachments/assets/afffb8b7-cf38-4f00-9142-2537bef394f3"
/>

#### Rule migration result card collapsible


https://github.com/user-attachments/assets/4518510c-e215-4015-862b-07d90e38094b

### Other changes

- Rule migration list order inverted to have more recent migrations
first (same on the dropdown of translation rules table)
- Rule migration result table fixes
- Make `retry` logic inside of the `start` API route.
- Fix results chart colors (Eui Borealis)

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
semd and kibanamachine authored Jan 15, 2025
1 parent 56ce5d4 commit 8345223
Show file tree
Hide file tree
Showing 34 changed files with 635 additions and 693 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,6 @@ import type {
InstallMigrationRulesResponse,
InstallTranslatedMigrationRulesRequestParamsInput,
InstallTranslatedMigrationRulesResponse,
RetryRuleMigrationRequestParamsInput,
RetryRuleMigrationRequestBodyInput,
RetryRuleMigrationResponse,
StartRuleMigrationRequestParamsInput,
StartRuleMigrationRequestBodyInput,
StartRuleMigrationResponse,
Expand Down Expand Up @@ -2033,22 +2030,6 @@ detection engine rules.
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Retries a SIEM rules migration using the migration id provided
*/
async retryRuleMigration(props: RetryRuleMigrationProps) {
this.log.info(`${new Date().toISOString()} Calling API RetryRuleMigration`);
return this.kbnClient
.request<RetryRuleMigrationResponse>({
path: replaceParams('/internal/siem_migrations/rules/{migration_id}/retry', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '1',
},
method: 'PUT',
body: props.body,
})
.catch(catchAxiosErrorFormatAndThrow);
}
async riskEngineGetPrivileges() {
this.log.info(`${new Date().toISOString()} Calling API RiskEngineGetPrivileges`);
return this.kbnClient
Expand Down Expand Up @@ -2600,10 +2581,6 @@ export interface ReadRuleProps {
export interface ResolveTimelineProps {
query: ResolveTimelineRequestQueryInput;
}
export interface RetryRuleMigrationProps {
params: RetryRuleMigrationRequestParamsInput;
body: RetryRuleMigrationRequestBodyInput;
}
export interface RulePreviewProps {
query: RulePreviewRequestQueryInput;
body: RulePreviewRequestBodyInput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const SIEM_RULE_MIGRATION_CREATE_PATH =
`${SIEM_RULE_MIGRATIONS_PATH}/{migration_id?}` as const;
export const SIEM_RULE_MIGRATION_PATH = `${SIEM_RULE_MIGRATIONS_PATH}/{migration_id}` as const;
export const SIEM_RULE_MIGRATION_START_PATH = `${SIEM_RULE_MIGRATION_PATH}/start` as const;
export const SIEM_RULE_MIGRATION_RETRY_PATH = `${SIEM_RULE_MIGRATION_PATH}/retry` as const;
export const SIEM_RULE_MIGRATION_STATS_PATH = `${SIEM_RULE_MIGRATION_PATH}/stats` as const;
export const SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH =
`${SIEM_RULE_MIGRATION_PATH}/translation_stats` as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,28 +231,6 @@ export const InstallTranslatedMigrationRulesResponse = z.object({
installed: z.boolean(),
});

export type RetryRuleMigrationRequestParams = z.infer<typeof RetryRuleMigrationRequestParams>;
export const RetryRuleMigrationRequestParams = z.object({
migration_id: NonEmptyString,
});
export type RetryRuleMigrationRequestParamsInput = z.input<typeof RetryRuleMigrationRequestParams>;

export type RetryRuleMigrationRequestBody = z.infer<typeof RetryRuleMigrationRequestBody>;
export const RetryRuleMigrationRequestBody = z.object({
connector_id: ConnectorId,
langsmith_options: LangSmithOptions.optional(),
filter: RuleMigrationRetryFilter.optional(),
});
export type RetryRuleMigrationRequestBodyInput = z.input<typeof RetryRuleMigrationRequestBody>;

export type RetryRuleMigrationResponse = z.infer<typeof RetryRuleMigrationResponse>;
export const RetryRuleMigrationResponse = z.object({
/**
* Indicates the migration retry has been started. `false` means the migration does not need to be retried.
*/
started: z.boolean(),
});

export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
export const StartRuleMigrationRequestParams = z.object({
migration_id: NonEmptyString,
Expand All @@ -263,6 +241,10 @@ export type StartRuleMigrationRequestBody = z.infer<typeof StartRuleMigrationReq
export const StartRuleMigrationRequestBody = z.object({
connector_id: ConnectorId,
langsmith_options: LangSmithOptions.optional(),
/**
* The optional indicator to retry the rule translation based on this filter criteria
*/
retry: RuleMigrationRetryFilter.optional(),
});
export type StartRuleMigrationRequestBodyInput = z.input<typeof StartRuleMigrationRequestBody>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ paths:
$ref: '../../common.schema.yaml#/components/schemas/ConnectorId'
langsmith_options:
$ref: '../../common.schema.yaml#/components/schemas/LangSmithOptions'
retry:
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationRetryFilter'
description: The optional indicator to retry the rule translation based on this filter criteria
responses:
200:
description: Indicates the migration start request has been processed successfully.
Expand All @@ -336,53 +339,6 @@ paths:
204:
description: Indicates the migration id was not found.

/internal/siem_migrations/rules/{migration_id}/retry:
put:
summary: Retries a rule migration
operationId: RetryRuleMigration
x-codegen-enabled: true
x-internal: true
description: Retries a SIEM rules migration using the migration id provided
tags:
- SIEM Rule Migrations
parameters:
- name: migration_id
in: path
required: true
schema:
description: The migration id to retry
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- connector_id
properties:
connector_id:
$ref: '../../common.schema.yaml#/components/schemas/ConnectorId'
langsmith_options:
$ref: '../../common.schema.yaml#/components/schemas/LangSmithOptions'
filter:
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationRetryFilter'
responses:
200:
description: Indicates the migration retry request has been processed successfully.
content:
application/json:
schema:
type: object
required:
- started
properties:
started:
type: boolean
description: Indicates the migration retry has been started. `false` means the migration does not need to be retried.
204:
description: Indicates the migration id was not found.

/internal/siem_migrations/rules/{migration_id}/stats:
get:
summary: Gets a rule migration task stats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
*/

import { z } from '@kbn/zod';
import { isNonEmptyString } from '@kbn/zod-helpers';

/**
* A string that does not contain only whitespace characters
*/
export type NonEmptyString = z.infer<typeof NonEmptyString>;
export const NonEmptyString = z.string().min(1).superRefine(isNonEmptyString);

/**
* The GenAI connector id to use.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ paths: {}
components:
x-codegen-enabled: true
schemas:
NonEmptyString:
type: string
format: nonempty
minLength: 1
description: A string that does not contain only whitespace characters
ConnectorId:
type: string
description: The GenAI connector id to use.

LangSmithOptions:
type: object
description: The LangSmith options object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
*/

import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui';
import { OnboardingCardContentPanel } from '../../common/card_content_panel';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiPanel } from '@elastic/eui';
import { CardCallOut } from '../../common/card_callout';
import * as i18n from './translations';

Expand All @@ -17,7 +16,7 @@ interface MissingAIConnectorCalloutProps {

export const MissingAIConnectorCallout = React.memo<MissingAIConnectorCalloutProps>(
({ onExpandAiConnectorsCard }) => (
<OnboardingCardContentPanel paddingSize="none">
<EuiPanel hasShadow={false} paddingSize="none">
<CardCallOut
color="warning"
text={i18n.START_MIGRATION_CARD_CONNECTOR_MISSING_TEXT}
Expand All @@ -32,7 +31,7 @@ export const MissingAIConnectorCallout = React.memo<MissingAIConnectorCalloutPro
</EuiLink>
}
/>
</OnboardingCardContentPanel>
</EuiPanel>
)
);
MissingAIConnectorCallout.displayName = 'MissingAIConnectorCallout';
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 2.0.
*/

import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants';
import type { RuleMigrationStats } from '../../../../../../siem_migrations/rules/types';
import { UploadRulesPanel } from './upload_rules_panel';
Expand All @@ -22,24 +22,52 @@ export interface RuleMigrationsPanelsProps {
}
export const RuleMigrationsPanels = React.memo<RuleMigrationsPanelsProps>(
({ migrationsStats, isConnectorsCardComplete, expandConnectorsCard }) => {
if (migrationsStats.length === 0) {
return isConnectorsCardComplete ? (
<UploadRulesPanel />
) : (
<MissingAIConnectorCallout onExpandAiConnectorsCard={expandConnectorsCard} />
);
}
const latestMigrationsStats = useMemo(
() => migrationsStats.slice().reverse(),
[migrationsStats]
);

const [expandedCardId, setExpandedCardId] = useState<string | undefined>(() => {
if (latestMigrationsStats[0]?.status === SiemMigrationTaskStatus.FINISHED) {
return latestMigrationsStats[0]?.id;
}
return undefined;
});

useEffect(() => {
if (!expandedCardId && latestMigrationsStats.length > 0) {
const runningMigration = latestMigrationsStats.find(
({ status }) => status === SiemMigrationTaskStatus.RUNNING
);
if (runningMigration) {
setExpandedCardId(runningMigration.id); // Set the next migration to be expanded when it finishes
}
}
}, [latestMigrationsStats, expandedCardId]);

const getOnToggleCollapsed = useCallback(
(id: string) => (isCollapsed: boolean) => {
setExpandedCardId(isCollapsed ? undefined : id);
},
[]
);

return (
<EuiFlexGroup direction="column" gutterSize="m">
<EuiFlexItem grow={false}>
{isConnectorsCardComplete ? (
<UploadRulesPanel isUploadMore />
) : (
<MissingAIConnectorCallout onExpandAiConnectorsCard={expandConnectorsCard} />
{!isConnectorsCardComplete && (
<>
<MissingAIConnectorCallout onExpandAiConnectorsCard={expandConnectorsCard} />
<EuiSpacer size="s" />
</>
)}
<UploadRulesPanel
isUploadMore={latestMigrationsStats.length > 0}
isDisabled={!isConnectorsCardComplete}
/>
</EuiFlexItem>
{migrationsStats.map((migrationStats) => (

{latestMigrationsStats.map((migrationStats) => (
<EuiFlexItem grow={false} key={migrationStats.id}>
{migrationStats.status === SiemMigrationTaskStatus.READY && (
<MigrationReadyPanel migrationStats={migrationStats} />
Expand All @@ -48,7 +76,11 @@ export const RuleMigrationsPanels = React.memo<RuleMigrationsPanelsProps>(
<MigrationProgressPanel migrationStats={migrationStats} />
)}
{migrationStats.status === SiemMigrationTaskStatus.FINISHED && (
<MigrationResultPanel migrationStats={migrationStats} />
<MigrationResultPanel
migrationStats={migrationStats}
isCollapsed={migrationStats.id !== expandedCardId}
onToggleCollapsed={getOnToggleCollapsed(migrationStats.id)}
/>
)}
</EuiFlexItem>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ export const START_MIGRATION_CARD_FOOTER_NOTE = i18n.translate(
export const START_MIGRATION_CARD_CONNECTOR_MISSING_TEXT = i18n.translate(
'xpack.securitySolution.onboarding.startMigration.connectorMissingText',
{
defaultMessage: 'Rule migrations require an AI connector to be configured.',
defaultMessage:
'You need an LLM connector to power SIEM rule migration. Set one up or choose an existing one to get started.',
}
);
export const START_MIGRATION_CARD_CONNECTOR_MISSING_BUTTON = i18n.translate(
'xpack.securitySolution.onboarding.startMigration.connectorMissingText',
{ defaultMessage: 'AI provider step' }
{ defaultMessage: 'Set up AI Connector' }
);

export const START_MIGRATION_CARD_UPLOAD_TITLE = i18n.translate(
Expand Down
Loading

0 comments on commit 8345223

Please sign in to comment.