Files
grafana/public/app/features/alerting/unified/components/rules/RuleDetailsMatchingInstances.tsx
Konrad Lalik c6e6e92a80 Alerting: Filtering for notification policies (#44363)
* Add filtering by matching label

* Add label and contact based filters to Notification policies

* Improve filters UI, add clear filters option

* Add clearing of filters before switching to adding mode

* Move filtering code to the AmRoutesTable component

* Fix the clearing of silences filter

* Remove key-based input resetting

* Use uniqueId for input key generation

* Add tests for notification policies filtering
2022-01-28 09:40:05 +01:00

104 lines
3.3 KiB
TypeScript

import { Alert, Rule } from 'app/types/unified-alerting';
import React, { useMemo, useState } from 'react';
import { isAlertingRule } from '../../utils/rules';
import { DetailsField } from '../DetailsField';
import { AlertInstancesTable } from './AlertInstancesTable';
import { SortOrder } from 'app/plugins/panel/alertlist/types';
import { GrafanaAlertState } from 'app/types/unified-alerting-dto';
import { GrafanaTheme } from '@grafana/data';
import { useStyles } from '@grafana/ui';
import { css, cx } from '@emotion/css';
import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
import { sortAlerts } from 'app/features/alerting/unified/utils/misc';
import { MatcherFilter } from 'app/features/alerting/unified/components/alert-groups/MatcherFilter';
import { AlertInstanceStateFilter } from 'app/features/alerting/unified/components/rules/AlertInstanceStateFilter';
type Props = {
promRule?: Rule;
};
export function RuleDetailsMatchingInstances(props: Props): JSX.Element | null {
const { promRule } = props;
const [queryString, setQueryString] = useState<string>();
const [alertState, setAlertState] = useState<GrafanaAlertState>();
// This key is used to force a rerender on the inputs when the filters are cleared
const [filterKey] = useState<number>(Math.floor(Math.random() * 100));
const queryStringKey = `queryString-${filterKey}`;
const styles = useStyles(getStyles);
const alerts = useMemo(
(): Alert[] =>
isAlertingRule(promRule) && promRule.alerts?.length
? filterAlerts(queryString, alertState, sortAlerts(SortOrder.Importance, promRule.alerts))
: [],
[promRule, alertState, queryString]
);
if (!isAlertingRule(promRule)) {
return null;
}
return (
<DetailsField label="Matching instances" horizontal={true}>
<div className={cx(styles.flexRow, styles.spaceBetween)}>
<div className={styles.flexRow}>
<MatcherFilter
className={styles.rowChild}
key={queryStringKey}
defaultQueryString={queryString}
onFilterChange={(value) => setQueryString(value)}
/>
<AlertInstanceStateFilter
className={styles.rowChild}
stateFilter={alertState}
onStateFilterChange={setAlertState}
/>
</div>
</div>
<AlertInstancesTable instances={alerts} />
</DetailsField>
);
}
function filterAlerts(
alertInstanceLabel: string | undefined,
alertInstanceState: GrafanaAlertState | undefined,
alerts: Alert[]
): Alert[] {
let filteredAlerts = [...alerts];
if (alertInstanceLabel) {
const matchers = parseMatchers(alertInstanceLabel || '');
filteredAlerts = filteredAlerts.filter(({ labels }) => labelsMatchMatchers(labels, matchers));
}
if (alertInstanceState) {
filteredAlerts = filteredAlerts.filter((alert) => {
return alert.state === alertInstanceState;
});
}
return filteredAlerts;
}
const getStyles = (theme: GrafanaTheme) => {
return {
flexRow: css`
display: flex;
flex-direction: row;
align-items: flex-end;
width: 100%;
flex-wrap: wrap;
margin-bottom: ${theme.spacing.sm};
`,
spaceBetween: css`
justify-content: space-between;
`,
rowChild: css`
margin-right: ${theme.spacing.sm};
`,
};
};