diff --git a/public/app/features/alerting/unified/components/alert-groups/AlertGroupFilter.tsx b/public/app/features/alerting/unified/components/alert-groups/AlertGroupFilter.tsx index f41b228ffea..c3397893af5 100644 --- a/public/app/features/alerting/unified/components/alert-groups/AlertGroupFilter.tsx +++ b/public/app/features/alerting/unified/components/alert-groups/AlertGroupFilter.tsx @@ -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(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 (
@@ -50,6 +52,13 @@ export const AlertGroupFilter = ({ groups }: Props) => { groupBy={groupBy} onGroupingChange={(keys) => setQueryParams({ groupBy: keys.length ? keys.join(',') : null })} /> + + setQueryParams({ receivers: receivers.length ? receivers.join(',') : null }) + } + /> setQueryParams({ alertState: value ? value : null })} diff --git a/public/app/features/alerting/unified/components/alert-groups/ReceiverFilter.tsx b/public/app/features/alerting/unified/components/alert-groups/ReceiverFilter.tsx new file mode 100644 index 00000000000..42636da1a15 --- /dev/null +++ b/public/app/features/alerting/unified/components/alert-groups/ReceiverFilter.tsx @@ -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>((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 ( +
+ + + 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={} + onChange={(items) => { + onReceiversChange(items.map(({ value }) => value).filter((v): v is string => v !== undefined)); + }} + options={receiverOptions} + width={34} + /> +
+ ); +}; diff --git a/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts b/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts index 3d88eafd006..53f0578d504 100644 --- a/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts +++ b/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts @@ -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]); }; diff --git a/public/app/features/alerting/unified/utils/misc.ts b/public/app/features/alerting/unified/utils/misc.ts index 4501be47b69..e7db8710c32 100644 --- a/public/app/features/alerting/unified/utils/misc.ts +++ b/public/app/features/alerting/unified/utils/misc.ts @@ -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) => { diff --git a/public/app/types/unified-alerting.ts b/public/app/types/unified-alerting.ts index 84516174db5..a2a4b6e06ba 100644 --- a/public/app/types/unified-alerting.ts +++ b/public/app/types/unified-alerting.ts @@ -272,6 +272,7 @@ export interface FilterState { alertState?: string; groupBy?: string[]; ruleType?: string; + receivers?: string[]; } export interface SilenceFilterState { diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 492f772d1f9..37a908c0714 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -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.",