Skip to content

Commit

Permalink
Register alerts card with analytics workspace use case (#1064)
Browse files Browse the repository at this point in the history
* added recent alerts card

Signed-off-by: Amardeepsingh Siglani <[email protected]>

* fixed overflow in card

Signed-off-by: Amardeepsingh Siglani <[email protected]>

* updated badge text; fixed integ test

Signed-off-by: Amardeepsingh Siglani <[email protected]>

---------

Signed-off-by: Amardeepsingh Siglani <[email protected]>
  • Loading branch information
amsiglan authored Sep 3, 2024
1 parent db4af39 commit 04119a5
Show file tree
Hide file tree
Showing 18 changed files with 288 additions and 43 deletions.
2 changes: 1 addition & 1 deletion cypress/integration/composite_level_monitor_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('CompositeLevelMonitor', () => {
.type('{backspace}')
.type('Composite trigger');

cy.intercept('api/alerting/workflows?*').as('createMonitorRequest');
cy.intercept('api/alerting/workflows*').as('createMonitorRequest');
cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest');
cy.get('button').contains('Create').click({ force: true });

Expand Down
3 changes: 2 additions & 1 deletion opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"expressions",
"data",
"visAugmenter",
"opensearchDashboardsUtils"
"opensearchDashboardsUtils",
"contentManagement"
],
"server": true,
"ui": true,
Expand Down
144 changes: 144 additions & 0 deletions public/components/DataSourceAlertsCard/DataSourceAlertsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useCallback, useEffect, useState } from "react";
import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiLink, EuiLoadingContent, EuiPanel, EuiSmallButtonEmpty, EuiText, EuiTitle } from "@elastic/eui";
import { DataSourceManagementPluginSetup, DataSourceOption } from "../../../../../src/plugins/data_source_management/public";
import { getApplication, getClient, getNotifications, getSavedObjectsClient } from "../../services";
import { dataSourceFilterFn, getSeverityColor, getSeverityBadgeText, getTruncatedText } from "../../utils/helpers";
import { renderTime } from "../../pages/Dashboard/utils/tableUtils";
import { ALERTS_NAV_ID } from "../../../utils/constants";
import { DEFAULT_EMPTY_DATA } from "../../utils/constants";

export interface DataSourceAlertsCardProps {
getDataSourceMenu: DataSourceManagementPluginSetup['ui']['getDataSourceMenu'];
}

export const DataSourceAlertsCard: React.FC<DataSourceAlertsCardProps> = ({ getDataSourceMenu }) => {
const DataSourceSelector = getDataSourceMenu();
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState<DataSourceOption>({
label: 'Local cluster',
id: '',
});
const [alerts, setAlerts] = useState<any[]>([]);

useEffect(() => {
setLoading(true);
getClient().get('../api/alerting/alerts', {
query: {
size: 25,
sortField: 'start_time',
dataSourceId: dataSource?.id || '',
sortDirection: 'desc'
}
}).then(res => {
if (res.ok) {
setAlerts(res.alerts);
}
setLoading(false);
}).catch(() => {
setLoading(false);
})
}, [dataSource]);

const onDataSourceSelected = useCallback((options: any[]) => {
if (dataSource?.id !== undefined && dataSource?.id !== options[0]?.id) {
setDataSource(options[0]);
}
}, [dataSource]);

const createAlertDetailsHeader = useCallback((alert) => {
const severityColor = getSeverityColor(alert.severity);
const triggerName = alert.trigger_name ?? DEFAULT_EMPTY_DATA;

return (
<EuiFlexGroup gutterSize="s" justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<div>
<EuiBadge color={severityColor?.background} style={{ padding: '1px 4px', color: severityColor?.text }}>{getSeverityBadgeText(alert.severity)}</EuiBadge>
&nbsp;&nbsp;
<span style={{ color: "#006BB4" }} className="eui-textTruncate">{getTruncatedText(triggerName)}</span>
</div>
</EuiFlexItem>
<EuiFlexItem grow={false} >
<EuiText color="subdued" size="s">{renderTime(alert.start_time, { showFromNow: true })}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
)
}, []);

const createAlertDetailsDescription = useCallback((alert) => {
const monitorName = alert.monitor_name ?? DEFAULT_EMPTY_DATA;

return (
<>
<EuiFlexGroup gutterSize="s" justifyContent="flexStart" alignItems="center">
<EuiFlexItem grow={false}>
<EuiText size="s" color="subdued">Monitor:</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false} >
<EuiText size="m">{getTruncatedText(monitorName)}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="xs" />
</>
)
}, []);

const alertsListItems = alerts.map((alert) => {
return {
title: createAlertDetailsHeader(alert),
description: createAlertDetailsDescription(alert)
}
});

return (
<EuiPanel hasBorder={false} hasShadow={false}>
<EuiFlexGroup style={{ height: '100%' }} direction="column" justifyContent="spaceBetween" alignItems="flexStart" gutterSize="xs">
<EuiFlexItem grow={false} style={{ width: '100%', height: '90%' }}>
<EuiFlexGroup direction="column" style={{ height: '100%' }}>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h3>
Recent alerts
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<DataSourceSelector
componentType={"DataSourceSelectable"}
componentConfig={{
savedObjects: getSavedObjectsClient(),
notifications: getNotifications(),
onSelectedDataSources: onDataSourceSelected,
fullWidth: false,
dataSourceFilter: dataSourceFilterFn,
activeOption: dataSource ? [{ id: dataSource.id }] : undefined
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem style={{ overflow: 'scroll' }}>
{loading ? (
<EuiLoadingContent />
) : (
<EuiDescriptionList
listItems={alertsListItems}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink href={getApplication().getUrlForApp(ALERTS_NAV_ID, { path: '#/dasboard' })} target="_blank"><EuiText size="s" className="eui-displayInline">View all</EuiText></EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
)
};
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import { queryColumns } from '../../../../pages/Dashboard/utils/tableUtils';
import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../../../pages/Monitors/containers/Monitors/utils/constants';
import queryString from 'query-string';
import { MAX_ALERT_COUNT } from '../../../../pages/Dashboard/utils/constants';
import { SEVERITY_OPTIONS } from '../../../../pages/CreateTrigger/utils/constants';
import {
getAlertsFindingColumn,
TABLE_TAB_IDS,
Expand All @@ -62,6 +61,7 @@ import {
getDataSourceId,
getIsCommentsEnabled,
} from '../../../../pages/utils/helpers';
import { getSeverityText } from '../../../../utils/helpers';

export const DEFAULT_NUM_FLYOUT_ROWS = 10;

Expand Down Expand Up @@ -172,10 +172,6 @@ export default class AlertsDashboardFlyoutComponent extends Component {
});
};

getSeverityText = (severity) => {
return _.get(_.find(SEVERITY_OPTIONS, { value: severity }), 'text');
};

getBucketLevelGraphFilter = (trigger) => {
const compositeAggFilter = _.get(trigger, 'condition.composite_agg_filter');
if (_.isEmpty(compositeAggFilter)) return DEFAULT_WHERE_EXPRESSION_TEXT;
Expand Down Expand Up @@ -601,7 +597,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
<EuiFlexItem>
<EuiText size="s" data-test-subj={`alertsDashboardFlyout_severity_${trigger_name}`}>
<strong>Severity</strong>
<p>{this.getSeverityText(severity) || severity || DEFAULT_EMPTY_DATA}</p>
<p>{getSeverityText(severity) || severity || DEFAULT_EMPTY_DATA}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EuiAccordion, EuiButton, EuiSpacer, EuiText, EuiTitle } from '@elastic/
import 'brace/mode/plain_text';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid } from '../../../../utils/validate';
import { SEARCH_TYPE } from '../../../../utils/constants';
import { SEARCH_TYPE, SEVERITY_OPTIONS } from '../../../../utils/constants';
import { FORMIK_INITIAL_TRIGGER_CONDITION_VALUES } from '../CreateTrigger/utils/constants';
import AddTriggerConditionButton from '../../components/AddTriggerConditionButton';
import BucketLevelTriggerGraph from '../../components/BucketLevelTriggerGraph';
Expand All @@ -20,7 +20,7 @@ import WhereExpression from '../../../CreateMonitor/components/MonitorExpression
import { FieldArray } from 'formik';
import ConfigureActions from '../ConfigureActions';
import { inputLimitText } from '../../../../utils/helpers';
import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
import { DEFAULT_TRIGGER_NAME } from '../../utils/constants';
import { getTriggerContext } from '../../utils/helper';

const defaultRowProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import PropTypes from 'prop-types';
import _ from 'lodash';
import { EuiSpacer, EuiTitle, EuiAccordion, EuiButton } from '@elastic/eui';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid, required } from '../../../../utils/validate';
import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
import { hasError, isInvalid } from '../../../../utils/validate';
import { DEFAULT_TRIGGER_NAME } from '../../utils/constants';
import CompositeTriggerCondition from '../../components/CompositeTriggerCondition/CompositeTriggerCondition';
import TriggerNotifications from './TriggerNotifications';
import { validateTriggerName } from '../DefineTrigger/utils/validation';
import { FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES } from '../CreateTrigger/utils/constants';
import { titleTemplate } from '../../../../utils/helpers';
import { SEVERITY_OPTIONS } from '../../../../utils/constants';

const defaultRowProps = {
label: titleTemplate('Trigger name'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,26 +241,36 @@ exports[`DefineCompositeLevelTrigger renders 1`] = `
name="triggerDefinitions[undefined].severity"
>
<option
badgetext="Highest"
color="[object Object]"
value="1"
>
1 (Highest)
</option>
<option
badgetext="High"
color="[object Object]"
value="2"
>
2 (High)
</option>
<option
badgetext="Medium"
color="[object Object]"
value="3"
>
3 (Medium)
</option>
<option
badgetext="Low"
color="[object Object]"
value="4"
>
4 (Low)
</option>
<option
badgetext="Lowest"
color="[object Object]"
value="5"
>
5 (Lowest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
} from '@elastic/eui';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid } from '../../../../utils/validate';
import { SEARCH_TYPE } from '../../../../utils/constants';
import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
import { SEARCH_TYPE, SEVERITY_OPTIONS } from '../../../../utils/constants';
import { DEFAULT_TRIGGER_NAME } from '../../utils/constants';
import { validateTriggerName } from '../DefineTrigger/utils/validation';
import ConfigureActions from '../ConfigureActions';
import TriggerQuery from '../../components/TriggerQuery';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { isInvalid, hasError } from '../../../../utils/validate';
import TriggerQuery from '../../components/TriggerQuery';
import TriggerGraph from '../../components/TriggerGraph';
import { validateTriggerName } from './utils/validation';
import { OS_NOTIFICATION_PLUGIN, SEARCH_TYPE } from '../../../../utils/constants';
import { OS_NOTIFICATION_PLUGIN, SEARCH_TYPE, SEVERITY_OPTIONS } from '../../../../utils/constants';
import { AnomalyDetectorTrigger } from './AnomalyDetectorTrigger';
import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
import { FieldArray } from 'formik';
Expand All @@ -34,7 +34,7 @@ import {
buildClusterMetricsRequest,
canExecuteClusterMetricsMonitor,
} from '../../../CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorHelpers';
import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
import { DEFAULT_TRIGGER_NAME } from '../../utils/constants';
import MinimalAccordion from '../../../../components/FeatureAnywhereContextMenu/MinimalAccordion';
import { getTriggerContext } from '../../utils/helper';
import { getDataSourceQueryObj } from '../../../utils/helpers';
Expand Down
8 changes: 0 additions & 8 deletions public/pages/CreateTrigger/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@ export const FORMIK_INITIAL_ACTION_VALUES = {
},
};

export const SEVERITY_OPTIONS = [
{ value: '1', text: '1 (Highest)' },
{ value: '2', text: '2 (High)' },
{ value: '3', text: '3 (Medium)' },
{ value: '4', text: '4 (Low)' },
{ value: '5', text: '5 (Lowest)' },
];

export const THRESHOLD_ENUM_OPTIONS = [
{ value: 'ABOVE', text: 'IS ABOVE' },
{ value: 'BELOW', text: 'IS BELOW' },
Expand Down
Loading

0 comments on commit 04119a5

Please sign in to comment.