[Rules migration] Add comments tab in the details flyout (#11385) (#204084)

## Summary

[Internal link](https://github.com/elastic/security-team/issues/10820)
to the feature details

These changes add a "Summary" tab within the migration rule details
flyout which will show the AI assistant comments about the translation
process of the rule.

## Screenshot

<img width="1675" alt="Screenshot 2024-12-12 at 17 39 30"
src="https://github.com/user-attachments/assets/213c3e80-70df-482a-8dfd-09fc1e2e54c8"
/>
This commit is contained in:
Ievgen Sorokopud 2024-12-14 00:06:35 +01:00 committed by GitHub
parent 5a9129e22d
commit bee7f0f90e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 132 additions and 13 deletions

View file

@ -41,7 +41,7 @@ import {
DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS,
LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS,
} from './constants';
import { TranslationTab } from './translation_tab';
import { SummaryTab, TranslationTab } from './tabs';
import {
convertMigrationCustomRuleToSecurityRulePayload,
isMigrationCustomRule,
@ -174,9 +174,22 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
[ruleDetailsToOverview, size, expandedOverviewSections, toggleOverviewSection]
);
const summaryTab: EuiTabbedContentTab = useMemo(
() => ({
id: 'summary',
name: i18n.SUMMARY_TAB_LABEL,
content: (
<TabContentPadding>
<SummaryTab ruleMigration={ruleMigration} />
</TabContentPadding>
),
}),
[ruleMigration]
);
const tabs = useMemo(() => {
return [...extraTabs, translationTab, overviewTab];
}, [extraTabs, translationTab, overviewTab]);
return [...extraTabs, translationTab, overviewTab, summaryTab];
}, [extraTabs, translationTab, overviewTab, summaryTab]);
const [selectedTabId, setSelectedTabId] = useState<string>(tabs[0].id);
const selectedTab = tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0];

View file

@ -0,0 +1,9 @@
/*
* 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 * from './summary';
export * from './translation';

View file

@ -0,0 +1,54 @@
/*
* 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, { useMemo } from 'react';
import type { EuiCommentProps } from '@elastic/eui';
import { EuiAvatar, EuiCommentList, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
import moment from 'moment';
import { AssistantAvatar } from '@kbn/elastic-assistant';
import {
RuleMigrationStatusEnum,
type RuleMigration,
} from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
import * as i18n from './translations';
interface SummaryTabProps {
ruleMigration: RuleMigration;
}
export const SummaryTab: React.FC<SummaryTabProps> = React.memo(({ ruleMigration }) => {
const timestamp = useMemo(
// Date formats https://momentjs.com/docs/#/displaying/format/
() => moment(ruleMigration['@timestamp']).format('ll'),
[ruleMigration]
);
const comments: EuiCommentProps[] | undefined = useMemo(() => {
return ruleMigration.comments?.map((comment) => {
return {
username: i18n.ASSISTANT_USERNAME,
timelineAvatarAriaLabel: i18n.ASSISTANT_USERNAME,
timelineAvatar: (
<EuiAvatar name="machine" size="l" color="subdued" iconType={AssistantAvatar} />
),
event:
ruleMigration.status === RuleMigrationStatusEnum.failed
? i18n.COMMENT_EVENT_FAILED
: i18n.COMMENT_EVENT_TRANSLATED,
timestamp,
children: <EuiMarkdownFormat textSize="s">{comment}</EuiMarkdownFormat>,
};
});
}, [ruleMigration, timestamp]);
return (
<>
<EuiSpacer size="m" />
<EuiCommentList comments={comments} aria-label={i18n.ASSISTANT_COMMENTS} />
</>
);
});
SummaryTab.displayName = 'SummaryTab';

View file

@ -0,0 +1,36 @@
/*
* 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 ASSISTANT_USERNAME = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.assistantUsername',
{
defaultMessage: 'Assistant',
}
);
export const ASSISTANT_COMMENTS = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentsLabel',
{
defaultMessage: 'Assistant comments',
}
);
export const COMMENT_EVENT_TRANSLATED = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.translatedLabel',
{
defaultMessage: 'created a final translation',
}
);
export const COMMENT_EVENT_FAILED = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.failedLabel',
{
defaultMessage: 'failed to translate',
}
);

View file

@ -12,7 +12,7 @@ import { EuiCallOut } from '@elastic/eui';
import {
RuleMigrationTranslationResultEnum,
type RuleMigrationTranslationResult,
} from '../../../../../../common/siem_migrations/model/rule_migration.gen';
} from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
import * as i18n from './translations';
const getCallOutInfo = (

View file

@ -18,15 +18,15 @@ import {
} from '@elastic/eui';
import { css } from '@emotion/css';
import { FormattedMessage } from '@kbn/i18n-react';
import type { RuleResponse } from '../../../../../../common/api/detection_engine';
import type { RuleMigration } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
import type { RuleResponse } from '../../../../../../../common/api/detection_engine';
import type { RuleMigration } from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
import { TranslationTabHeader } from './header';
import { MigrationRuleQuery } from './migration_rule_query';
import * as i18n from './translations';
import {
convertTranslationResultIntoColor,
convertTranslationResultIntoText,
} from '../../../utils/helpers';
} from '../../../../utils/helpers';
import { TranslationCallOut } from './callout';
interface TranslationTabProps {

View file

@ -17,10 +17,10 @@ import {
useEuiTheme,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { VALIDATION_WARNING_CODES } from '../../../../../detection_engine/rule_creation/constants/validation_warning_codes';
import { useFormWithWarnings } from '../../../../../common/hooks/use_form_with_warnings';
import { EsqlQueryEdit } from '../../../../../detection_engine/rule_creation/components/esql_query_edit';
import { Field, Form, getUseField } from '../../../../../shared_imports';
import { VALIDATION_WARNING_CODES } from '../../../../../../detection_engine/rule_creation/constants/validation_warning_codes';
import { useFormWithWarnings } from '../../../../../../common/hooks/use_form_with_warnings';
import { EsqlQueryEdit } from '../../../../../../detection_engine/rule_creation/components/esql_query_edit';
import { Field, Form, getUseField } from '../../../../../../shared_imports';
import type { RuleTranslationSchema } from './types';
import { schema } from './schema';
import * as i18n from './translations';

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import { queryRequiredValidatorFactory } from '../../../../../detection_engine/rule_creation_ui/validators/query_required_validator_factory';
import { FIELD_TYPES, fieldValidators, type FormSchema } from '../../../../../shared_imports';
import { queryRequiredValidatorFactory } from '../../../../../../detection_engine/rule_creation_ui/validators/query_required_validator_factory';
import { FIELD_TYPES, fieldValidators, type FormSchema } from '../../../../../../shared_imports';
import type { RuleTranslationSchema } from './types';
import * as i18n from './translations';

View file

@ -21,6 +21,13 @@ export const OVERVIEW_TAB_LABEL = i18n.translate(
}
);
export const SUMMARY_TAB_LABEL = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTabLabel',
{
defaultMessage: 'Summary',
}
);
export const TRANSLATION_TAB_LABEL = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTabLabel',
{