import React, { useMemo } from 'react'; import { useStyles2 } from '@grafana/ui'; import { AlertLabel } from 'app/features/alerting/unified/components/AlertLabel'; import { getAlertingRule } from 'app/features/alerting/unified/utils/rules'; import { Alert } from 'app/types/unified-alerting'; import { CombinedRuleWithLocation } from '../../../../types/unified-alerting'; import { AlertInstances } from '../AlertInstances'; import { getStyles } from '../UnifiedAlertList'; import { UnifiedAlertListOptions } from '../types'; import { filterAlerts } from '../util'; type Props = { rules: CombinedRuleWithLocation[]; options: UnifiedAlertListOptions; }; type RuleWithAlerts = { rule?: CombinedRuleWithLocation; alerts: Alert[]; }; export const UNGROUPED_KEY = '__ungrouped__'; const GroupedModeView = ({ rules, options }: Props) => { const styles = useStyles2(getStyles); const groupBy = options.groupBy; const groupedRules = useMemo>(() => { const groupedRules = new Map(); const hasInstancesWithMatchingLabels = (rule: CombinedRuleWithLocation) => groupBy ? alertHasEveryLabelForCombinedRules(rule, groupBy) : true; rules.forEach((rule) => { const alertingRule = getAlertingRule(rule); const hasInstancesMatching = hasInstancesWithMatchingLabels(rule); (alertingRule?.alerts ?? []).forEach((alert) => { const mapKey = hasInstancesMatching ? createMapKey(groupBy, alert.labels) : UNGROUPED_KEY; const existingAlerts = groupedRules.get(mapKey)?.alerts ?? []; groupedRules.set(mapKey, { rule, alerts: [...existingAlerts, alert] }); }); }); // move the "UNGROUPED" key to the last item in the Map, items are shown in insertion order const ungrouped = groupedRules.get(UNGROUPED_KEY)?.alerts ?? []; groupedRules.delete(UNGROUPED_KEY); groupedRules.set(UNGROUPED_KEY, { alerts: ungrouped }); // Remove groups having no instances // This is different from filtering Rules without instances that we do in UnifiedAlertList const filteredGroupedRules = Array.from(groupedRules.entries()).reduce( (acc, [groupKey, { rule, alerts: groupAlerts }]) => { const filteredAlerts = filterAlerts(options, groupAlerts); if (filteredAlerts.length > 0) { acc.set(groupKey, { rule, alerts: filteredAlerts }); } return acc; }, new Map() ); return filteredGroupedRules; }, [groupBy, rules, options]); return ( <> {Array.from(groupedRules).map(([key, { rule, alerts }]) => (
  • {key !== UNGROUPED_KEY && parseMapKey(key).map(([key, value]) => )} {key === UNGROUPED_KEY && 'No grouping'}
  • ))} ); }; function createMapKey(groupBy: string[], labels: Record): string { return new URLSearchParams(groupBy.map((key) => [key, labels[key]])).toString(); } function parseMapKey(key: string): Array<[string, string]> { return [...new URLSearchParams(key)]; } function alertHasEveryLabelForCombinedRules(rule: CombinedRuleWithLocation, groupByKeys: string[]) { const alertingRule = getAlertingRule(rule); return groupByKeys.every((key) => { return (alertingRule?.alerts ?? []).some((alert) => alert.labels[key]); }); } export default GroupedModeView;