Skip to content

Commit

Permalink
[8.x] [Security Solution] ThreeWayDiff UI: Add remaining field compon…
Browse files Browse the repository at this point in the history
…ents for `FieldReadOnly` (#193261) (#193825)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution] ThreeWayDiff UI: Add remaining field components
for `FieldReadOnly`
(#193261)](#193261)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nikita
Indik","email":"nikita.indik@elastic.co"},"sourceCommit":{"committedDate":"2024-09-24T08:04:38Z","message":"[Security
Solution] ThreeWayDiff UI: Add remaining field components for
`FieldReadOnly` (#193261)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\nThis is the
3rd of the 3 PRs for `FieldReadOnly`.\r\n- The 1st
[PR](#191499) added
the\r\n`FieldReadOnly` and a bunch of field components.\r\n- The 2nd
[PR](#192342) moved away\r\nfrom
using `DiffableAllFields` type in favour of `DiffableRule` and\r\nsplit
the large `FieldReadOnly` component into smaller ones
for\r\nreadability.\r\n - This (3rd) PR adds the remaining field
components.\r\n\r\n## Summary\r\n\r\nThis PR adds field components for
`FieldReadOnly`. Field components\r\ndisplay a read-only view of a
particular `DiffableRule` field, similar\r\nto how fields are shown on
the Rule Details page.\r\n\r\n`FieldReadOnly` and field components will
be displayed in the right side\r\nof the new Diff tab of the Upgrade
flyout (see it on the
[Miro\r\nboard](https://miro.com/app/board/uXjVK0gqjjQ=/?moveToWidget=3458764594148126123&cot=14)).\r\nThey
will let the user see how an upgraded version of a rule will
look\r\nlike in a user-friendly way.\r\n\r\n\r\n###
Running\r\n`FinalReadOnly` and its field components are not yet
integrated into the\r\nflyout, but you can view components in
Storybook.\r\n1. Run Storybook: `yarn storybook security_solution`\r\n2.
Go to `http://localhost:9001` in browser.\r\n\r\n<img width=\"1062\"
alt=\"Scherm­afbeelding 2024-09-03 om 13 05
11\"\r\nsrc=\"https://github.com/user-attachments/assets/13b227d4-1321-47d9-a0a7-93868c9f4a15\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Maxim Palenov
<maxim.palenov@elastic.co>","sha":"b78b6337970b2e7332266cd0e181e14d26c3ed45","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Detections
and Resp","Team: SecuritySolution","Team:Detection Rule
Management","Feature:Prebuilt Detection
Rules","backport:prev-minor"],"title":"[Security Solution] ThreeWayDiff
UI: Add remaining field components for
`FieldReadOnly`","number":193261,"url":"https://github.com/elastic/kibana/pull/193261","mergeCommit":{"message":"[Security
Solution] ThreeWayDiff UI: Add remaining field components for
`FieldReadOnly` (#193261)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\nThis is the
3rd of the 3 PRs for `FieldReadOnly`.\r\n- The 1st
[PR](#191499) added
the\r\n`FieldReadOnly` and a bunch of field components.\r\n- The 2nd
[PR](#192342) moved away\r\nfrom
using `DiffableAllFields` type in favour of `DiffableRule` and\r\nsplit
the large `FieldReadOnly` component into smaller ones
for\r\nreadability.\r\n - This (3rd) PR adds the remaining field
components.\r\n\r\n## Summary\r\n\r\nThis PR adds field components for
`FieldReadOnly`. Field components\r\ndisplay a read-only view of a
particular `DiffableRule` field, similar\r\nto how fields are shown on
the Rule Details page.\r\n\r\n`FieldReadOnly` and field components will
be displayed in the right side\r\nof the new Diff tab of the Upgrade
flyout (see it on the
[Miro\r\nboard](https://miro.com/app/board/uXjVK0gqjjQ=/?moveToWidget=3458764594148126123&cot=14)).\r\nThey
will let the user see how an upgraded version of a rule will
look\r\nlike in a user-friendly way.\r\n\r\n\r\n###
Running\r\n`FinalReadOnly` and its field components are not yet
integrated into the\r\nflyout, but you can view components in
Storybook.\r\n1. Run Storybook: `yarn storybook security_solution`\r\n2.
Go to `http://localhost:9001` in browser.\r\n\r\n<img width=\"1062\"
alt=\"Scherm­afbeelding 2024-09-03 om 13 05
11\"\r\nsrc=\"https://github.com/user-attachments/assets/13b227d4-1321-47d9-a0a7-93868c9f4a15\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Maxim Palenov
<maxim.palenov@elastic.co>","sha":"b78b6337970b2e7332266cd0e181e14d26c3ed45"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193261","number":193261,"mergeCommit":{"message":"[Security
Solution] ThreeWayDiff UI: Add remaining field components for
`FieldReadOnly` (#193261)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\nThis is the
3rd of the 3 PRs for `FieldReadOnly`.\r\n- The 1st
[PR](#191499) added
the\r\n`FieldReadOnly` and a bunch of field components.\r\n- The 2nd
[PR](#192342) moved away\r\nfrom
using `DiffableAllFields` type in favour of `DiffableRule` and\r\nsplit
the large `FieldReadOnly` component into smaller ones
for\r\nreadability.\r\n - This (3rd) PR adds the remaining field
components.\r\n\r\n## Summary\r\n\r\nThis PR adds field components for
`FieldReadOnly`. Field components\r\ndisplay a read-only view of a
particular `DiffableRule` field, similar\r\nto how fields are shown on
the Rule Details page.\r\n\r\n`FieldReadOnly` and field components will
be displayed in the right side\r\nof the new Diff tab of the Upgrade
flyout (see it on the
[Miro\r\nboard](https://miro.com/app/board/uXjVK0gqjjQ=/?moveToWidget=3458764594148126123&cot=14)).\r\nThey
will let the user see how an upgraded version of a rule will
look\r\nlike in a user-friendly way.\r\n\r\n\r\n###
Running\r\n`FinalReadOnly` and its field components are not yet
integrated into the\r\nflyout, but you can view components in
Storybook.\r\n1. Run Storybook: `yarn storybook security_solution`\r\n2.
Go to `http://localhost:9001` in browser.\r\n\r\n<img width=\"1062\"
alt=\"Scherm­afbeelding 2024-09-03 om 13 05
11\"\r\nsrc=\"https://github.com/user-attachments/assets/13b227d4-1321-47d9-a0a7-93868c9f4a15\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Maxim Palenov
<maxim.palenov@elastic.co>","sha":"b78b6337970b2e7332266cd0e181e14d26c3ed45"}}]}]
BACKPORT-->

Co-authored-by: Nikita Indik <nikita.indik@elastic.co>
  • Loading branch information
kibanamachine and nikitaindik authored Sep 24, 2024
1 parent 0fa8eb3 commit 13c26d4
Show file tree
Hide file tree
Showing 71 changed files with 2,079 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { COLORS } from './constants';

/*
Finds an element with a text content that exactly matches the passed argument.
Handly because React Testing Library's doesn't provide an easy way to search by
Handy because React Testing Library's doesn't provide an easy way to search by
text if the text is split into multiple DOM elements.
*/
function findChildByTextContent(parent: Element, textContent: string): HTMLElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ interface AuthorProps {
author: string[];
}

const Author = ({ author }: AuthorProps) => (
export const Author = ({ author }: AuthorProps) => (
<BadgeList badges={author} data-test-subj="authorPropertyValue" />
);

const BuildingBlock = () => (
export const BuildingBlock = () => (
<EuiText size="s" data-test-subj="buildingBlockPropertyValue">
{i18n.BUILDING_BLOCK_FIELD_DESCRIPTION}
</EuiText>
Expand Down Expand Up @@ -124,7 +124,7 @@ interface RiskScoreProps {
riskScore: number;
}

const RiskScore = ({ riskScore }: RiskScoreProps) => (
export const RiskScore = ({ riskScore }: RiskScoreProps) => (
<EuiText size="s" data-test-subj="riskScorePropertyValue">
{riskScore}
</EuiText>
Expand Down Expand Up @@ -157,7 +157,7 @@ interface ReferencesProps {
references: string[];
}

const References = ({ references }: ReferencesProps) => (
export const References = ({ references }: ReferencesProps) => (
<EuiText size="s">
<ul>
{references
Expand All @@ -173,7 +173,7 @@ const References = ({ references }: ReferencesProps) => (
</EuiText>
);

const FalsePositives = ({ falsePositives }: { falsePositives: string[] }) => (
export const FalsePositives = ({ falsePositives }: { falsePositives: string[] }) => (
<EuiText size="s">
<ul>
{falsePositives.map((falsePositivesItem) => (
Expand All @@ -192,15 +192,15 @@ interface InvestigationFieldsProps {
investigationFields: string[];
}

const InvestigationFields = ({ investigationFields }: InvestigationFieldsProps) => (
export const InvestigationFields = ({ investigationFields }: InvestigationFieldsProps) => (
<BadgeList badges={investigationFields} data-test-subj="investigationFieldsPropertyValue" />
);

interface LicenseProps {
license: string;
}

const License = ({ license }: LicenseProps) => (
export const License = ({ license }: LicenseProps) => (
<EuiText size="s" data-test-subj="licensePropertyValue">
{license}
</EuiText>
Expand All @@ -210,7 +210,7 @@ interface RuleNameOverrideProps {
ruleNameOverride: string;
}

const RuleNameOverride = ({ ruleNameOverride }: RuleNameOverrideProps) => (
export const RuleNameOverride = ({ ruleNameOverride }: RuleNameOverrideProps) => (
<EuiText size="s" data-test-subj="ruleNameOverridePropertyValue">
{ruleNameOverride}
</EuiText>
Expand All @@ -236,7 +236,7 @@ interface TimestampOverrideProps {
timestampOverride: string;
}

const TimestampOverride = ({ timestampOverride }: TimestampOverrideProps) => (
export const TimestampOverride = ({ timestampOverride }: TimestampOverrideProps) => (
<EuiText size="s" data-test-subj="timestampOverridePropertyValue">
{timestampOverride}
</EuiText>
Expand All @@ -246,7 +246,7 @@ interface MaxSignalsProps {
maxSignals: number;
}

const MaxSignals = ({ maxSignals }: MaxSignalsProps) => (
export const MaxSignals = ({ maxSignals }: MaxSignalsProps) => (
<EuiText size="s" data-test-subj="maxSignalsPropertyValue">
{maxSignals}
</EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ interface ThresholdProps {
threshold: ThresholdType;
}

const Threshold = ({ threshold }: ThresholdProps) => (
export const Threshold = ({ threshold }: ThresholdProps) => (
<div data-test-subj="thresholdPropertyValue">
{isEmpty(threshold.field[0])
? `${descriptionStepI18n.THRESHOLD_RESULTS_ALL} >= ${threshold.value}`
Expand All @@ -193,7 +193,7 @@ interface AnomalyThresholdProps {
anomalyThreshold: number;
}

const AnomalyThreshold = ({ anomalyThreshold }: AnomalyThresholdProps) => (
export const AnomalyThreshold = ({ anomalyThreshold }: AnomalyThresholdProps) => (
<EuiText size="s" data-test-subj="anomalyThresholdPropertyValue">
{anomalyThreshold}
</EuiText>
Expand Down Expand Up @@ -258,7 +258,7 @@ interface RuleTypeProps {
type: Type;
}

const RuleType = ({ type }: RuleTypeProps) => (
export const RuleType = ({ type }: RuleTypeProps) => (
<EuiText size="s">{getRuleTypeDescription(type)}</EuiText>
);

Expand Down Expand Up @@ -298,7 +298,7 @@ interface TimelineTitleProps {
timelineTitle: string;
}

const TimelineTitle = ({ timelineTitle }: TimelineTitleProps) => (
export const TimelineTitle = ({ timelineTitle }: TimelineTitleProps) => (
<EuiText size="s" data-test-subj="timelineTemplatePropertyValue">
{timelineTitle}
</EuiText>
Expand Down Expand Up @@ -354,15 +354,15 @@ interface SuppressAlertsByFieldProps {
fields: string[];
}

const SuppressAlertsByField = ({ fields }: SuppressAlertsByFieldProps) => (
export const SuppressAlertsByField = ({ fields }: SuppressAlertsByFieldProps) => (
<BadgeList badges={fields} data-test-subj="alertSuppressionGroupByPropertyValue" />
);

interface SuppressAlertsDurationProps {
duration?: Duration;
}

const SuppressAlertsDuration = ({ duration }: SuppressAlertsDurationProps) => {
export const SuppressAlertsDuration = ({ duration }: SuppressAlertsDurationProps) => {
const durationDescription = duration
? `${duration.value}${duration.unit}`
: descriptionStepI18n.ALERT_SUPPRESSION_PER_RULE_EXECUTION;
Expand All @@ -378,7 +378,7 @@ interface MissingFieldsStrategyProps {
missingFieldsStrategy?: AlertSuppressionMissingFieldsStrategy;
}

const MissingFieldsStrategy = ({ missingFieldsStrategy }: MissingFieldsStrategyProps) => {
export const MissingFieldsStrategy = ({ missingFieldsStrategy }: MissingFieldsStrategyProps) => {
const missingFieldsDescription =
missingFieldsStrategy === AlertSuppressionMissingFieldsStrategyEnum.suppress
? descriptionStepI18n.ALERT_SUPPRESSION_SUPPRESS_ON_MISSING_FIELDS
Expand All @@ -395,15 +395,15 @@ interface NewTermsFieldsProps {
newTermsFields: string[];
}

const NewTermsFields = ({ newTermsFields }: NewTermsFieldsProps) => (
export const NewTermsFields = ({ newTermsFields }: NewTermsFieldsProps) => (
<BadgeList badges={newTermsFields} data-test-subj="newTermsFieldsPropertyValue" />
);

interface HistoryWindowSizeProps {
historyWindowStart?: string;
}

const HistoryWindowSize = ({ historyWindowStart }: HistoryWindowSizeProps) => {
export const HistoryWindowSize = ({ historyWindowStart }: HistoryWindowSizeProps) => {
const size = historyWindowStart ? convertHistoryStartToSize(historyWindowStart) : '7d';

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@ import { getHumanizedDuration } from '../../../../detections/pages/detection_eng
import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants';
import * as i18n from './translations';

interface AccessibleTimeValueProps {
timeValue: string;
'data-test-subj'?: string;
}

export const AccessibleTimeValue = ({
timeValue,
'data-test-subj': dataTestSubj,
}: AccessibleTimeValueProps) => (
<EuiText size="s" data-test-subj={dataTestSubj}>
<IntervalAbbrScreenReader interval={timeValue} />
</EuiText>
);

interface IntervalProps {
interval: string;
}

const Interval = ({ interval }: IntervalProps) => (
<EuiText size="s" data-test-subj="intervalPropertyValue">
<IntervalAbbrScreenReader interval={interval} />
</EuiText>
<AccessibleTimeValue timeValue={interval} data-test-subj="intervalPropertyValue" />
);

interface FromProps {
Expand All @@ -30,9 +42,10 @@ interface FromProps {
}

const From = ({ from, interval }: FromProps) => (
<EuiText size="s" data-test-subj={`fromPropertyValue-${from}`}>
<IntervalAbbrScreenReader interval={getHumanizedDuration(from, interval)} />
</EuiText>
<AccessibleTimeValue
timeValue={getHumanizedDuration(from, interval)}
data-test-subj={`fromPropertyValue-${from}`}
/>
);

export interface RuleScheduleSectionProps extends React.ComponentProps<typeof EuiDescriptionList> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ import { NameReadOnly } from './fields/name/name';
import { TagsReadOnly } from './fields/tags/tags';
import { DescriptionReadOnly } from './fields/description/description';
import { assertUnreachable } from '../../../../../../../common/utility_types';
import { AuthorReadOnly } from './fields/author/author';
import { BuildingBlockReadOnly } from './fields/building_block/building_block';
import { InvestigationFieldsReadOnly } from './fields/investigation_fields/investigation_fields';
import { FalsePositivesReadOnly } from './fields/false_positives/false_positives';
import { LicenseReadOnly } from './fields/license/license';
import { MaxSignalsReadOnly } from './fields/max_signals/max_signals';
import { NoteReadOnly } from './fields/note/note';
import { RuleScheduleReadOnly } from './fields/rule_schedule/rule_schedule';
import { ReferencesReadOnly } from './fields/references/references';
import { RiskScoreReadOnly } from './fields/risk_score/risk_score';
import { RuleNameOverrideReadOnly } from './fields/rule_name_override/rule_name_override';
import { SetupReadOnly } from './fields/setup/setup';
import { SeverityReadOnly } from './fields/severity/severity';
import { TimestampOverrideReadOnly } from './fields/timestamp_override/timestamp_override';
import { TimelineTemplateReadOnly } from './fields/timeline_template/timeline_template';

interface CommonRuleFieldReadOnlyProps {
fieldName: keyof DiffableCommonFields;
Expand All @@ -32,25 +47,28 @@ export function CommonRuleFieldReadOnly({
}: CommonRuleFieldReadOnlyProps) {
switch (fieldName) {
case 'author':
return null;
return <AuthorReadOnly author={finalDiffableRule.author} />;
case 'building_block':
return null;
return <BuildingBlockReadOnly />;
case 'description':
return <DescriptionReadOnly description={finalDiffableRule.description} />;
case 'exceptions_list':
/* Exceptions are not used in prebuilt rules */
return null;
case 'investigation_fields':
return null;
return (
<InvestigationFieldsReadOnly investigationFields={finalDiffableRule.investigation_fields} />
);
case 'false_positives':
return null;
return <FalsePositivesReadOnly falsePositives={finalDiffableRule.false_positives} />;
case 'license':
return null;
return <LicenseReadOnly license={finalDiffableRule.license} />;
case 'max_signals':
return null;
return <MaxSignalsReadOnly maxSignals={finalDiffableRule.max_signals} />;
case 'name':
return <NameReadOnly name={finalDiffableRule.name} />;
case 'note':
return null;
return <NoteReadOnly note={finalDiffableRule.note} />;
case 'related_integrations':
return (
<RelatedIntegrationsReadOnly relatedIntegrations={finalDiffableRule.related_integrations} />
Expand All @@ -60,30 +78,32 @@ export function CommonRuleFieldReadOnly({
case 'risk_score_mapping':
return <RiskScoreMappingReadOnly riskScoreMapping={finalDiffableRule.risk_score_mapping} />;
case 'rule_schedule':
return null;
return <RuleScheduleReadOnly ruleSchedule={finalDiffableRule.rule_schedule} />;
case 'severity_mapping':
return <SeverityMappingReadOnly severityMapping={finalDiffableRule.severity_mapping} />;
case 'tags':
return <TagsReadOnly tags={finalDiffableRule.tags} />;
case 'threat':
return <ThreatReadOnly threat={finalDiffableRule.threat} />;
case 'references':
return null;
return <ReferencesReadOnly references={finalDiffableRule.references} />;
case 'risk_score':
return null;
return <RiskScoreReadOnly riskScore={finalDiffableRule.risk_score} />;
case 'rule_id':
/* Rule ID is not displayed in the UI */
return null;
case 'rule_name_override':
return null;
return <RuleNameOverrideReadOnly ruleNameOverride={finalDiffableRule.rule_name_override} />;
case 'setup':
return null;
return <SetupReadOnly setup={finalDiffableRule.setup} />;
case 'severity':
return null;
return <SeverityReadOnly severity={finalDiffableRule.severity} />;
case 'timestamp_override':
return null;
return <TimestampOverrideReadOnly timestampOverride={finalDiffableRule.timestamp_override} />;
case 'timeline_template':
return null;
return <TimelineTemplateReadOnly timelineTemplate={finalDiffableRule.timeline_template} />;
case 'version':
/* Version is not displayed in the UI */
return null;
default:
return assertUnreachable(fieldName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import React from 'react';
import type { DiffableCustomQueryFields } from '../../../../../../../common/api/detection_engine';
import { DataSourceReadOnly } from './fields/data_source/data_source';
import { KqlQueryReadOnly } from './fields/kql_query';
import { assertUnreachable } from '../../../../../../../common/utility_types';
import { TypeReadOnly } from './fields/type/type';
import { AlertSuppressionReadOnly } from './fields/alert_suppression/alert_suppression';

interface CustomQueryRuleFieldReadOnlyProps {
fieldName: keyof DiffableCustomQueryFields;
Expand All @@ -20,6 +23,13 @@ export function CustomQueryRuleFieldReadOnly({
finalDiffableRule,
}: CustomQueryRuleFieldReadOnlyProps) {
switch (fieldName) {
case 'alert_suppression':
return (
<AlertSuppressionReadOnly
alertSuppression={finalDiffableRule.alert_suppression}
ruleType={finalDiffableRule.type}
/>
);
case 'data_source':
return <DataSourceReadOnly dataSource={finalDiffableRule.data_source} />;
case 'kql_query':
Expand All @@ -30,7 +40,9 @@ export function CustomQueryRuleFieldReadOnly({
ruleType={finalDiffableRule.type}
/>
);
case 'type':
return <TypeReadOnly type={finalDiffableRule.type} />;
default:
return null; // Will replace with `assertUnreachable(fieldName)` once all fields are implemented
return assertUnreachable(fieldName);
}
}
Loading

0 comments on commit 13c26d4

Please sign in to comment.