mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Improve Contact Points error handling (#44888)
* Add 400 and 408 errors handling to display useful error message * Add generic error handling * Improve type guard
This commit is contained in:
parent
92b33d6528
commit
1cf48618de
@ -1,22 +1,22 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { urlUtil } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
AlertmanagerAlert,
|
||||
AlertManagerCortexConfig,
|
||||
AlertmanagerGroup,
|
||||
AlertmanagerStatus,
|
||||
ExternalAlertmanagersResponse,
|
||||
Matcher,
|
||||
Receiver,
|
||||
Silence,
|
||||
SilenceCreatePayload,
|
||||
Matcher,
|
||||
AlertmanagerStatus,
|
||||
Receiver,
|
||||
TestReceiversAlert,
|
||||
TestReceiversPayload,
|
||||
TestReceiversResult,
|
||||
TestReceiversAlert,
|
||||
ExternalAlertmanagersResponse,
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { getDatasourceAPIId, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
import { isFetchError } from '../utils/alertmanager';
|
||||
|
||||
// "grafana" for grafana-managed, otherwise a datasource name
|
||||
export async function fetchAlertManagerConfig(alertManagerSourceName: string): Promise<AlertManagerCortexConfig> {
|
||||
@ -171,28 +171,55 @@ export async function testReceivers(
|
||||
receivers,
|
||||
alert,
|
||||
};
|
||||
const result = await lastValueFrom(
|
||||
getBackendSrv().fetch<TestReceiversResult>({
|
||||
method: 'POST',
|
||||
data,
|
||||
url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/receivers/test`,
|
||||
showErrorAlert: false,
|
||||
showSuccessAlert: false,
|
||||
})
|
||||
);
|
||||
try {
|
||||
const result = await lastValueFrom(
|
||||
getBackendSrv().fetch<TestReceiversResult>({
|
||||
method: 'POST',
|
||||
data,
|
||||
url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/receivers/test`,
|
||||
showErrorAlert: false,
|
||||
showSuccessAlert: false,
|
||||
})
|
||||
);
|
||||
|
||||
// api returns 207 if one or more receivers has failed test. Collect errors in this case
|
||||
if (result.status === 207) {
|
||||
throw new Error(
|
||||
result.data.receivers
|
||||
.flatMap((receiver) =>
|
||||
receiver.grafana_managed_receiver_configs
|
||||
.filter((receiver) => receiver.status === 'failed')
|
||||
.map((receiver) => receiver.error ?? 'Unknown error.')
|
||||
)
|
||||
.join('; ')
|
||||
if (receiversResponseContainsErrors(result.data)) {
|
||||
throw new Error(getReceiverResultError(result.data));
|
||||
}
|
||||
} catch (error) {
|
||||
if (isFetchError(error) && isTestReceiversResult(error.data) && receiversResponseContainsErrors(error.data)) {
|
||||
throw new Error(getReceiverResultError(error.data));
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function receiversResponseContainsErrors(result: TestReceiversResult) {
|
||||
return result.receivers.some((receiver) =>
|
||||
receiver.grafana_managed_receiver_configs.some((config) => config.status === 'failed')
|
||||
);
|
||||
}
|
||||
|
||||
function isTestReceiversResult(data: any): data is TestReceiversResult {
|
||||
const receivers = data?.receivers;
|
||||
|
||||
if (Array.isArray(receivers)) {
|
||||
return receivers.every(
|
||||
(receiver: any) => typeof receiver.name === 'string' && Array.isArray(receiver.grafana_managed_receiver_configs)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getReceiverResultError(receiversResult: TestReceiversResult) {
|
||||
return receiversResult.receivers
|
||||
.flatMap((receiver) =>
|
||||
receiver.grafana_managed_receiver_configs
|
||||
.filter((receiver) => receiver.status === 'failed')
|
||||
.map((receiver) => receiver.error ?? 'Unknown error.')
|
||||
)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
export async function addAlertManagers(alertManagers: string[]): Promise<void> {
|
||||
|
@ -53,7 +53,7 @@ import {
|
||||
isVanillaPrometheusAlertManagerDataSource,
|
||||
} from '../utils/datasource';
|
||||
import { makeAMLink, retryWhile } from '../utils/misc';
|
||||
import { isFetchError, withAppEvents, withSerializedError } from '../utils/redux';
|
||||
import { withAppEvents, withSerializedError } from '../utils/redux';
|
||||
import { formValuesToRulerRuleDTO, formValuesToRulerGrafanaRuleDTO } from '../utils/rule-form';
|
||||
import {
|
||||
isCloudRuleIdentifier,
|
||||
@ -62,7 +62,7 @@ import {
|
||||
isPrometheusRuleIdentifier,
|
||||
isRulerNotSupportedResponse,
|
||||
} from '../utils/rules';
|
||||
import { addDefaultsToAlertmanagerConfig, removeMuteTimingFromRoute } from '../utils/alertmanager';
|
||||
import { addDefaultsToAlertmanagerConfig, removeMuteTimingFromRoute, isFetchError } from '../utils/alertmanager';
|
||||
import * as ruleId from '../utils/rule-id';
|
||||
import { isEmpty } from 'lodash';
|
||||
import messageFromError from 'app/plugins/datasource/grafana-azure-monitor-datasource/utils/messageFromError';
|
||||
|
@ -11,6 +11,7 @@ import { MatcherFieldValue } from '../types/silence-form';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { getAllDataSources } from './config';
|
||||
import { DataSourceType } from './datasource';
|
||||
import { FetchError } from '@grafana/runtime';
|
||||
|
||||
export function addDefaultsToAlertmanagerConfig(config: AlertManagerCortexConfig): AlertManagerCortexConfig {
|
||||
// add default receiver if it does not exist
|
||||
@ -254,3 +255,7 @@ export function getMonthsString(months?: string[]): string {
|
||||
export function getYearsString(years?: string[]): string {
|
||||
return 'Years: ' + (years?.join(', ') ?? 'All');
|
||||
}
|
||||
|
||||
export function isFetchError(e: unknown): e is FetchError {
|
||||
return typeof e === 'object' && e !== null && 'status' in e && 'data' in e;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { FetchError } from '@grafana/runtime';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { isFetchError } from './alertmanager';
|
||||
|
||||
export interface AsyncRequestState<T> {
|
||||
result?: T;
|
||||
@ -138,10 +139,6 @@ export function withAppEvents<T>(
|
||||
});
|
||||
}
|
||||
|
||||
export function isFetchError(e: unknown): e is FetchError {
|
||||
return typeof e === 'object' && e !== null && 'status' in e && 'data' in e;
|
||||
}
|
||||
|
||||
export function messageFromError(e: Error | FetchError | SerializedError): string {
|
||||
if (isFetchError(e)) {
|
||||
if (e.data?.message) {
|
||||
|
@ -247,7 +247,7 @@ interface TestReceiversResultGrafanaReceiverConfig {
|
||||
name: string;
|
||||
uid?: string;
|
||||
error?: string;
|
||||
status: 'failed';
|
||||
status: 'ok' | 'failed';
|
||||
}
|
||||
|
||||
interface TestReceiversResultReceiver {
|
||||
|
Loading…
Reference in New Issue
Block a user