Alerting: Add contact point filter to Active Notifications page (#109775)

This commit is contained in:
Alexander Akhmetov
2025-08-21 12:28:31 +02:00
committed by GitHub
parent 8f1ba9acef
commit 557a40497c
6 changed files with 83 additions and 5 deletions
@@ -12,6 +12,7 @@ import { getFiltersFromUrlParams } from '../../utils/misc';
import { AlertStateFilter } from './AlertStateFilter';
import { GroupBy } from './GroupBy';
import { MatcherFilter } from './MatcherFilter';
import { ReceiverFilter } from './ReceiverFilter';
interface Props {
groups: AlertmanagerGroup[];
@@ -20,7 +21,7 @@ interface Props {
export const AlertGroupFilter = ({ groups }: Props) => {
const [filterKey, setFilterKey] = useState<number>(Math.floor(Math.random() * 100));
const [queryParams, setQueryParams] = useQueryParams();
const { groupBy = [], queryString, alertState } = getFiltersFromUrlParams(queryParams);
const { groupBy = [], queryString, alertState, receivers = [] } = getFiltersFromUrlParams(queryParams);
const matcherFilterKey = `matcher-${filterKey}`;
const styles = useStyles2(getStyles);
@@ -31,11 +32,12 @@ export const AlertGroupFilter = ({ groups }: Props) => {
queryString: null,
alertState: null,
contactPoint: null,
receivers: null,
});
setTimeout(() => setFilterKey(filterKey + 1), 100);
};
const showClearButton = !!(groupBy.length > 0 || queryString || alertState);
const showClearButton = !!(groupBy.length > 0 || queryString || alertState || receivers.length > 0);
return (
<div className={styles.wrapper}>
@@ -50,6 +52,13 @@ export const AlertGroupFilter = ({ groups }: Props) => {
groupBy={groupBy}
onGroupingChange={(keys) => setQueryParams({ groupBy: keys.length ? keys.join(',') : null })}
/>
<ReceiverFilter
groups={groups}
receivers={receivers}
onReceiversChange={(receivers) =>
setQueryParams({ receivers: receivers.length ? receivers.join(',') : null })
}
/>
<AlertStateFilter
stateFilter={alertState as AlertState}
onStateFilterChange={(value) => setQueryParams({ alertState: value ? value : null })}
@@ -0,0 +1,53 @@
import { uniq } from 'lodash';
import { SelectableValue } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { Icon, Label, MultiSelect, Tooltip } from '@grafana/ui';
import { AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types';
const collator = new Intl.Collator('en', { sensitivity: 'accent' });
interface Props {
groups: AlertmanagerGroup[];
receivers: string[];
onReceiversChange: (receivers: string[]) => void;
}
export const ReceiverFilter = ({ groups, receivers, onReceiversChange }: Props) => {
const receiverOptions = uniq(groups.map((group) => group.receiver.name))
.map<SelectableValue<string>>((receiverName) => ({
label: receiverName === 'NONE' ? t('alerting.receiver-filter.no-grouping', 'No grouping') : receiverName,
value: receiverName,
}))
.sort((a, b) => collator.compare(a.label || '', b.label || ''));
return (
<div data-testid={'receiver-filter-container'}>
<Label>
<span>
<Trans i18nKey="alerting.receiver-filter.contact-point">Contact point</Trans>&nbsp;
</span>
<Tooltip
content={
<Trans i18nKey="alerting.receiver-filter.tooltip-contact-point">
Filter notifications by the contact point they are being delivered to.
</Trans>
}
>
<Icon name="info-circle" size="sm" />
</Tooltip>
</Label>
<MultiSelect<string>
aria-label={t('alerting.receiver-filter.aria-label-contact-points', 'Filter by contact points')}
value={receivers}
placeholder={t('alerting.receiver-filter.placeholder-contact-point', 'Filter by contact point')}
prefix={<Icon name="at" />}
onChange={(items) => {
onReceiversChange(items.map(({ value }) => value).filter((v): v is string => v !== undefined));
}}
options={receiverOptions}
width={34}
/>
</div>
);
};
@@ -9,12 +9,19 @@ import { getFiltersFromUrlParams } from '../utils/misc';
export const useFilteredAmGroups = (groups: AlertmanagerGroup[]) => {
const [queryParams] = useQueryParams();
const { queryString, alertState } = getFiltersFromUrlParams(queryParams);
const { queryString, alertState, receivers } = getFiltersFromUrlParams(queryParams);
return useMemo(() => {
const matchers = queryString ? parsePromQLStyleMatcherLooseSafe(queryString) : [];
return groups.reduce((filteredGroup: AlertmanagerGroup[], group) => {
// Filter by receiver if specified
const receiverMatches = receivers && receivers.length > 0 ? receivers.includes(group.receiver.name) : true;
if (!receiverMatches) {
return filteredGroup;
}
const alerts = group.alerts.filter(({ labels, status }) => {
const labelsMatch = labelsMatchMatchers(labels, matchers);
const filtersMatch = alertState ? status.state === alertState : true;
@@ -30,5 +37,5 @@ export const useFilteredAmGroups = (groups: AlertmanagerGroup[]) => {
}
return filteredGroup;
}, []);
}, [queryString, groups, alertState]);
}, [queryString, groups, alertState, receivers]);
};
@@ -134,7 +134,8 @@ export const getFiltersFromUrlParams = (queryParams: UrlQueryMap): FilterState =
const dataSource = queryParams.dataSource === undefined ? undefined : String(queryParams.dataSource);
const ruleType = queryParams.ruleType === undefined ? undefined : String(queryParams.ruleType);
const groupBy = queryParams.groupBy === undefined ? undefined : String(queryParams.groupBy).split(',');
return { queryString, alertState, dataSource, groupBy, ruleType };
const receivers = queryParams.receivers === undefined ? undefined : String(queryParams.receivers).split(',');
return { queryString, alertState, dataSource, groupBy, ruleType, receivers };
};
export const getNotificationPoliciesFilters = (searchParams: URLSearchParams) => {
+1
View File
@@ -272,6 +272,7 @@ export interface FilterState {
alertState?: string;
groupBy?: string[];
ruleType?: string;
receivers?: string[];
}
export interface SilenceFilterState {
+7
View File
@@ -2221,6 +2221,13 @@
"preview": "Preview",
"previewCondition": "Preview alert rule condition"
},
"receiver-filter": {
"aria-label-contact-points": "Filter by contact points",
"contact-point": "Contact point",
"no-grouping": "No grouping",
"placeholder-contact-point": "Filter by contact point",
"tooltip-contact-point": "Filter notifications by the contact point they are being delivered to."
},
"receiver-form": {
"add-contact-point-integration": "Add contact point integration",
"body-attention": "Because there is no default policy configured yet, this contact point will automatically be set as default.",