2021-12-13 05:54:36 -05:00
|
|
|
import { Alert, Rule } from 'app/types/unified-alerting';
|
|
|
|
|
import React, { useMemo, useState } from 'react';
|
2021-07-01 12:02:41 +02:00
|
|
|
import { isAlertingRule } from '../../utils/rules';
|
|
|
|
|
import { DetailsField } from '../DetailsField';
|
|
|
|
|
import { AlertInstancesTable } from './AlertInstancesTable';
|
2021-12-13 05:54:36 -05:00
|
|
|
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';
|
2021-07-01 12:02:41 +02:00
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
promRule?: Rule;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function RuleDetailsMatchingInstances(props: Props): JSX.Element | null {
|
|
|
|
|
const { promRule } = props;
|
|
|
|
|
|
2021-12-13 05:54:36 -05:00
|
|
|
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)) {
|
2021-07-01 12:02:41 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<DetailsField label="Matching instances" horizontal={true}>
|
2021-12-13 05:54:36 -05:00
|
|
|
<div className={cx(styles.flexRow, styles.spaceBetween)}>
|
|
|
|
|
<div className={styles.flexRow}>
|
|
|
|
|
<MatcherFilter
|
|
|
|
|
className={styles.rowChild}
|
|
|
|
|
key={queryStringKey}
|
2022-01-28 09:40:05 +01:00
|
|
|
defaultQueryString={queryString}
|
2021-12-13 05:54:36 -05:00
|
|
|
onFilterChange={(value) => setQueryString(value)}
|
|
|
|
|
/>
|
|
|
|
|
<AlertInstanceStateFilter
|
|
|
|
|
className={styles.rowChild}
|
|
|
|
|
stateFilter={alertState}
|
|
|
|
|
onStateFilterChange={setAlertState}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<AlertInstancesTable instances={alerts} />
|
2021-07-01 12:02:41 +02:00
|
|
|
</DetailsField>
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-12-13 05:54:36 -05:00
|
|
|
|
|
|
|
|
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};
|
|
|
|
|
`,
|
|
|
|
|
};
|
|
|
|
|
};
|