Skip to content

Commit

Permalink
Add custom highlighted fields through the bulk edit feature (elastic#…
Browse files Browse the repository at this point in the history
  • Loading branch information
e40pud committed Mar 24, 2024
1 parent d4dd309 commit 669d295
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
RuleActionAlertsFilter,
IndexPatternArray,
RuleTagArray,
InvestigationFields,
TimelineTemplateId,
TimelineTemplateTitle,
} from '../../model/rule_schema/common_attributes.gen';
Expand Down Expand Up @@ -187,6 +188,9 @@ export const BulkActionEditType = z.enum([
'add_rule_actions',
'set_rule_actions',
'set_schedule',
'add_investigation_fields',
'delete_investigation_fields',
'set_investigation_fields',
]);
export type BulkActionEditTypeEnum = typeof BulkActionEditType.enum;
export const BulkActionEditTypeEnum = BulkActionEditType.enum;
Expand Down Expand Up @@ -239,6 +243,18 @@ export const BulkActionEditPayloadTags = z.object({
value: RuleTagArray,
});

export type BulkActionEditPayloadInvestigationFields = z.infer<
typeof BulkActionEditPayloadInvestigationFields
>;
export const BulkActionEditPayloadInvestigationFields = z.object({
type: z.enum([
'add_investigation_fields',
'delete_investigation_fields',
'set_investigation_fields',
]),
value: InvestigationFields,
});

export type BulkActionEditPayloadTimeline = z.infer<typeof BulkActionEditPayloadTimeline>;
export const BulkActionEditPayloadTimeline = z.object({
type: z.literal('set_timeline'),
Expand All @@ -252,6 +268,7 @@ export type BulkActionEditPayload = z.infer<typeof BulkActionEditPayload>;
export const BulkActionEditPayload = z.union([
BulkActionEditPayloadTags,
BulkActionEditPayloadIndexPatterns,
BulkActionEditPayloadInvestigationFields,
BulkActionEditPayloadTimeline,
BulkActionEditPayloadRuleActions,
BulkActionEditPayloadSchedule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ components:
- add_rule_actions
- set_rule_actions
- set_schedule
- add_investigation_fields
- delete_investigation_fields
- set_investigation_fields

# Per rulesClient.bulkEdit rules actions operation contract (x-pack/plugins/alerting/server/rules_client/rules_client.ts) normalized rule action object is expected (NormalizedAlertAction) as value for the edit operation
NormalizedRuleAction:
Expand Down Expand Up @@ -381,6 +384,21 @@ components:
- type
- value

BulkActionEditPayloadInvestigationFields:
type: object
properties:
type:
type: string
enum:
- add_investigation_fields
- delete_investigation_fields
- set_investigation_fields
value:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/InvestigationFields'
required:
- type
- value

BulkActionEditPayloadTimeline:
type: object
properties:
Expand All @@ -406,6 +424,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BulkActionEditPayloadTags'
- $ref: '#/components/schemas/BulkActionEditPayloadIndexPatterns'
- $ref: '#/components/schemas/BulkActionEditPayloadInvestigationFields'
- $ref: '#/components/schemas/BulkActionEditPayloadTimeline'
- $ref: '#/components/schemas/BulkActionEditPayloadRuleActions'
- $ref: '#/components/schemas/BulkActionEditPayloadSchedule'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type {
BulkActionEditPayloadIndexPatterns,
BulkActionEditPayloadInvestigationFields,
BulkActionEditPayloadRuleActions,
BulkActionEditPayloadSchedule,
BulkActionEditPayloadTags,
Expand All @@ -26,5 +27,6 @@ export type BulkActionEditForRuleAttributes =
*/
export type BulkActionEditForRuleParams =
| BulkActionEditPayloadIndexPatterns
| BulkActionEditPayloadInvestigationFields
| BulkActionEditPayloadTimeline
| BulkActionEditPayloadSchedule;
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TagsForm } from './forms/tags_form';
import { TimelineTemplateForm } from './forms/timeline_template_form';
import { RuleActionsForm } from './forms/rule_actions_form';
import { ScheduleForm } from './forms/schedule_form';
import { InvestigationFieldsForm } from './forms/investigation_fields_form';

interface BulkEditFlyoutProps {
onClose: () => void;
Expand All @@ -38,6 +39,11 @@ const BulkEditFlyoutComponent = ({ editAction, ...props }: BulkEditFlyoutProps)
case BulkActionEditTypeEnum.set_tags:
return <TagsForm {...props} editAction={editAction} />;

case BulkActionEditTypeEnum.add_investigation_fields:
case BulkActionEditTypeEnum.delete_investigation_fields:
case BulkActionEditTypeEnum.set_investigation_fields:
return <InvestigationFieldsForm {...props} editAction={editAction} />;

case BulkActionEditTypeEnum.set_timeline:
return <TimelineTemplateForm {...props} />;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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 from 'react';
import { EuiFormRow, EuiCallOut } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';

import * as i18n from '../../../../../../detections/pages/detection_engine/rules/translations';

import { DEFAULT_INDEX_KEY } from '../../../../../../../common/constants';
import { useFetchIndex } from '../../../../../../common/containers/source';
import { useKibana } from '../../../../../../common/lib/kibana';

import { BulkActionEditTypeEnum } from '../../../../../../../common/api/detection_engine/rule_management';
import type { BulkActionEditPayload } from '../../../../../../../common/api/detection_engine/rule_management';

import type { FormSchema } from '../../../../../../shared_imports';
import {
Field,
getUseField,
useFormData,
useForm,
FIELD_TYPES,
fieldValidators,
} from '../../../../../../shared_imports';

import { BulkEditFormWrapper } from './bulk_edit_form_wrapper';
const CommonUseField = getUseField({ component: Field });

type InvestigationFieldsEditActions =
| BulkActionEditTypeEnum['add_investigation_fields']
| BulkActionEditTypeEnum['delete_investigation_fields']
| BulkActionEditTypeEnum['set_investigation_fields'];

interface InvestigationFieldsFormData {
investigationFields: string[];
overwrite: boolean;
}

const schema: FormSchema<InvestigationFieldsFormData> = {
investigationFields: {
fieldsToValidateOnChange: ['investigationFields'],
type: FIELD_TYPES.COMBO_BOX,
validations: [
{
validator: fieldValidators.emptyField(
i18n.BULK_EDIT_FLYOUT_FORM_ADD_INVESTIGATION_FIELDS_REQUIRED_ERROR
),
},
],
},
overwrite: {
type: FIELD_TYPES.CHECKBOX,
label: i18n.BULK_EDIT_FLYOUT_FORM_ADD_INVESTIGATION_FIELDS_OVERWRITE_LABEL,
},
};

const initialFormData: InvestigationFieldsFormData = {
investigationFields: [],
overwrite: false,
};

const getFormConfig = (editAction: InvestigationFieldsEditActions) =>
editAction === BulkActionEditTypeEnum.add_investigation_fields
? {
indexLabel: i18n.BULK_EDIT_FLYOUT_FORM_ADD_INVESTIGATION_FIELDS_LABEL,
indexHelpText: i18n.BULK_EDIT_FLYOUT_FORM_ADD_INVESTIGATION_FIELDS_HELP_TEXT,
formTitle: i18n.BULK_EDIT_FLYOUT_FORM_ADD_INVESTIGATION_FIELDS_TITLE,
}
: {
indexLabel: i18n.BULK_EDIT_FLYOUT_FORM_DELETE_INVESTIGATION_FIELDS_LABEL,
indexHelpText: i18n.BULK_EDIT_FLYOUT_FORM_DELETE_INVESTIGATION_FIELDS_HELP_TEXT,
formTitle: i18n.BULK_EDIT_FLYOUT_FORM_DELETE_INVESTIGATION_FIELDS_TITLE,
};

interface InvestigationFieldsFormProps {
editAction: InvestigationFieldsEditActions;
rulesCount: number;
onClose: () => void;
onConfirm: (bulkActionEditPayload: BulkActionEditPayload) => void;
}

const InvestigationFieldsFormComponent = ({
editAction,
rulesCount,
onClose,
onConfirm,
}: InvestigationFieldsFormProps) => {
const { form } = useForm({
defaultValue: initialFormData,
schema,
});

const { indexHelpText, indexLabel, formTitle } = getFormConfig(editAction);

const [{ overwrite }] = useFormData({
form,
watch: ['overwrite'],
});
const { uiSettings } = useKibana().services;
const defaultPatterns = uiSettings.get<string[]>(DEFAULT_INDEX_KEY);
const [_, { indexPatterns }] = useFetchIndex(defaultPatterns, false);
const fieldOptions = indexPatterns.fields.map((field) => ({
label: field.name,
}));

const handleSubmit = async () => {
const { data, isValid } = await form.submit();
if (!isValid) {
return;
}

onConfirm({
value: { field_names: data.investigationFields },
type: data.overwrite ? BulkActionEditTypeEnum.set_investigation_fields : editAction,
});
};

return (
<BulkEditFormWrapper form={form} onClose={onClose} onSubmit={handleSubmit} title={formTitle}>
<CommonUseField
path="investigationFields"
config={{ ...schema.investigationFields, label: indexLabel, helpText: indexHelpText }}
componentProps={{
idAria: 'bulkEditRulesInvestigationFields',
'data-test-subj': 'bulkEditRulesInvestigationFields',
euiFieldProps: {
fullWidth: true,
placeholder: '',
noSuggestions: false,
options: fieldOptions,
},
}}
/>
{editAction === BulkActionEditTypeEnum.add_investigation_fields && (
<CommonUseField
path="overwrite"
componentProps={{
idAria: 'bulkEditRulesOverwriteInvestigationFields',
'data-test-subj': 'bulkEditRulesOverwriteInvestigationFields',
}}
/>
)}
{overwrite && (
<EuiFormRow fullWidth>
<EuiCallOut
color="warning"
size="s"
data-test-subj="bulkEditRulesInvestigationFieldsWarning"
>
<FormattedMessage
id="xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setInvestigationFieldsWarningCallout"
defaultMessage="You’re about to overwrite custom highlighted fields for {rulesCount, plural, one {# selected rule} other {# selected rules}}, press Save to
apply changes."
values={{ rulesCount }}
/>
</EuiCallOut>
</EuiFormRow>
)}
</BulkEditFormWrapper>
);
};

export const InvestigationFieldsForm = React.memo(InvestigationFieldsFormComponent);
InvestigationFieldsForm.displayName = 'InvestigationFieldsForm';
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,13 @@ export const useBulkActions = ({
disabled: isEditDisabled,
panel: 1,
},
{
key: i18n.BULK_ACTION_INVESTIGATION_FIELDS,
name: i18n.BULK_ACTION_INVESTIGATION_FIELDS,
'data-test-subj': 'investigationFieldsBulkEditRule',
disabled: isEditDisabled,
panel: 3,
},
{
key: i18n.BULK_ACTION_ADD_RULE_ACTIONS,
name: i18n.BULK_ACTION_ADD_RULE_ACTIONS,
Expand Down Expand Up @@ -458,6 +465,34 @@ export const useBulkActions = ({
},
],
},
{
id: 3,
title: i18n.BULK_ACTION_MENU_TITLE,
items: [
{
key: i18n.BULK_ACTION_ADD_INVESTIGATION_FIELDS,
name: i18n.BULK_ACTION_ADD_INVESTIGATION_FIELDS,
'data-test-subj': 'addInvestigationFieldsBulkEditRule',
onClick: handleBulkEdit(BulkActionEditTypeEnum.add_investigation_fields),
disabled: isEditDisabled,
toolTipContent: missingActionPrivileges
? i18n.LACK_OF_KIBANA_ACTIONS_FEATURE_PRIVILEGES
: undefined,
toolTipProps: { position: 'right' },
},
{
key: i18n.BULK_ACTION_DELETE_INVESTIGATION_FIELDS,
name: i18n.BULK_ACTION_DELETE_INVESTIGATION_FIELDS,
'data-test-subj': 'deleteInvestigationFieldsBulkEditRule',
onClick: handleBulkEdit(BulkActionEditTypeEnum.delete_investigation_fields),
disabled: isEditDisabled,
toolTipContent: missingActionPrivileges
? i18n.LACK_OF_KIBANA_ACTIONS_FEATURE_PRIVILEGES
: undefined,
toolTipProps: { position: 'right' },
},
],
},
];
},
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ import { assertUnreachable } from '../../../../../../../common/utility_types';
*/
export function computeDryRunEditPayload(editAction: BulkActionEditType): BulkActionEditPayload[] {
switch (editAction) {
case BulkActionEditTypeEnum.add_investigation_fields:
case BulkActionEditTypeEnum.delete_investigation_fields:
case BulkActionEditTypeEnum.set_investigation_fields:
return [
{
type: editAction,
value: { field_names: ['@timestamp'] },
},
];

case BulkActionEditTypeEnum.add_index_patterns:
case BulkActionEditTypeEnum.delete_index_patterns:
case BulkActionEditTypeEnum.set_index_patterns:
Expand Down
Loading

0 comments on commit 669d295

Please sign in to comment.