-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Amardeepsingh Siglani <[email protected]> Signed-off-by: Amardeepsingh Siglani <[email protected]> (cherry picked from commit c2858ff)
- Loading branch information
1 parent
06bd81f
commit d7b2d04
Showing
14 changed files
with
1,772 additions
and
127 deletions.
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
public/pages/Detectors/components/AlertTriggerView/AlertTriggerView.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,111 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { | ||
EuiAccordion, | ||
EuiHorizontalRule, | ||
EuiLink, | ||
EuiResizableContainer, | ||
EuiResizablePanel, | ||
EuiSpacer, | ||
EuiText, | ||
EuiTitle, | ||
} from '@elastic/eui'; | ||
import { AlertCondition, Detector } from '../../../../../models/interfaces'; | ||
import React, { useEffect } from 'react'; | ||
import { createTextDetailsGroup } from '../../../../utils/helpers'; | ||
import { parseAlertSeverityToOption } from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers'; | ||
import { DEFAULT_EMPTY_DATA, getNotificationDetailsHref } from '../../../../utils/constants'; | ||
import { FeatureChannelList, RuleInfo } from '../../../../../server/models/interfaces'; | ||
|
||
export interface AlertTriggerViewProps { | ||
alertTrigger: AlertCondition; | ||
orderPosition: number; | ||
detector: Detector; | ||
notificationChannels: FeatureChannelList[]; | ||
rules: { [key: string]: RuleInfo }; | ||
} | ||
|
||
export const AlertTriggerView: React.FC<AlertTriggerViewProps> = ({ | ||
alertTrigger, | ||
orderPosition, | ||
detector, | ||
notificationChannels, | ||
rules, | ||
}) => { | ||
const { name, sev_levels, types, tags, ids, severity, actions } = alertTrigger; | ||
const alertSeverity = parseAlertSeverityToOption(severity)?.label || DEFAULT_EMPTY_DATA; | ||
const action = actions[0]; | ||
const notificationChannelId = detector.triggers[orderPosition].actions[0].destination_id; | ||
const notificationChannel = notificationChannels.find( | ||
(channel) => channel.config_id === notificationChannelId | ||
); | ||
const conditionRuleNames = ids.map((ruleId) => rules[ruleId]?._source.title); | ||
return ( | ||
<div> | ||
{orderPosition > 0 && <EuiHorizontalRule />} | ||
<EuiSpacer size={'l'} /> | ||
<EuiAccordion | ||
id={`alert-trigger-${orderPosition}`} | ||
paddingSize={'m'} | ||
initialIsOpen={false} | ||
buttonContent={ | ||
<EuiText size="m"> | ||
<p>{`Alert on ${name}`}</p> | ||
</EuiText> | ||
} | ||
> | ||
<EuiSpacer size="m" /> | ||
{createTextDetailsGroup([{ label: 'Trigger name', content: `${name}` }])} | ||
<EuiSpacer size="xl" /> | ||
|
||
<EuiTitle size="s"> | ||
<h5>If any detection rule matches</h5> | ||
</EuiTitle> | ||
<EuiSpacer size={'m'} /> | ||
{createTextDetailsGroup( | ||
[ | ||
{ label: 'Log type', content: `${types[0]}` || DEFAULT_EMPTY_DATA }, | ||
{ label: 'Rule names', content: conditionRuleNames.join('\n') || DEFAULT_EMPTY_DATA }, | ||
], | ||
3 | ||
)} | ||
{createTextDetailsGroup( | ||
[ | ||
{ label: 'Rule severities', content: sev_levels.join('\n') || DEFAULT_EMPTY_DATA }, | ||
{ label: 'Tags', content: tags.join('\n') || DEFAULT_EMPTY_DATA }, | ||
], | ||
3 | ||
)} | ||
<EuiSpacer size="xl" /> | ||
|
||
<EuiTitle size="s"> | ||
<h5>Alert and notify</h5> | ||
</EuiTitle> | ||
<EuiSpacer size="m" /> | ||
{createTextDetailsGroup([ | ||
{ | ||
label: 'Trigger alerts with severity', | ||
content: `${alertSeverity}` || DEFAULT_EMPTY_DATA, | ||
}, | ||
])} | ||
<EuiSpacer size="l" /> | ||
{createTextDetailsGroup([ | ||
{ | ||
label: 'Notify channel', | ||
content: notificationChannel ? ( | ||
<EuiLink href={getNotificationDetailsHref(action.destination_id)} target={'_blank'}> | ||
{notificationChannel.name} | ||
</EuiLink> | ||
) : ( | ||
<EuiText>{DEFAULT_EMPTY_DATA}</EuiText> | ||
), | ||
}, | ||
])} | ||
</EuiAccordion> | ||
<EuiSpacer size={'m'} /> | ||
</div> | ||
); | ||
}; |
63 changes: 63 additions & 0 deletions
63
public/pages/Detectors/components/DetectorBasicDetailsView/DetectorBasicDetailsView.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,63 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { EuiButton, EuiSpacer } from '@elastic/eui'; | ||
import React from 'react'; | ||
import { ContentPanel } from '../../../../components/ContentPanel'; | ||
import { createTextDetailsGroup, parseSchedule } from '../../../../utils/helpers'; | ||
import moment from 'moment'; | ||
import { Detector } from '../../../../../models/interfaces'; | ||
import { DEFAULT_EMPTY_DATA } from '../../../../utils/constants'; | ||
|
||
export interface DetectorBasicDetailsViewProps { | ||
detector: Detector; | ||
rulesCanFold?: boolean; | ||
enabled_time?: number; | ||
last_update_time?: number; | ||
onEditClicked: () => void; | ||
} | ||
|
||
export const DetectorBasicDetailsView: React.FC<DetectorBasicDetailsViewProps> = ({ | ||
detector, | ||
enabled_time, | ||
last_update_time, | ||
rulesCanFold, | ||
children, | ||
onEditClicked, | ||
}) => { | ||
const { name, detector_type, inputs, schedule } = detector; | ||
const detectorSchedule = parseSchedule(schedule); | ||
const createdAt = enabled_time ? moment(enabled_time).format('YYYY-MM-DDTHH:mm') : undefined; | ||
const lastUpdated = last_update_time | ||
? moment(last_update_time).format('YYYY-MM-DDTHH:mm') | ||
: undefined; | ||
|
||
return ( | ||
<ContentPanel | ||
title={'Detector details'} | ||
actions={[<EuiButton onClick={onEditClicked}>Edit</EuiButton>]} | ||
> | ||
<EuiSpacer size={'l'} /> | ||
{createTextDetailsGroup( | ||
[ | ||
{ label: 'Detector name', content: name }, | ||
{ label: 'Log type', content: detector_type.toLowerCase() }, | ||
{ label: 'Data source', content: inputs[0].detector_input.indices[0] }, | ||
], | ||
4 | ||
)} | ||
{createTextDetailsGroup( | ||
[ | ||
{ label: 'Description', content: inputs[0].detector_input.description }, | ||
{ label: 'Detector schedule', content: detectorSchedule }, | ||
{ label: 'Created at', content: createdAt || DEFAULT_EMPTY_DATA }, | ||
{ label: 'Last updated time', content: lastUpdated || DEFAULT_EMPTY_DATA }, | ||
], | ||
4 | ||
)} | ||
{rulesCanFold ? children : null} | ||
</ContentPanel> | ||
); | ||
}; |
155 changes: 155 additions & 0 deletions
155
public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.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,155 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { ContentPanel } from '../../../../components/ContentPanel'; | ||
import React, { useContext, useEffect, useState } from 'react'; | ||
import { EuiAccordion, EuiButton, EuiInMemoryTable, EuiSpacer, EuiText } from '@elastic/eui'; | ||
import { | ||
RuleItem, | ||
RuleItemInfo, | ||
} from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; | ||
import { getRulesColumns } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/utils/constants'; | ||
import { ServicesContext } from '../../../../services'; | ||
import { ruleItemInfosToItems } from '../../../../utils/helpers'; | ||
import { Detector } from '../../../../../models/interfaces'; | ||
import { RuleInfo } from '../../../../../server/models/interfaces/Rules'; | ||
|
||
export interface DetectorRulesViewProps { | ||
detector: Detector; | ||
rulesCanFold?: boolean; | ||
onEditClicked: (enabledRules: RuleItem[], allRuleItems: RuleItem[]) => void; | ||
} | ||
|
||
export const DetectorRulesView: React.FC<DetectorRulesViewProps> = (props) => { | ||
const totalSelected = props.detector.inputs.reduce((sum, inputObj) => { | ||
return ( | ||
sum + | ||
inputObj.detector_input.custom_rules.length + | ||
inputObj.detector_input.pre_packaged_rules.length | ||
); | ||
}, 0); | ||
|
||
const [enabledRuleItems, setEnabledRuleItems] = useState<RuleItem[]>([]); | ||
const [allRuleItems, setAllRuleItems] = useState<RuleItem[]>([]); | ||
const actions = [ | ||
<EuiButton onClick={() => props.onEditClicked(enabledRuleItems, allRuleItems)}>Edit</EuiButton>, | ||
]; | ||
const services = useContext(ServicesContext); | ||
|
||
useEffect(() => { | ||
const getRules = async (prePackaged: boolean): Promise<RuleInfo[]> => { | ||
const getRulesRes = await services?.ruleService.getRules(prePackaged, { | ||
from: 0, | ||
size: 5000, | ||
query: { | ||
nested: { | ||
path: 'rule', | ||
query: { | ||
bool: { | ||
must: [ | ||
{ match: { 'rule.category': `${props.detector.detector_type.toLowerCase()}` } }, | ||
], | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
if (getRulesRes?.ok) { | ||
return getRulesRes.response.hits.hits; | ||
} | ||
|
||
return []; | ||
}; | ||
|
||
const translateToRuleItems = ( | ||
prePackagedRules: RuleInfo[], | ||
customRules: RuleInfo[], | ||
isEnabled: (rule: RuleInfo) => boolean | ||
) => { | ||
let ruleItemInfos: RuleItemInfo[] = prePackagedRules.map((rule) => ({ | ||
...rule, | ||
enabled: isEnabled(rule), | ||
prePackaged: true, | ||
})); | ||
|
||
ruleItemInfos = ruleItemInfos.concat( | ||
customRules.map((rule) => ({ | ||
...rule, | ||
enabled: isEnabled(rule), | ||
prePackaged: false, | ||
})) | ||
); | ||
|
||
return ruleItemInfosToItems(props.detector.detector_type, ruleItemInfos); | ||
}; | ||
|
||
const updateRulesState = async () => { | ||
const enabledPrePackagedRuleIds = new Set( | ||
props.detector.inputs[0].detector_input.pre_packaged_rules.map((ruleInfo) => ruleInfo.id) | ||
); | ||
const enabledCustomRuleIds = new Set( | ||
props.detector.inputs[0].detector_input.custom_rules.map((ruleInfo) => ruleInfo.id) | ||
); | ||
|
||
const prePackagedRules = await getRules(true); | ||
const customRules = await getRules(false); | ||
|
||
const enabledPrePackagedRules = prePackagedRules.filter((hit: RuleInfo) => { | ||
return enabledPrePackagedRuleIds.has(hit._id); | ||
}); | ||
|
||
const enabledCustomRules = customRules.filter((hit: RuleInfo) => { | ||
return enabledCustomRuleIds.has(hit._id); | ||
}); | ||
|
||
const enabledRuleItems = translateToRuleItems( | ||
enabledPrePackagedRules, | ||
enabledCustomRules, | ||
() => true | ||
); | ||
const allRuleItems = translateToRuleItems( | ||
prePackagedRules, | ||
customRules, | ||
(ruleInfo) => | ||
enabledPrePackagedRuleIds.has(ruleInfo._id) || enabledCustomRuleIds.has(ruleInfo._id) | ||
); | ||
setEnabledRuleItems(enabledRuleItems); | ||
setAllRuleItems(allRuleItems); | ||
}; | ||
|
||
updateRulesState().catch((error) => { | ||
// TODO: Show error toast | ||
}); | ||
}, [services, props.detector]); | ||
|
||
const rules = ( | ||
<EuiInMemoryTable | ||
columns={getRulesColumns(false)} | ||
items={enabledRuleItems} | ||
itemId={(item: RuleItem) => `${item.name}`} | ||
pagination | ||
/> | ||
); | ||
|
||
return props.rulesCanFold ? ( | ||
<EuiAccordion | ||
id={props.detector.name} | ||
title="View detection rules" | ||
buttonContent={ | ||
<EuiText size="m"> | ||
<p>View detection rules</p> | ||
</EuiText> | ||
} | ||
> | ||
<EuiSpacer size="l" /> | ||
{rules} | ||
</EuiAccordion> | ||
) : ( | ||
<ContentPanel title={`Detection rules (${totalSelected})`} actions={actions}> | ||
{rules} | ||
</ContentPanel> | ||
); | ||
}; |
Oops, something went wrong.