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

View File

@ -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();
});

View File

@ -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 () => {

View File

@ -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();
});
});

View File

@ -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>

View File

@ -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');
});
});

View File

@ -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;
}