From c191104ba1b2b12d693e8e9e2b0f34e092da1e9a Mon Sep 17 00:00:00 2001 From: Nikita Indik Date: Mon, 19 Jun 2023 16:22:02 +0000 Subject: [PATCH] [Security Solution] Show rule actions on Rule Details page (#158189) **Resolves: https://github.com/elastic/kibana/issues/154879** ## Summary Adds a list of notification and response actions to the Rule Details page. Screenshot 2023-06-05 at 11 42 24 ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../pages/rule_creation/helpers.test.ts | 6 + .../pages/rule_creation/helpers.ts | 2 +- .../pages/rule_details/index.tsx | 25 +++- .../rule_management/api/api.ts | 18 +++ .../rule_management/api/hooks/translations.ts | 29 +++++ .../api/hooks/use_fetch_connectors.ts | 46 +++++++ .../rules/step_rule_actions/index.test.tsx | 112 ++++++++++++++++ .../rules/step_rule_actions/index.tsx | 86 ++++++++++-- .../step_rule_actions/notification_action.tsx | 123 ++++++++++++++++++ .../step_rule_actions/response_action.tsx | 34 +++++ .../rules/step_rule_actions/translations.tsx | 34 +++++ 11 files changed, 502 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_connectors.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/notification_action.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/response_action.tsx diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index 8b7a9c62b1541..594a236955d14 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -85,6 +85,12 @@ describe('helpers', () => { expect(result).toEqual({ unit: 'ms', value: 0 }); }); + + test('returns timeObj with unit of d and value 5 when time is 5d ', () => { + const result = getTimeTypeValue('5d'); + + expect(result).toEqual({ unit: 'd', value: 5 }); + }); }); describe('filterEmptyThreats', () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index e283c8aae095b..fbf57f48fe150 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -62,7 +62,7 @@ export const getTimeTypeValue = (time: string): { unit: Unit; value: number } => if ( !isEmpty(filterTimeType) && filterTimeType != null && - ['s', 'm', 'h'].includes(filterTimeType[0]) + ['s', 'm', 'h', 'd'].includes(filterTimeType[0]) ) { timeObj.unit = filterTimeType[0] as Unit; } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index c02aa7fdca34c..336e697cbdb8f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -67,6 +67,7 @@ import { AlertsHistogramPanel } from '../../../../detections/components/alerts_k import { useUserData } from '../../../../detections/components/user_info'; import { StepDefineRuleReadOnly } from '../../../../detections/components/rules/step_define_rule'; import { StepScheduleRuleReadOnly } from '../../../../detections/components/rules/step_schedule_rule'; +import { StepRuleActionsReadOnly } from '../../../../detections/components/rules/step_rule_actions'; import { buildAlertsFilter, buildAlertStatusFilter, @@ -290,7 +291,13 @@ const RuleDetailsPageComponent: React.FC = ({ const [pageTabs, setTabs] = useState>>(ruleDetailTabs); - const { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData } = + const { + aboutRuleData, + modifiedAboutRuleDetailsData, + defineRuleData, + scheduleRuleData, + ruleActionsData, + } = rule != null ? getStepsData({ rule, detailsView: true }) : { @@ -298,6 +305,7 @@ const RuleDetailsPageComponent: React.FC = ({ modifiedAboutRuleDetailsData: null, defineRuleData: null, scheduleRuleData: null, + ruleActionsData: null, }; const [dataViewTitle, setDataViewTitle] = useState(); useEffect(() => { @@ -644,6 +652,11 @@ const RuleDetailsPageComponent: React.FC = ({ const defaultRuleStackByOption: AlertsStackByField = 'event.category'; + const hasNotificationActions = ruleActionsData != null && ruleActionsData.actions.length > 0; + const hasResponseActions = + ruleActionsData != null && (ruleActionsData.responseActions || []).length > 0; + const hasActions = hasNotificationActions || hasResponseActions; + return ( <> @@ -791,6 +804,16 @@ const RuleDetailsPageComponent: React.FC = ({ )} + {hasActions && ( + + + + + + )} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts index aff10f8a817aa..796ac3513f67b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts @@ -10,6 +10,9 @@ import type { ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/common'; +import { BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; +import type { ActionType, AsApiContract } from '@kbn/actions-plugin/common'; +import type { ActionResult } from '@kbn/actions-plugin/server'; import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common'; import { epmRouteService } from '@kbn/fleet-plugin/common'; import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; @@ -236,6 +239,21 @@ export const fetchRulesSnoozeSettings = async ({ return result; }, {} as RulesSnoozeSettingsMap); }; + +export const fetchConnectors = ( + signal?: AbortSignal +): Promise>> => + KibanaServices.get().http.fetch(`${BASE_ACTION_API_PATH}/connectors`, { method: 'GET', signal }); + +export const fetchConnectorTypes = (signal?: AbortSignal): Promise => + KibanaServices.get().http.fetch(`${BASE_ACTION_API_PATH}/connector_types`, { + method: 'GET', + signal, + query: { + feature_id: 'siem', + }, + }); + export interface BulkActionSummary { failed: number; skipped: number; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/translations.ts new file mode 100644 index 0000000000000..96afe91611c69 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/translations.ts @@ -0,0 +1,29 @@ +/* + * 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 CONNECTORS_FETCH_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.actions.connectorsFetchError', + { + defaultMessage: 'Failed to fetch connectors', + } +); + +export const CONNECTOR_TYPES_FETCH_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.actions.connectorTypesFetchError', + { + defaultMessage: 'Failed to fetch connector types', + } +); + +export const ACTIONS_FETCH_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.actions.actionsFetchErrorDescription', + { + defaultMessage: 'Viewing actions is not available', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_connectors.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_connectors.ts new file mode 100644 index 0000000000000..500a346fa1406 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_connectors.ts @@ -0,0 +1,46 @@ +/* + * 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 { useQuery } from '@tanstack/react-query'; +import { BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { fetchConnectors, fetchConnectorTypes } from '../api'; +import * as i18n from './translations'; + +export const useFetchConnectors = () => { + const { addError } = useAppToasts(); + + return useQuery( + ['GET', BASE_ACTION_API_PATH, 'connectors'], + ({ signal }) => fetchConnectors(signal), + { + onError: (error) => { + addError(error, { + title: i18n.CONNECTORS_FETCH_ERROR, + toastMessage: i18n.ACTIONS_FETCH_ERROR_DESCRIPTION, + }); + }, + } + ); +}; + +export const useFetchConnectorTypes = () => { + const { addError } = useAppToasts(); + + return useQuery( + ['GET', BASE_ACTION_API_PATH, 'connector_types', 'siem'], + ({ signal }) => fetchConnectorTypes(signal), + { + onError: (error) => { + addError(error, { + title: i18n.CONNECTOR_TYPES_FETCH_ERROR, + toastMessage: i18n.ACTIONS_FETCH_ERROR_DESCRIPTION, + }); + }, + } + ); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx index cffd025d70ec8..afa4327fe6b27 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock'; @@ -19,6 +20,7 @@ import { import { useRuleForms } from '../../../../detection_engine/rule_creation_ui/pages/form'; import type { FormHook } from '../../../../shared_imports'; import type { ActionsStepRule } from '../../../pages/detection_engine/rules/types'; +import { FrequencyDescription } from './notification_action'; jest.mock('../../../../common/lib/kibana', () => ({ useKibana: jest.fn().mockReturnValue({ @@ -83,3 +85,113 @@ describe('StepRuleActions', () => { expect(wrapper.find('Form[data-test-subj="stepRuleActions"]')).toHaveLength(1); }); }); + +describe('getFrequencyDescription', () => { + it('should return empty string if frequency is not specified', () => { + const { container } = render(, { + wrapper: TestProviders, + }); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should correctly handle "For each alert. Per rule run."', async () => { + const frequency = { notifyWhen: 'onActiveAlert', summary: false, throttle: null } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('For each alert. Per rule run.')).toBeInTheDocument(); + }); + + it('should correctly handle "Summary of alerts. Per rule run."', async () => { + const frequency = { notifyWhen: 'onActiveAlert', summary: true, throttle: null } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Summary of alerts. Per rule run.')).toBeInTheDocument(); + }); + + it('should return empty string if type is "onThrottleInterval" but throttle is not specified', () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: null } as const; + + const { container } = render(, { + wrapper: TestProviders, + }); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should correctly handle "Once a second"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1s' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once a second')).toBeInTheDocument(); + }); + + it('should correctly handle "Once in every # seconds"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '2s' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once in every 2 seconds')).toBeInTheDocument(); + }); + + it('should correctly handle "Once a minute"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1m' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once a minute')).toBeInTheDocument(); + }); + + it('should correctly handle "Once in every # minutes"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '2m' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once in every 2 minutes')).toBeInTheDocument(); + }); + + it('should correctly handle "Once an hour"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1h' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once an hour')).toBeInTheDocument(); + }); + + it('should correctly handle "Once in every # hours"', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '2h' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Once in every 2 hours')).toBeInTheDocument(); + }); + + it('should correctly handle unknown time units', async () => { + const frequency = { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1z' } as const; + + const { findByText } = render(, { + wrapper: TestProviders, + }); + + expect(await findByText('Periodically')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 52056b355c82a..b6acd99c5abb4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -10,7 +10,10 @@ import type { FC } from 'react'; import React, { memo, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import type { + ActionTypeRegistryContract, + ActionVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; import { UseArray } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import type { RuleObjectId } from '../../../../../common/detection_engine/rule_schema'; @@ -18,15 +21,19 @@ import { isQueryRule } from '../../../../../common/detection_engine/utils'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { ResponseActionsForm } from '../../../../detection_engine/rule_response_actions/response_actions_form'; import type { RuleStepProps, ActionsStepRule } from '../../../pages/detection_engine/rules/types'; -import { StepRuleDescription } from '../description_step'; import { Form, UseField } from '../../../../shared_imports'; import type { FormHook } from '../../../../shared_imports'; import { StepContentWrapper } from '../step_content_wrapper'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; -import { getSchema } from './get_schema'; -import * as I18n from './translations'; +import { + useFetchConnectors, + useFetchConnectorTypes, +} from '../../../../detection_engine/rule_management/api/hooks/use_fetch_connectors'; +import * as i18n from './translations'; import { RuleSnoozeSection } from './rule_snooze_section'; +import { NotificationAction } from './notification_action'; +import { ResponseAction } from './response_action'; interface StepRuleActionsProps extends RuleStepProps { ruleId?: RuleObjectId; // Rule SO's id (not ruleId) @@ -118,7 +125,7 @@ const StepRuleActionsComponent: FC = ({ ) : ( <> - {I18n.NO_ACTIONS_READ_PERMISSIONS} + {i18n.NO_ACTIONS_READ_PERMISSIONS} ); }, [ @@ -139,22 +146,79 @@ const StepRuleActionsComponent: FC = ({ ); }; + export const StepRuleActions = memo(StepRuleActionsComponent); const StepRuleActionsReadOnlyComponent: FC = ({ addPadding, - defaultValues: data, + defaultValues: ruleActionsData, }) => { const { - services: { - triggersActionsUi: { actionTypeRegistry }, - }, + services: { triggersActionsUi }, } = useKibana(); - const schema = useMemo(() => getSchema({ actionTypeRegistry }), [actionTypeRegistry]); + + const actionTypeRegistry = triggersActionsUi.actionTypeRegistry as ActionTypeRegistryContract; + + const { data: connectors } = useFetchConnectors(); + const { data: connectorTypes } = useFetchConnectorTypes(); + + const notificationActions = ruleActionsData.actions; + const responseActions = ruleActionsData.responseActions || []; + + const ruleHasActions = notificationActions.length > 0 || responseActions.length > 0; + + if (!ruleHasActions || !connectors || !connectorTypes) { + return null; + } + + const hasBothNotificationAndResponseActions = + notificationActions.length > 0 && responseActions.length > 0; + return ( - + {notificationActions.length > 0 && ( + <> + {i18n.NOTIFICATION_ACTIONS} + + + )} + + {notificationActions.map((action, index) => { + const isLastItem = index === notificationActions.length - 1; + return ( + <> + + {!isLastItem && } + + ); + })} + + {hasBothNotificationAndResponseActions && } + + {responseActions.length > 0 && ( + <> + {i18n.RESPONSE_ACTIONS} + + + )} + + {responseActions.map((action, index) => { + const isLastItem = index === responseActions.length - 1; + return ( + <> + + {!isLastItem && } + + ); + })} ); }; + export const StepRuleActionsReadOnly = memo(StepRuleActionsReadOnlyComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/notification_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/notification_action.tsx new file mode 100644 index 0000000000000..6a02a6da88291 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/notification_action.tsx @@ -0,0 +1,123 @@ +/* + * 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 { EuiToolTip, EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import type { ActionType, AsApiContract } from '@kbn/actions-plugin/common'; +import type { ActionResult } from '@kbn/actions-plugin/server'; +import type { RuleActionFrequency, RuleAction } from '@kbn/alerting-plugin/common'; +import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { getTimeTypeValue } from '../../../../detection_engine/rule_creation_ui/pages/rule_creation/helpers'; +import * as i18n from './translations'; + +const DescriptionLine = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + +); + +export const FrequencyDescription: React.FC<{ frequency?: RuleActionFrequency }> = ({ + frequency, +}) => { + if (!frequency) { + return null; + } + + if (!frequency.summary) { + return {i18n.FOR_EACH_ALERT_PER_RULE_RUN}; + } + + if (frequency.notifyWhen === 'onActiveAlert') { + return {i18n.SUMMARY_OF_ALERTS_PER_RULE_RUN}; + } + + if (!frequency.throttle) { + return null; + } + + const { unit, value } = getTimeTypeValue(frequency.throttle); + + const messagesByUnit: { [unit: string]: JSX.Element } = { + s: ( + + ), + m: ( + + ), + h: ( + + ), + d: ( + + ), + }; + + return {messagesByUnit[unit] || i18n.PERIODICALLY}; +}; + +interface NotificationActionProps { + action: RuleAction; + connectorTypes: ActionType[]; + connectors: Array>; + actionTypeRegistry: ActionTypeRegistryContract; +} + +export function NotificationAction({ + action, + connectorTypes, + connectors, + actionTypeRegistry, +}: NotificationActionProps) { + const connectorType = connectorTypes.find(({ id }) => id === action.actionTypeId); + const connectorTypeName = connectorType?.name ?? ''; + + const connector = connectors.find(({ id }) => id === action.id); + const connectorName = connector?.name ?? ''; + + const iconType = actionTypeRegistry.get(action.actionTypeId)?.iconClass ?? 'apps'; + + return ( + + + + + + + + + {connectorName} + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/response_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/response_action.tsx new file mode 100644 index 0000000000000..6607ce29e9bc9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/response_action.tsx @@ -0,0 +1,34 @@ +/* + * 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 { EuiToolTip, EuiText, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import type { RuleAction } from '@kbn/alerting-plugin/common'; +import { getActionDetails } from '../../../../detection_engine/rule_response_actions/constants'; + +interface ResponseActionProps { + action: Omit; +} + +export function ResponseAction({ action }: ResponseActionProps) { + const { name, logo } = getActionDetails(action.actionTypeId); + + return ( + + + + + + + + + {name} + + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index 968cc27826e79..a1830186837c2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -52,3 +52,37 @@ function RuleSnoozeDescription(): JSX.Element { } export const RULE_SNOOZE_DESCRIPTION = ; + +export const ACTIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.actionsSectionLabel', + { defaultMessage: 'Actions' } +); + +export const NOTIFICATION_ACTIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.notificationActions', + { + defaultMessage: 'Notification actions', + } +); + +export const RESPONSE_ACTIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.responseActions', + { + defaultMessage: 'Response actions', + } +); + +export const FOR_EACH_ALERT_PER_RULE_RUN = i18n.translate( + 'xpack.securitySolution.detectionEngine.actionNotifyWhen.forEachOption', + { defaultMessage: 'For each alert. Per rule run.' } +); + +export const SUMMARY_OF_ALERTS_PER_RULE_RUN = i18n.translate( + 'xpack.securitySolution.detectionEngine.actionNotifyWhen.summaryOption', + { defaultMessage: 'Summary of alerts. Per rule run.' } +); + +export const PERIODICALLY = i18n.translate( + 'xpack.securitySolution.detectionEngine.actionNotifyWhen.periodically', + { defaultMessage: 'Periodically' } +);