Alerting: Update error message for failed FetchError (#106846)

This commit is contained in:
Gilles De Mey
2025-06-18 15:51:25 +02:00
committed by GitHub
parent 24192745b6
commit 13406336ce
8 changed files with 48 additions and 9 deletions
+8 -1
View File
@@ -1,4 +1,4 @@
import { isFetchError } from '@grafana/runtime';
import { FetchError, isFetchError } from '@grafana/runtime';
export function getMessageFromError(err: unknown): string {
if (typeof err === 'string') {
@@ -60,3 +60,10 @@ export function getMessageIdFromError(err: unknown): string | undefined {
return undefined;
}
export function getRequestConfigFromError(err: FetchError): string {
const method = err.config?.method ?? 'GET';
const url = err.config?.url;
return method && url ? `${method} ${url}` : 'request';
}
@@ -284,7 +284,7 @@ describe('NotificationPolicies', () => {
renderNotificationPolicies();
const alert = await screen.findByRole('alert', { name: /error loading alertmanager config/i });
expect(await within(alert).findByText(errMessage)).toBeInTheDocument();
expect(await within(alert).findByText(new RegExp(errMessage))).toBeInTheDocument();
expect(ui.rootRouteContainer.query()).not.toBeInTheDocument();
});
@@ -135,7 +135,7 @@ describe('GroupDetailsPage', () => {
// Assert
expect(await screen.findByText('Error loading the group')).toBeInTheDocument();
expect(await screen.findByText('Failed to fetch rule group')).toBeInTheDocument();
expect(await screen.findByText(/Failed to fetch rule group/)).toBeInTheDocument();
});
it('should render "not found" when group does not exist', async () => {
@@ -70,7 +70,7 @@ describe('pause rule', () => {
await userEvent.click(byRole('button').get());
expect(await byText(/loading/i).find()).toBeInTheDocument();
expect(byText(/success/i).query()).not.toBeInTheDocument();
expect(await byText(/error: oops/i).find()).toBeInTheDocument();
expect(await byText(/error:(.+)oops/i).find()).toBeInTheDocument();
});
});
@@ -77,9 +77,9 @@ export const DataSourceSection = ({
{Boolean(error) && (
<Toggletip
title={t('alerting.rule-list.ds-error.title', 'Cannot load rules for this datasource')}
content={<div>{stringifyErrorLike(error)}</div>}
content={<Text color="error">{stringifyErrorLike(error)}</Text>}
>
<Button variant="destructive" fill="outline" size="sm" icon="exclamation-circle">
<Button variant="destructive" fill="text" size="sm" icon="exclamation-circle">
<Trans i18nKey="alerting.rule-list.error-button">Error</Trans>
</Button>
</Toggletip>
@@ -1,3 +1,4 @@
import { FetchError } from '@grafana/runtime';
import {
createExploreLink,
makeDashboardLink,
@@ -140,7 +141,7 @@ describe('stringifyErrorLike', () => {
it('should stringify Fetch error with message embedded in HTTP response', () => {
const error = { status: 404, data: { message: 'message from the API' } };
expect(stringifyErrorLike(error)).toBe('message from the API');
expect(stringifyErrorLike(error)).toBe('request failed with 404: message from the API');
});
it('should stringify Fetch error with status text as fallback', () => {
@@ -164,7 +165,7 @@ describe('stringifyErrorLike', () => {
reason: 'Conflict',
};
expect(stringifyErrorLike({ status: 409, data: error })).toBe('some message');
expect(stringifyErrorLike({ status: 409, data: error })).toBe('request failed with 409: some message');
});
it('should stringify ApiMachineryError with known code', () => {
@@ -180,4 +181,19 @@ describe('stringifyErrorLike', () => {
expect(stringifyErrorLike({ status: 409, data: error })).toBe(getErrorMessageFromCode(ERROR_NEWER_CONFIGURATION));
});
it('should stringify fetchh error with status code and URL', () => {
const error = {
status: 404,
data: {
message: 'not found',
},
config: {
url: '/my/url',
method: 'POST',
},
} satisfies FetchError;
expect(stringifyErrorLike(error)).toBe('POST /my/url failed with 404: not found');
});
});
@@ -2,9 +2,11 @@ import { sortBy } from 'lodash';
import { Labels, UrlQueryMap } from '@grafana/data';
import { GrafanaEdition } from '@grafana/data/internal';
import { t } from '@grafana/i18n';
import { config, isFetchError } from '@grafana/runtime';
import { DataSourceRef } from '@grafana/schema';
import { contextSrv } from 'app/core/services/context_srv';
import { getMessageFromError, getRequestConfigFromError, getStatusFromError } from 'app/core/utils/errors';
import { escapePathSeparators } from 'app/features/alerting/unified/utils/rule-id';
import {
alertInstanceKey,
@@ -309,9 +311,20 @@ export function stringifyErrorLike(error: unknown): string {
if (error.message) {
return error.message;
}
if ('message' in error.data && typeof error.data.message === 'string') {
return error.data.message;
const status = getStatusFromError(error);
const message = getMessageFromError(error);
const config = getRequestConfigFromError(error);
return t('alerting.errors.failedWith', '{{-config}} failed with {{status}}: {{-message}}', {
config,
status,
message,
});
}
if (error.statusText) {
return error.statusText;
}
+3
View File
@@ -1065,6 +1065,9 @@
"error-summary-button": {
"content-show-all-errors": "Show all errors"
},
"errors": {
"failedWith": "{{-config}} failed with {{status}}: {{-message}}"
},
"evaluate-every-validation-options": {
"message": {
"required": "Required."