diff --git a/public/app/features/alerting/unified/AlertGroups.test.tsx b/public/app/features/alerting/unified/AlertGroups.test.tsx index 582b4a94a2b..890aa4aef50 100644 --- a/public/app/features/alerting/unified/AlertGroups.test.tsx +++ b/public/app/features/alerting/unified/AlertGroups.test.tsx @@ -147,4 +147,19 @@ describe('AlertGroups', () => { expect(groups[0]).toHaveTextContent('No grouping'); expect(groups[1]).toHaveTextContent('uniqueLabel=true'); }); + + it('should combine multiple ungrouped groups', async () => { + mocks.api.fetchAlertGroups.mockImplementation(() => { + const groups = [ + mockAlertGroup({ labels: {} }), + mockAlertGroup({ labels: {}, alerts: [mockAlertmanagerAlert({ labels: { foo: 'bar' } })] }), + ]; + return Promise.resolve(groups); + }); + renderAmNotifications(); + await waitFor(() => expect(mocks.api.fetchAlertGroups).toHaveBeenCalled()); + const groups = ui.group.getAll(); + + expect(groups).toHaveLength(1); + }); }); diff --git a/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts b/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts index fb5f54ae78a..a47e9685979 100644 --- a/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts +++ b/public/app/features/alerting/unified/hooks/useFilteredAmGroups.ts @@ -10,7 +10,7 @@ export const useFilteredAmGroups = (groups: AlertmanagerGroup[]) => { const matchers = parseMatchers(filters.queryString || ''); return useMemo(() => { - return groups.reduce((filteredGroup, group) => { + return groups.reduce((filteredGroup: AlertmanagerGroup[], group) => { const alerts = group.alerts.filter(({ labels, status }) => { const labelsMatch = labelsMatchMatchers(labels, matchers); const filtersMatch = filters.alertState ? status.state === filters.alertState : true; @@ -25,6 +25,6 @@ export const useFilteredAmGroups = (groups: AlertmanagerGroup[]) => { } } return filteredGroup; - }, [] as AlertmanagerGroup[]); + }, []); }, [groups, filters, matchers]); }; diff --git a/public/app/features/alerting/unified/hooks/useGroupedAlerts.ts b/public/app/features/alerting/unified/hooks/useGroupedAlerts.ts index 278851fbfc5..72932587d2a 100644 --- a/public/app/features/alerting/unified/hooks/useGroupedAlerts.ts +++ b/public/app/features/alerting/unified/hooks/useGroupedAlerts.ts @@ -1,11 +1,30 @@ import { useMemo } from 'react'; import { AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types'; import { Labels } from '@grafana/data'; +import { uniqBy } from 'lodash'; -export const useGroupedAlerts = (groups: AlertmanagerGroup[], groupBy: string[]) => { +export const useGroupedAlerts = (groups: AlertmanagerGroup[], groupBy: string[]): AlertmanagerGroup[] => { return useMemo(() => { if (groupBy.length === 0) { - return groups; + const emptyGroupings = groups.filter((group) => Object.keys(group.labels).length === 0); + if (emptyGroupings.length > 1) { + // Merges multiple ungrouped grouping + return groups.reduce((combinedGroups, group) => { + if (Object.keys(group.labels).length === 0) { + const noGroupingGroup = combinedGroups.find(({ labels }) => Object.keys(labels)); + if (!noGroupingGroup) { + combinedGroups.push({ alerts: group.alerts, labels: {}, receiver: { name: 'NONE' } }); + } else { + noGroupingGroup.alerts = uniqBy([...noGroupingGroup.alerts, ...group.alerts], 'labels'); + } + } else { + combinedGroups.push(group); + } + return combinedGroups; + }, [] as AlertmanagerGroup[]); + } else { + return groups; + } } const alerts = groups.flatMap(({ alerts }) => alerts); return alerts.reduce((groupings, alert) => {