-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* alerts in Correlations inital commit Signed-off-by: Riya Saxena <riysaxen@amazon.com> * added experimental banner and correlation Signed-off-by: Riya Saxena <riysaxen@amazon.com> * working partially Signed-off-by: Riya Saxena <riysaxen@amazon.com> * bug fix for Findings API and correlationAlert flyout changes Signed-off-by: Riya Saxena <riysaxen@amazon.com> * fixed acknowledge button Signed-off-by: Riya Saxena <riysaxen@amazon.com> * removed redundant files Signed-off-by: Riya Saxena <riysaxen@amazon.com> * bug fixes Signed-off-by: Riya Saxena <riysaxen@amazon.com> * fixed bugs and removed console.log Signed-off-by: Riya Saxena <riysaxen@amazon.com> * removed Generate Message Button Signed-off-by: Riya Saxena <riysaxen@amazon.com> * addressed the comments by UX Signed-off-by: Riya Saxena <riysaxen@amazon.com> * address the comments Signed-off-by: Riya Saxena <riysaxen@amazon.com> * Alerts in Correlations Experimental Signed-off-by: Riya Saxena <riysaxen@amazon.com> * update snapshot for Alert tests Signed-off-by: Riya Saxena <riysaxen@amazon.com> * fix integ tests Signed-off-by: Riya Saxena <riysaxen@amazon.com> * address the review comments Signed-off-by: Riya Saxena <riysaxen@amazon.com> * address the review comments Signed-off-by: Riya Saxena <riysaxen@amazon.com> * timeout removed to fix integ tests Signed-off-by: Riya Saxena <riysaxen@amazon.com> * fixed alert tests Signed-off-by: Riya Saxena <riysaxen@amazon.com> * removed unnecessary change Signed-off-by: Riya Saxena <riysaxen@amazon.com> --------- Signed-off-by: Riya Saxena <riysaxen@amazon.com> (cherry picked from commit 2ada9e4)
- Loading branch information
1 parent
95a6e9f
commit bc692d1
Showing
16 changed files
with
1,414 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
274 changes: 274 additions & 0 deletions
274
public/pages/Alerts/components/CorrelationAlertFlyout/CorrelationAlertFlyout.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { | ||
EuiBadge, | ||
EuiBasicTable, | ||
EuiBasicTableColumn, | ||
EuiButton, | ||
EuiButtonIcon, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiFlyout, | ||
EuiFlyoutBody, | ||
EuiFlyoutHeader, | ||
EuiLink, | ||
EuiSpacer, | ||
EuiTitle, | ||
} from '@elastic/eui'; | ||
import { RuleSource } from '../../../../../server/models/interfaces'; | ||
import React from 'react'; | ||
import { ContentPanel } from '../../../../components/ContentPanel'; | ||
import { ALERT_STATE, DEFAULT_EMPTY_DATA, ROUTES } from '../../../../utils/constants'; | ||
import { | ||
capitalizeFirstLetter, | ||
createTextDetailsGroup, | ||
errorNotificationToast, | ||
formatRuleType, | ||
renderTime, | ||
} from '../../../../utils/helpers'; | ||
import { parseAlertSeverityToOption } from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers'; | ||
import { NotificationsStart } from 'opensearch-dashboards/public'; | ||
import { DataStore } from '../../../../store/DataStore'; | ||
import { CorrelationAlertTableItem, Finding, Query } from '../../../../../types'; | ||
|
||
export interface CorrelationAlertFlyoutProps { | ||
alertItem: CorrelationAlertTableItem; | ||
notifications: NotificationsStart; | ||
onClose: () => void; | ||
onAcknowledge: (selectedItems: CorrelationAlertTableItem[]) => void; | ||
} | ||
|
||
export interface CorrelationAlertFlyoutState { | ||
acknowledged: boolean; | ||
findingItems: Finding[]; | ||
loading: boolean; | ||
rules: { [key: string]: RuleSource }; | ||
} | ||
|
||
export class CorrelationAlertFlyout extends React.Component<CorrelationAlertFlyoutProps, CorrelationAlertFlyoutState> { | ||
constructor(props: CorrelationAlertFlyoutProps) { | ||
super(props); | ||
|
||
this.state = { | ||
acknowledged: props.alertItem.state === ALERT_STATE.ACKNOWLEDGED, | ||
findingItems: [], | ||
loading: false, | ||
rules: {}, | ||
}; | ||
} | ||
|
||
async componentDidMount() { | ||
this.getFindings(); | ||
} | ||
|
||
getFindings = async () => { | ||
this.setState({ loading: true }); | ||
const { notifications } = this.props; | ||
try { | ||
const findingIds = this.props.alertItem.correlated_finding_ids; | ||
const relatedFindings = await DataStore.findings.getFindingsByIds( | ||
findingIds | ||
); | ||
this.setState({ findingItems: relatedFindings }); | ||
} catch (e: any) { | ||
errorNotificationToast(notifications, 'retrieve', 'findings', e); | ||
} | ||
await this.getRules(); | ||
this.setState({ loading: false }); | ||
}; | ||
|
||
getRules = async () => { | ||
const { notifications } = this.props; | ||
try { | ||
const { findingItems } = this.state; | ||
const ruleIds: string[] = []; | ||
|
||
// Extract ruleIds in order from findingItems | ||
findingItems.forEach((finding) => { | ||
finding.queries.forEach((query) => { | ||
ruleIds.push(query.id); | ||
}); | ||
}); | ||
|
||
if (ruleIds.length > 0) { | ||
// Fetch rules based on ruleIds | ||
const rules = await DataStore.rules.getAllRules({ _id: ruleIds }); | ||
|
||
// Prepare allRules object with rules mapped by _id | ||
const allRules: { [id: string]: RuleSource } = {}; | ||
rules.forEach((hit) => { | ||
allRules[hit._id] = hit._source; | ||
}); | ||
|
||
// Update state with allRules | ||
this.setState({ rules: allRules }); | ||
} | ||
} catch (e: any) { | ||
// Handle errors if any | ||
errorNotificationToast(notifications, 'retrieve', 'rules', e); | ||
} | ||
}; | ||
|
||
createFindingTableColumns(): EuiBasicTableColumn<Finding>[] { | ||
const { rules } = this.state; | ||
|
||
const backButton = ( | ||
<EuiButtonIcon | ||
iconType="arrowLeft" | ||
aria-label="back" | ||
onClick={() => DataStore.findings.closeFlyout()} | ||
display="base" | ||
size="s" | ||
data-test-subj={'finding-details-flyout-back-button'} | ||
/> | ||
); | ||
|
||
return [ | ||
{ | ||
field: 'timestamp', | ||
name: 'Time', | ||
sortable: true, | ||
dataType: 'date', | ||
render: renderTime, | ||
}, | ||
{ | ||
field: 'id', | ||
name: 'Finding ID', | ||
sortable: true, | ||
dataType: 'string', | ||
render: (id: string, finding: any) => ( | ||
<EuiLink | ||
onClick={() => { | ||
const ruleId = finding.queries[0]?.id; // Assuming you retrieve rule ID from finding | ||
const rule: RuleSource | undefined = rules[ruleId]; | ||
|
||
DataStore.findings.openFlyout( | ||
{ | ||
...finding, | ||
detector: { _id: finding.detector_id as string, _index: '' }, | ||
ruleName: rule?.title || '', | ||
ruleSeverity: rule?.level === 'critical' ? rule.level : finding['ruleSeverity'] || rule?.level, | ||
}, | ||
[...this.state.findingItems, finding], | ||
true, | ||
backButton | ||
); | ||
}} | ||
data-test-subj={'finding-details-flyout-button'} | ||
> | ||
{id.length > 7 ? `${id.slice(0, 7)}...` : id} | ||
</EuiLink> | ||
), | ||
}, | ||
{ | ||
field: 'detectionType', | ||
name: 'Detection type', | ||
render: (detectionType: string) => detectionType || DEFAULT_EMPTY_DATA, | ||
}, | ||
{ | ||
field: 'queries', | ||
name: 'Log type', | ||
sortable: true, | ||
dataType: 'string', | ||
render: (queries: Query[], item: any) => { | ||
const key = item.id; | ||
const tag = queries[0]?.tags[1]; | ||
return ( | ||
<EuiBadge key={key}> | ||
{tag ? formatRuleType(tag) : ''} | ||
</EuiBadge> | ||
); | ||
}, | ||
}, | ||
]; | ||
} | ||
|
||
|
||
render() { | ||
const { onClose, alertItem, onAcknowledge } = this.props; | ||
const { trigger_name, state, severity, start_time, end_time } = alertItem; | ||
const { acknowledged, findingItems, loading } = this.state; | ||
|
||
return ( | ||
<EuiFlyout | ||
onClose={onClose} | ||
hideCloseButton | ||
closeButtonProps={{ | ||
size: 'm', | ||
display: 'base', | ||
}} | ||
data-test-subj={'alert-details-flyout'} | ||
> | ||
<EuiFlyoutHeader hasBorder={true}> | ||
<EuiFlexGroup justifyContent="spaceBetween"> | ||
<EuiFlexItem grow={2}> | ||
<EuiTitle size={'m'}> | ||
<h3>Alert details</h3> | ||
</EuiTitle> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={8}> | ||
<EuiFlexGroup justifyContent="flexEnd" alignItems="center"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton | ||
disabled={acknowledged || alertItem.state !== ALERT_STATE.ACTIVE} | ||
onClick={() => { | ||
this.setState({ acknowledged: true }); | ||
onAcknowledge([alertItem]); | ||
}} | ||
data-test-subj={'alert-details-flyout-acknowledge-button'} | ||
> | ||
Acknowledge | ||
</EuiButton> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButtonIcon | ||
aria-label="close" | ||
iconType="cross" | ||
iconSize="m" | ||
display="empty" | ||
onClick={onClose} | ||
data-test-subj={'alert-details-flyout-close-button'} | ||
/> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiFlyoutHeader> | ||
<EuiFlyoutBody> | ||
{createTextDetailsGroup([ | ||
{ label: 'Alert trigger name', content: trigger_name }, | ||
{ label: 'Alert status', content: capitalizeFirstLetter(state) }, | ||
{ | ||
label: 'Alert severity', | ||
content: parseAlertSeverityToOption(severity)?.label || DEFAULT_EMPTY_DATA, | ||
}, | ||
])} | ||
{createTextDetailsGroup([ | ||
{ label: 'Start time', content: renderTime(start_time) }, | ||
{ label: 'Last updated time', content: renderTime(end_time) }, | ||
{ | ||
label: 'Correlation rule', | ||
content: alertItem.correlation_rule_name, | ||
url: `#${ROUTES.CORRELATION_RULE_EDIT}/${alertItem.correlation_rule_id}`, | ||
target: '_blank', | ||
}, | ||
])} | ||
|
||
<EuiSpacer size={'xxl'} /> | ||
|
||
<ContentPanel title={`Findings (${findingItems.length})`}> | ||
<EuiBasicTable<Finding> | ||
columns={this.createFindingTableColumns()} | ||
items={findingItems} | ||
loading={loading} | ||
/> | ||
</ContentPanel> | ||
</EuiFlyoutBody> | ||
</EuiFlyout> | ||
); | ||
} | ||
} | ||
|
Oops, something went wrong.