From 4b1f2aa6ef89de404ac0919c29df0abd3cdff2e6 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Tue, 18 Jun 2024 00:12:46 -0700 Subject: [PATCH] removed redundant files Signed-off-by: Riya Saxena --- .../AlertConditionPanel.test.tsx | 16 - .../AlertCondition/AlertConditionPanel.tsx | 648 ------- .../AlertConditionPanel.test.tsx.snap | 1720 ----------------- .../components/AlertCondition/index.ts | 8 - .../containers/ConfigureAlerts.tsx | 258 --- .../components/ConfigureAlerts/index.ts | 8 - .../ConfigureAlerts/models/interfaces.ts | 16 - .../ConfigureAlerts/utils/constants.ts | 47 - .../ConfigureAlerts/utils/helpers.ts | 86 - .../containers/CreateCorrelationRule.tsx | 6 +- 10 files changed, 3 insertions(+), 2810 deletions(-) delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.test.tsx delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/index.ts delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/containers/ConfigureAlerts.tsx delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/index.ts delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/models/interfaces.ts delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/utils/constants.ts delete mode 100644 public/pages/Correlations/components/ConfigureAlerts/utils/helpers.ts diff --git a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.test.tsx b/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.test.tsx deleted file mode 100644 index f7df63b84..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import AlertConditionPanel from './AlertConditionPanel'; -import alertConditionPanelMock from '../../../../../../../test/mocks/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.mock'; - -describe(' spec', () => { - it('renders the component', () => { - const tree = render(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx b/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx deleted file mode 100644 index 22608e617..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { ChangeEvent, Component } from 'react'; -import { RouteComponentProps } from 'react-router-dom'; -import { - EuiAccordion, - EuiButton, - EuiCheckbox, - EuiComboBox, - EuiComboBoxOptionOption, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSpacer, - EuiSwitch, - EuiText, - EuiTextArea, - EuiTitle, -} from '@elastic/eui'; -import { AlertCondition } from '../../../../../../../models/interfaces'; -import { - createSelectedOptions, - getEmptyAlertCondition, - parseAlertSeverityToOption, -} from '../../utils/helpers'; -import { ALERT_SEVERITY_OPTIONS } from '../../utils/constants'; -import { CreateDetectorRulesOptions } from '../../../../../../models/types'; -import { NotificationChannelOption, NotificationChannelTypeOptions } from '../../models/interfaces'; -import { NOTIFICATIONS_HREF } from '../../../../../../utils/constants'; -import { getNameErrorMessage, validateName } from '../../../../../../utils/validation'; -import { NotificationsCallOut } from '../../../../../../components/NotificationsCallOut'; -import { Detector } from '../../../../../../../types'; - -interface AlertConditionPanelProps extends RouteComponentProps { - alertCondition: AlertCondition; - allNotificationChannels: NotificationChannelTypeOptions[]; - rulesOptions: CreateDetectorRulesOptions; - detector: Detector; - indexNum: number; - isEdit: boolean; - hasNotificationPlugin: boolean; - loadingNotifications: boolean; - onAlertTriggerChanged: (newDetector: Detector, emitMetrics?: boolean) => void; - refreshNotificationChannels: () => void; -} - -interface AlertConditionPanelState { - nameFieldTouched: boolean; - nameIsInvalid: boolean; - previewToggle: boolean; - selectedNames: EuiComboBoxOptionOption[]; - showNotificationDetails: boolean; - detectionRulesTriggerEnabled: boolean; - threatIntelTriggerEnabled: boolean; -} - -export default class AlertConditionPanel extends Component< - AlertConditionPanelProps, - AlertConditionPanelState -> { - constructor(props: AlertConditionPanelProps) { - super(props); - this.state = { - nameFieldTouched: props.isEdit, - nameIsInvalid: false, - previewToggle: false, - selectedNames: [], - showNotificationDetails: true, - detectionRulesTriggerEnabled: props.alertCondition.detection_types.includes('rules'), - threatIntelTriggerEnabled: props.alertCondition.detection_types.includes('threat_intel'), - }; - } - - componentDidMount() { - this.prepareMessage(false /* updateMessage */, true /* onMount */); - } - - onDetectionTypeChange(detectionType: 'rules' | 'threat_intel', enabled: boolean) { - const detectionTypes = new Set(this.props.alertCondition.detection_types); - enabled ? detectionTypes.add(detectionType) : detectionTypes.delete(detectionType); - this.updateTrigger({ - detection_types: Array.from(detectionTypes), - }); - } - - // When component mounts, we prepare message but at this point we don't want to emit the - // trigger changed metric since it is not user initiated. So we use the onMount flag to determine that - // and pass it downstream accordingly. - prepareMessage = (updateMessage: boolean = false, onMount: boolean = false) => { - const { alertCondition, detector } = this.props; - const detectorInput = detector.inputs[0].detector_input; - const lineBreak = '\n'; - const lineBreakAndTab = '\n\t'; - - const alertConditionName = `Triggered alert condition: ${alertCondition.name}`; - const alertConditionSeverity = `Severity: ${ - parseAlertSeverityToOption(alertCondition.severity)?.label || alertCondition.severity - }`; - const detectorName = `Threat detector: ${detector.name}`; - const defaultSubject = [alertConditionName, alertConditionSeverity, detectorName].join(' - '); - - if (updateMessage || !alertCondition.actions[0]?.subject_template.source) - this.onMessageSubjectChange(defaultSubject, !onMount); - - if (updateMessage || !alertCondition.actions[0]?.message_template.source) { - const selectedNames = this.setSelectedNames(alertCondition.ids); - const detectorDescription = `Description: ${detectorInput.description}`; - const detectorIndices = `Detector data sources:${lineBreakAndTab}${detectorInput.indices.join( - `,${lineBreakAndTab}` - )}`; - const ruleNames = `Rule Names:${lineBreakAndTab}${selectedNames.join(`,${lineBreakAndTab}`)}`; - const ruleSeverities = `Rule Severities:${lineBreakAndTab}${alertCondition.sev_levels.join( - `,${lineBreakAndTab}` - )}`; - const ruleTags = `Rule Tags:${lineBreakAndTab}${alertCondition.tags.join( - `,${lineBreakAndTab}` - )}`; - - const alertConditionSelections = []; - if (selectedNames.length) { - alertConditionSelections.push(ruleNames); - alertConditionSelections.push(lineBreak); - } - if (alertCondition.sev_levels.length) { - alertConditionSelections.push(ruleSeverities); - alertConditionSelections.push(lineBreak); - } - if (alertCondition.tags.length) { - alertConditionSelections.push(ruleTags); - alertConditionSelections.push(lineBreak); - } - - const alertConditionDetails = [ - alertConditionName, - alertConditionSeverity, - detectorName, - detectorDescription, - detectorIndices, - ]; - let defaultMessageBody = alertConditionDetails.join(lineBreak); - if (alertConditionSelections.length) - defaultMessageBody = - defaultMessageBody + lineBreak + lineBreak + alertConditionSelections.join(lineBreak); - this.onMessageBodyChange(defaultMessageBody, !onMount); - } - }; - - updateTrigger(trigger: Partial, emitMetrics: boolean = true) { - const { - alertCondition, - onAlertTriggerChanged, - detector, - detector: { triggers }, - indexNum, - } = this.props; - trigger.types = [detector.detector_type.toLowerCase()]; - const newTriggers = [...triggers]; - newTriggers.splice(indexNum, 1, { ...alertCondition, ...trigger }); - onAlertTriggerChanged({ ...detector, triggers: newTriggers }, emitMetrics); - } - - onNameBlur = (event: React.ChangeEvent) => { - this.setState({ - nameFieldTouched: true, - nameIsInvalid: !validateName(event.target.value), - }); - }; - - onNameChange = (event: ChangeEvent) => { - this.updateTrigger({ name: event.target.value.trimStart() }); - }; - - onRuleSeverityChange = (selectedOptions: EuiComboBoxOptionOption[]) => { - const severitySelections = selectedOptions.map((option) => option.label); - this.updateTrigger({ sev_levels: severitySelections }); - }; - - onAlertSeverityChange = (selectedOptions: EuiComboBoxOptionOption[]) => { - const severitySelections = selectedOptions.map((option) => option.value); - if (severitySelections.length > 0) { - this.updateTrigger({ severity: severitySelections[0] }); - } - }; - - onCreateTag = (value: string) => { - const { - alertCondition: { tags }, - } = this.props; - const tagOptions = tags.map((tag) => ({ label: tag })); - tagOptions.push({ label: value }); - this.onTagsChange(tagOptions); - }; - - onTagsChange = (selectedOptions: EuiComboBoxOptionOption[]) => { - const tags = selectedOptions.map((tag) => tag.label); - this.updateTrigger({ tags }); - }; - - onNotificationChannelsChange = (selectedOptions: EuiComboBoxOptionOption[]) => { - const { - alertCondition, - onAlertTriggerChanged, - detector, - detector: { triggers }, - indexNum, - } = this.props; - - const actions = alertCondition.actions; - if (selectedOptions.length > 0) { - actions[0].destination_id = selectedOptions[0].value!; - } else { - actions[0].destination_id = ''; - } - - triggers.splice(indexNum, 1, { - ...alertCondition, - actions: actions, - }); - onAlertTriggerChanged({ ...detector, triggers: triggers }); - }; - - onMessageSubjectChange = (subject: string, emitMetrics: boolean = true) => { - const { - alertCondition: { actions }, - } = this.props; - actions[0].name = subject; - actions[0].subject_template.source = subject; - this.updateTrigger({ actions: actions }, emitMetrics); - }; - - onMessageBodyChange = (message: string, emitMetrics: boolean = true) => { - const { - alertCondition: { actions }, - } = this.props; - actions[0].message_template.source = message; - this.updateTrigger({ actions: actions }, emitMetrics); - }; - - onDelete = () => { - const { - onAlertTriggerChanged, - detector, - detector: { triggers }, - indexNum, - } = this.props; - const newTriggers = [...triggers]; - delete newTriggers[indexNum]; - onAlertTriggerChanged({ ...detector, triggers: newTriggers }); - }; - - onRuleNamesChange = (selectedOptions: EuiComboBoxOptionOption[]) => { - const ids = selectedOptions.map((nameOption) => nameOption.value as string); - this.updateTrigger({ ids }); - this.setSelectedNames(ids); - }; - - setSelectedNames = (ids: string[]): string[] => { - const { rulesOptions } = this.props; - const selectedNames: string[] = []; - const selectedNamesOptions: EuiComboBoxOptionOption[] = []; - ids.forEach((ruleId) => { - const option = rulesOptions.find((option) => option.id === ruleId); - if (option) { - selectedNames.push(option.name); - selectedNamesOptions.push({ label: option.name, value: option.id }); - } - }); - this.setState({ selectedNames: selectedNamesOptions }); - return selectedNames; - }; - - render() { - const { - alertCondition = getEmptyAlertCondition(), - allNotificationChannels, - detector: { threat_intel_enabled: threatIntelEnabledInDetector }, - indexNum, - loadingNotifications, - refreshNotificationChannels, - rulesOptions, - hasNotificationPlugin, - } = this.props; - const { - nameFieldTouched, - nameIsInvalid, - selectedNames, - showNotificationDetails, - detectionRulesTriggerEnabled, - threatIntelTriggerEnabled, - } = this.state; - const { name, sev_levels: ruleSeverityLevels, tags, severity } = alertCondition; - const uniqueTagsOptions = new Set( - rulesOptions.map((option) => option.tags).reduce((prev, current) => prev.concat(current), []) - ); - const tagsOptions: { label: string }[] = []; - uniqueTagsOptions.forEach((tag) => { - tagsOptions.push({ - label: tag, - }); - }); - - const uniqueRuleSeverityOptions = new Set(rulesOptions.map((option) => option.severity)); - const ruleSeverityOptions: { label: string }[] = []; - uniqueRuleSeverityOptions.forEach((severity) => { - ruleSeverityOptions.push({ - label: severity, - }); - }); - const uniqueRuleNames = new Set(); - const namesOptions: EuiComboBoxOptionOption[] = []; - rulesOptions.forEach((option) => { - if (!uniqueRuleNames.has(option.name)) { - uniqueRuleNames.add(option.name); - namesOptions.push({ - label: option.name, - value: option.id, - }); - } - }); - - const channelId = alertCondition.actions[0].destination_id; - const selectedNotificationChannelOption: NotificationChannelOption[] = []; - if (channelId) { - allNotificationChannels.forEach((typeOption) => { - const matchingChannel = typeOption.options.find((option) => option.value === channelId); - if (matchingChannel) selectedNotificationChannelOption.push(matchingChannel); - }); - } - - const triggerDetailsSubheading = ` - ${ - selectedNames.length === 1 - ? '1 rule' - : `${!selectedNames.length ? 'All' : selectedNames.length} rules` - }, - ${ - ruleSeverityLevels.length === 1 - ? '1 severity' - : `${!ruleSeverityLevels.length ? 'All' : ruleSeverityLevels.length} severities` - }, - ${tags.length === 1 ? '1 tag' : `${!tags.length ? 'All' : tags.length} tags`} - `; - - return ( -
- -

Trigger name

- - } - isInvalid={nameFieldTouched && nameIsInvalid} - error={getNameErrorMessage(name, nameIsInvalid, nameFieldTouched)} - > - -
- - - -

Detection type

-
- - {threatIntelEnabledInDetector ? ( - { - this.setState({ detectionRulesTriggerEnabled: e.target.checked }); - this.onDetectionTypeChange('rules', e.target.checked); - }} - /> - ) : ( - -

Detection rules

-
- )} - - - - {detectionRulesTriggerEnabled && ( - <> - - Trigger condition - - {triggerDetailsSubheading} - -
- } - > - -

Rule names

- - } - > - -
- - - -

Rule Severities

- - } - > - -
- - - -

Tags

- - } - > - -
- - - - - )} - - {threatIntelEnabledInDetector && ( - <> - { - this.setState({ threatIntelTriggerEnabled: e.target.checked }); - this.onDetectionTypeChange('threat_intel', e.target.checked); - }} - /> - - {threatIntelTriggerEnabled && ( - <> - - -

- An alert will be generated when any match is found by the threat intelligence - feed. -

-
- - - )} - - )} - - - - {!detectionRulesTriggerEnabled && !threatIntelTriggerEnabled && ( - <> - -

Select detection type for the trigger

-
- - - )} - - - - -

Alert severity

- - } - > - -
- - - - this.setState({ showNotificationDetails: e.target.checked })} - /> - - - - {showNotificationDetails && ( - <> - - - -

Notification channel

- - } - > - []} - selectedOptions={ - selectedNotificationChannelOption as EuiComboBoxOptionOption[] - } - onChange={this.onNotificationChannelsChange} - singleSelection={{ asPlainText: true }} - onFocus={refreshNotificationChannels} - isDisabled={!hasNotificationPlugin} - /> -
-
- - - Manage channels - - -
- - {!hasNotificationPlugin && ( - <> - - - - )} - - - - -

Notification message

- - } - paddingSize={'l'} - initialIsOpen={false} - > - - - -

Message subject

- - } - fullWidth={true} - > - this.onMessageSubjectChange(e.target.value)} - required={true} - fullWidth={true} - /> -
-
- - - -

Message body

- - } - fullWidth={true} - > - this.onMessageBodyChange(e.target.value)} - required={true} - fullWidth={true} - /> -
-
- - - - this.prepareMessage(true)}> - Generate message - - - -
-
- - - - )} - - ); - } -} diff --git a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap b/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap deleted file mode 100644 index ba7edfb3a..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap +++ /dev/null @@ -1,1720 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` spec renders the component 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
-
-
-
- -
-
-
-
- -
-
-
-
-
-

- Detection type -

-
-

- Detection rules -

-
-
-
-
- -
-
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
- -
-
- -
-
-
- - - Send notification - -
-
-
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- , - "container":
-
-
-
- -
-
-
-
- -
-
-
-
-
-

- Detection type -

-
-

- Detection rules -

-
-
-
-
- -
-
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
- -
-
- -
-
-
- - - Send notification - -
-
-
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
, - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; diff --git a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/index.ts b/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/index.ts deleted file mode 100644 index 5a0a54223..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/components/AlertCondition/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import AlertConditionPanel from './AlertConditionPanel'; - -export default AlertConditionPanel; diff --git a/public/pages/Correlations/components/ConfigureAlerts/containers/ConfigureAlerts.tsx b/public/pages/Correlations/components/ConfigureAlerts/containers/ConfigureAlerts.tsx deleted file mode 100644 index 5f36a817e..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/containers/ConfigureAlerts.tsx +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { Component } from 'react'; -import { RouteComponentProps } from 'react-router-dom'; -import { - EuiAccordion, - EuiButton, - EuiCallOut, - EuiPanel, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { MAX_ALERT_CONDITIONS } from '../utils/constants'; -import AlertConditionPanel from '../components/AlertCondition'; -import { CreateDetectorRulesOptions } from '../../../../../models/types'; -import { NotificationChannelTypeOptions } from '../models/interfaces'; -import { - getEmptyAlertCondition, - getNotificationChannels, - parseNotificationChannelsToOptions, -} from '../utils/helpers'; -import { NotificationsService } from '../../../../../services'; -import { validateName } from '../../../../../utils/validation'; -import { CoreServicesContext } from '../../../../../components/core_services'; -import { BREADCRUMBS } from '../../../../../utils/constants'; -import { - AlertCondition, - CreateDetectorSteps, - Detector, - DetectorCreationStep, -} from '../../../../../../types'; -import { MetricsContext } from '../../../../../metrics/MetricsContext'; - -interface ConfigureAlertsProps extends RouteComponentProps { - detector: Detector; - isEdit: boolean; - rulesOptions: CreateDetectorRulesOptions; - changeDetector: (detector: Detector) => void; - updateDataValidState: (step: DetectorCreationStep, isValid: boolean) => void; - notificationsService: NotificationsService; - hasNotificationPlugin: boolean; - getTriggerName: () => string; - metricsContext?: MetricsContext; -} - -interface ConfigureAlertsState { - loading: boolean; - notificationChannels: NotificationChannelTypeOptions[]; -} - -const isTriggerValid = (triggers: AlertCondition[], hasNotificationPlugin: boolean) => { - return ( - !triggers.length || - triggers.every((trigger) => { - return ( - !!trigger.name && - validateName(trigger.name) && - trigger.severity && - trigger.detection_types.length - ); - }) - ); -}; - -export default class ConfigureAlerts extends Component { - static contextType = CoreServicesContext; - - constructor(props: ConfigureAlertsProps) { - super(props); - this.state = { - loading: false, - notificationChannels: [], - }; - } - - updateBreadcrumbs = () => { - const { - isEdit, - detector: { id = '', name }, - } = this.props; - - isEdit && - this.context.chrome.setBreadcrumbs([ - BREADCRUMBS.SECURITY_ANALYTICS, - BREADCRUMBS.DETECTORS, - BREADCRUMBS.DETECTORS_DETAILS(name, id), - { - text: 'Edit alert triggers', - }, - ]); - }; - - componentDidMount = async () => { - this.updateBreadcrumbs(); - const { - detector: { triggers }, - } = this.props; - this.getNotificationChannels(); - - if (triggers.length === 0) { - this.addCondition(); - this.props.updateDataValidState(DetectorCreationStep.CONFIGURE_ALERTS, true); - } else { - const isTriggerDataValid = isTriggerValid(triggers, this.props.hasNotificationPlugin); - this.props.updateDataValidState(DetectorCreationStep.CONFIGURE_ALERTS, isTriggerDataValid); - } - }; - - componentDidUpdate( - prevProps: Readonly, - prevState: Readonly, - snapshot?: any - ) { - this.updateBreadcrumbs(); - } - - getNotificationChannels = async () => { - this.setState({ loading: true }); - const channels = await getNotificationChannels(this.props.notificationsService); - const parsedChannels = parseNotificationChannelsToOptions(channels); - this.setState({ notificationChannels: parsedChannels }); - this.setState({ loading: false }); - }; - - addCondition = () => { - const { - changeDetector, - detector, - detector: { triggers }, - getTriggerName, - } = this.props; - const detectionTypes = ['rules']; - if (detector.threat_intel_enabled) { - detectionTypes.push('threat_intel'); - } - const newTriggers = [...triggers]; - newTriggers.push(getEmptyAlertCondition(getTriggerName(), detectionTypes)); - changeDetector({ ...detector, triggers: newTriggers }); - }; - - onAlertTriggerChanged = (newDetector: Detector, emitMetrics: boolean = true): void => { - const isTriggerDataValid = isTriggerValid( - newDetector.triggers, - this.props.hasNotificationPlugin - ); - this.props.changeDetector(newDetector); - this.props.updateDataValidState(DetectorCreationStep.CONFIGURE_ALERTS, isTriggerDataValid); - if (emitMetrics) { - this.props.metricsContext?.detectorMetricsManager.sendMetrics( - CreateDetectorSteps.triggerConfigured - ); - } - }; - - onDelete = (index: number) => { - const { - detector, - detector: { triggers }, - } = this.props; - triggers.splice(index, 1); - this.onAlertTriggerChanged({ ...detector, triggers: triggers }); - }; - - render() { - const { - isEdit, - detector: { triggers }, - } = this.props; - - let getPageTitle = (): string | JSX.Element => { - if (isEdit) { - return <>{`Alert triggers (${triggers.length})`}; - } - - return ( - <> - -

Set up alert triggers

-
- - Get notified when specific rule conditions are found by the detector. - - - ); - }; - - const { loading, notificationChannels } = this.state; - const content = ( - <> - {getPageTitle()} - - - - {triggers.map((alertCondition, index) => ( -
- {index > 0 && } - - -

{alertCondition.name}

- - } - paddingSize={'none'} - initialIsOpen={true} - extraAction={ - this.onDelete(index)}> - Remove - - } - > - - -
-
-
- ))} - {!triggers?.length && ( - -

- We recommend creating alert triggers to get notified when specific conditions are - found by the detector. -

-

You can also configure alert triggers after the detector is created.

- - } - /> - )} - - - - = MAX_ALERT_CONDITIONS} onClick={this.addCondition}> - {triggers.length > 0 ? 'Add another alert trigger' : 'Add alert triggers'} - - - ); - - return isEdit ?
{content}
: {content}; - } -} diff --git a/public/pages/Correlations/components/ConfigureAlerts/index.ts b/public/pages/Correlations/components/ConfigureAlerts/index.ts deleted file mode 100644 index beed8f247..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import ConfigureAlerts from './containers/ConfigureAlerts'; - -export default ConfigureAlerts; diff --git a/public/pages/Correlations/components/ConfigureAlerts/models/interfaces.ts b/public/pages/Correlations/components/ConfigureAlerts/models/interfaces.ts deleted file mode 100644 index fda16e043..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/models/interfaces.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export interface NotificationChannelTypeOptions { - label: string; - options: NotificationChannelOption[]; -} - -export interface NotificationChannelOption { - label: string; - value: string; - type: string; - description: string; -} diff --git a/public/pages/Correlations/components/ConfigureAlerts/utils/constants.ts b/public/pages/Correlations/components/ConfigureAlerts/utils/constants.ts deleted file mode 100644 index 269fc20d8..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/utils/constants.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -export const MAX_ALERT_CONDITIONS = 10; -export const MIN_ALERT_CONDITIONS = 0; - -// SEVERITY_OPTIONS have the id, value, label, and text fields because some EUI components -// (e.g, EuiComboBox) require value/label pairings, while others -// (e.g., EuiCheckboxGroup) require id/text pairings. -export const ALERT_SEVERITY_OPTIONS = { - HIGHEST: { id: '1', value: '1', label: '1 (Highest)', text: '1 (Highest)' }, - HIGH: { id: '2', value: '2', label: '2 (High)', text: '2 (High)' }, - MEDIUM: { id: '3', value: '3', label: '3 (Medium)', text: '3 (Medium)' }, - LOW: { id: '4', value: '4', label: '4 (Low)', text: '4 (Low)' }, - LOWEST: { id: '5', value: '5', label: '5 (Lowest)', text: '5 (Lowest)' }, -}; - -export const RULE_SEVERITY_OPTIONS = { - CRITICAL: { id: '1', value: 'critical', label: 'Critical', text: 'critical' }, - HIGH: { id: '2', value: 'high', label: 'High', text: 'High' }, - MEDIUM: { id: '3', value: 'medium', label: 'Medium', text: 'Medium' }, - LOW: { id: '4', value: 'low', label: 'Low', text: 'Low' }, - INFORMATIONAL: { id: '5', value: 'informational', label: 'Info', text: 'Info' }, -}; - -export const MIN_NUM_NOTIFICATION_CHANNELS = 1; -export const MAX_NUM_NOTIFICATION_CHANNELS = 5; - -export const MIN_NUM_RULES = 1; -export const MAX_NUM_RULES = 5; - -// Only allows letters. No spaces, numbers, or special characters. -export const MIN_NUM_TAGS = 0; -export const MAX_NUM_TAGS = 5; - -export let CHANNEL_TYPES = [ - 'slack', - 'email', - 'email_group', - 'chime', - 'webhook', - 'ses_account', - 'sns', - 'microsoft_teams', - 'smtp_account', -]; diff --git a/public/pages/Correlations/components/ConfigureAlerts/utils/helpers.ts b/public/pages/Correlations/components/ConfigureAlerts/utils/helpers.ts deleted file mode 100644 index c579bab59..000000000 --- a/public/pages/Correlations/components/ConfigureAlerts/utils/helpers.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiComboBoxOptionOption } from '@elastic/eui'; -import { ALERT_SEVERITY_OPTIONS, CHANNEL_TYPES } from './constants'; -import { NotificationChannelTypeOptions } from '../models/interfaces'; -import { NotificationsService } from '../../../../../services'; -import { AlertCondition, TriggerAction } from '../../../../../../models/interfaces'; -import { FeatureChannelList } from '../../../../../../types'; - -export const parseAlertSeverityToOption = (severity: string): EuiComboBoxOptionOption => { - return Object.values(ALERT_SEVERITY_OPTIONS).find( - (option) => option.value === severity - ) as EuiComboBoxOptionOption; -}; - -export function createSelectedOptions(optionNames: string[]): EuiComboBoxOptionOption[] { - return optionNames.map((optionName) => ({ id: optionName, label: optionName })); -} - -export const getNotificationChannels = async (notificationsService: NotificationsService) => { - try { - const response = await notificationsService.getChannels(); - if (response.ok) { - return response.response.channel_list; - } else { - console.error('Failed to retrieve notification channels:', response.error); - } - } catch (e) { - console.error('Failed to retrieve notification channels:', e); - } - return []; -}; - -export function parseNotificationChannelsToOptions( - notificationChannels: FeatureChannelList[], - supportedTypes = CHANNEL_TYPES -): NotificationChannelTypeOptions[] { - const allOptions = notificationChannels.map((channel) => ({ - label: `[Channel] ${channel.name}`, - value: channel.config_id, - type: channel.config_type, - description: channel.description, - })); - return supportedTypes.map((type) => ({ - label: type, - options: allOptions.filter((channel) => channel.type === type), - })); -} - -export function getEmptyAlertCondition( - conditionName: string = '', - detection_types: string[] = [] -): AlertCondition { - const emptyTriggerAction: TriggerAction = { - id: '', - name: '', - destination_id: '', - subject_template: { - source: '', - lang: 'mustache', - }, - message_template: { - source: '', - lang: 'mustache', - }, - throttle_enabled: false, - throttle: { - value: 10, - unit: 'MINUTES', - }, - }; - - return { - name: conditionName, - sev_levels: [], - tags: [], - actions: [emptyTriggerAction], - types: [], - severity: '1', - ids: [], - detection_types, - }; -} diff --git a/public/pages/Correlations/containers/CreateCorrelationRule.tsx b/public/pages/Correlations/containers/CreateCorrelationRule.tsx index b0ed6f6aa..7c93a1d20 100644 --- a/public/pages/Correlations/containers/CreateCorrelationRule.tsx +++ b/public/pages/Correlations/containers/CreateCorrelationRule.tsx @@ -50,12 +50,12 @@ import { FieldMappingService, IndexService } from '../../../services'; import { errorNotificationToast, getDataSources, getLogTypeOptions, getPlugins } from '../../../utils/helpers'; import { severityOptions } from '../../../pages/Alerts/utils/constants'; import _ from 'lodash'; -import { NotificationChannelOption, NotificationChannelTypeOptions } from '../components/ConfigureAlerts/models/interfaces'; -import { getEmptyAlertCondition, getNotificationChannels, parseAlertSeverityToOption, parseNotificationChannelsToOptions } from '../components/ConfigureAlerts/utils/helpers'; +import { NotificationChannelOption, NotificationChannelTypeOptions } from '../../CreateDetector/components/ConfigureAlerts/models/interfaces'; +import { getEmptyAlertCondition, getNotificationChannels, parseAlertSeverityToOption, parseNotificationChannelsToOptions } from '../../CreateDetector/components/ConfigureAlerts/utils/helpers'; import { NotificationsCallOut } from '../../../../public/components/NotificationsCallOut'; import { BrowserServices } from '../../../../public/models/interfaces'; import { ExperimentalBanner } from '../components/ExperimentalBanner'; -import { ALERT_SEVERITY_OPTIONS } from '../components/ConfigureAlerts/utils/constants'; +import { ALERT_SEVERITY_OPTIONS } from'../../CreateDetector/components/ConfigureAlerts/utils/constants'; export interface CreateCorrelationRuleProps extends DataSourceProps { indexService: IndexService;