Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Siem migrations] Onboarding UI changes (#205734) #206863

Merged
merged 1 commit into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,6 @@ import type {
InstallMigrationRulesResponse,
InstallTranslatedMigrationRulesRequestParamsInput,
InstallTranslatedMigrationRulesResponse,
RetryRuleMigrationRequestParamsInput,
RetryRuleMigrationRequestBodyInput,
RetryRuleMigrationResponse,
StartRuleMigrationRequestParamsInput,
StartRuleMigrationRequestBodyInput,
StartRuleMigrationResponse,
Expand Down Expand Up @@ -2083,22 +2080,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 @@ -2656,10 +2637,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
Loading