The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/alerting/unified/components/contact-points/utils.ts

117 lines
4.3 KiB

import { countBy, split, trim } from 'lodash';
import { ReactNode } from 'react';
import {
AlertManagerCortexConfig,
GrafanaManagedContactPoint,
GrafanaManagedReceiverConfig,
Route,
} from 'app/plugins/datasource/alertmanager/types';
import { NotifierStatus, ReceiversStateDTO } from 'app/types';
import { computeInheritedTree } from '../../utils/notification-policies';
import { extractReceivers } from '../../utils/receivers';
import { RECEIVER_STATUS_KEY } from './useContactPoints';
export function isProvisioned(contactPoint: GrafanaManagedContactPoint) {
// for some reason the provenance is on the receiver and not the entire contact point
const provenance = contactPoint.grafana_managed_receiver_configs?.find((receiver) => receiver.provenance)?.provenance;
return Boolean(provenance);
}
// TODO we should really add some type information to these receiver settings...
export function getReceiverDescription(receiver: GrafanaManagedReceiverConfig): ReactNode | undefined {
switch (receiver.type) {
case 'email': {
const hasEmailAddresses = 'addresses' in receiver.settings; // when dealing with alertmanager email_configs we don't normalize the settings
return hasEmailAddresses ? summarizeEmailAddresses(receiver.settings['addresses']) : undefined;
}
case 'slack': {
const channelName = receiver.settings['recipient'];
return channelName ? `#${channelName}` : undefined;
}
case 'kafka': {
const topicName = receiver.settings['kafkaTopic'];
return topicName;
}
case 'webhook': {
const url = receiver.settings['url'];
return url;
}
default:
return undefined;
}
}
// input: foo+1@bar.com, foo+2@bar.com, foo+3@bar.com, foo+4@bar.com
// output: foo+1@bar.com, foo+2@bar.com, +2 more
function summarizeEmailAddresses(addresses: string): string {
const MAX_ADDRESSES_SHOWN = 3;
const SUPPORTED_SEPARATORS = /,|;|\n+/g;
const emails = addresses.trim().split(SUPPORTED_SEPARATORS).map(trim);
const notShown = emails.length - MAX_ADDRESSES_SHOWN;
const truncatedAddresses = split(addresses, SUPPORTED_SEPARATORS, MAX_ADDRESSES_SHOWN);
if (notShown > 0) {
truncatedAddresses.push(`+${notShown} more`);
}
return truncatedAddresses.join(', ');
}
// Grafana Managed contact points have receivers with additional diagnostics
export interface ReceiverConfigWithStatus extends GrafanaManagedReceiverConfig {
// we're using a symbol here so we'll never have a conflict on keys for a receiver
// we also specify that the diagnostics might be "undefined" for vanilla Alertmanager
[RECEIVER_STATUS_KEY]?: NotifierStatus | undefined;
}
export interface ContactPointWithStatus extends GrafanaManagedContactPoint {
numberOfPolicies: number;
grafana_managed_receiver_configs: ReceiverConfigWithStatus[];
}
/**
* This function adds the status information for each of the integrations (contact point types) in a contact point
* 1. we iterate over all contact points
* 2. for each contact point we "enhance" it with the status or "undefined" for vanilla Alertmanager
*/
export function enhanceContactPointsWithStatus(
result: AlertManagerCortexConfig,
status: ReceiversStateDTO[] = []
): ContactPointWithStatus[] {
const contactPoints = result.alertmanager_config.receivers ?? [];
// compute the entire inherited tree before finding what notification policies are using a particular contact point
const fullyInheritedTree = computeInheritedTree(result?.alertmanager_config?.route ?? {});
const usedContactPoints = getUsedContactPoints(fullyInheritedTree);
const usedContactPointsByName = countBy(usedContactPoints);
return contactPoints.map((contactPoint) => {
const receivers = extractReceivers(contactPoint);
const statusForReceiver = status.find((status) => status.name === contactPoint.name);
return {
...contactPoint,
numberOfPolicies: usedContactPointsByName[contactPoint.name] ?? 0,
grafana_managed_receiver_configs: receivers.map((receiver, index) => ({
...receiver,
[RECEIVER_STATUS_KEY]: statusForReceiver?.integrations[index],
})),
};
});
}
export function getUsedContactPoints(route: Route): string[] {
const childrenContactPoints = route.routes?.flatMap((route) => getUsedContactPoints(route)) ?? [];
if (route.receiver) {
return [route.receiver, ...childrenContactPoints];
}
return childrenContactPoints;
}