-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alerts in correlations #1048
Merged
riysaxen-amzn
merged 20 commits into
opensearch-project:main
from
riysaxen-amzn:alertsInCorrelations
Jun 26, 2024
Merged
Alerts in correlations #1048
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
b2c4c98
alerts in Correlations inital commit
riysaxen-amzn 2fb6b6c
added experimental banner and correlation
riysaxen-amzn 15f0aa6
working partially
riysaxen-amzn 07a8559
bug fix for Findings API and correlationAlert flyout changes
riysaxen-amzn 0ce266b
fixed acknowledge button
riysaxen-amzn 4b1f2aa
removed redundant files
riysaxen-amzn ac4b304
bug fixes
riysaxen-amzn e8a72d7
fixed bugs and removed console.log
riysaxen-amzn aaf938b
removed Generate Message Button
riysaxen-amzn 557a403
addressed the comments by UX
riysaxen-amzn 78dae49
Merge branch 'main' into alertsInCorrelations
riysaxen-amzn fa9c6ef
address the comments
riysaxen-amzn fae0531
Alerts in Correlations Experimental
riysaxen-amzn 5110f10
update snapshot for Alert tests
riysaxen-amzn 501bfaa
fix integ tests
riysaxen-amzn 7bd5683
address the review comments
riysaxen-amzn 37a5e5f
address the review comments
riysaxen-amzn b72da46
timeout removed to fix integ tests
riysaxen-amzn e4e5d9c
fixed alert tests
riysaxen-amzn 16e16f5
removed unnecessary change
riysaxen-amzn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dedupe these utility methods between the regular alert flyout and this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will raise a seperate PR to refactor this.