mirror of
https://github.com/grafana/grafana.git
synced 2025-09-18 04:02:53 +08:00
Alerting: Improve API error payload handling (#109956)
* Handle error.data being set to null * Tidy up code
This commit is contained in:
@ -196,4 +196,71 @@ describe('stringifyErrorLike', () => {
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('POST /my/url failed with 404: not found');
|
||||
});
|
||||
|
||||
it('should prioritize data.message over statusText when both are present', () => {
|
||||
const error = {
|
||||
status: 500,
|
||||
data: { message: 'API error message' },
|
||||
statusText: 'Internal Server Error',
|
||||
config: { url: '/api/test', method: 'GET' },
|
||||
} satisfies FetchError;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('GET /api/test failed with 500: API error message');
|
||||
});
|
||||
|
||||
it('should fall back to statusText when data.message is not available', () => {
|
||||
const error = {
|
||||
status: 404,
|
||||
data: { error: 'some other field' },
|
||||
statusText: 'Not Found',
|
||||
config: { url: '/api/test', method: 'GET' },
|
||||
} satisfies FetchError;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('Not Found');
|
||||
});
|
||||
|
||||
it('should handle null data safely without crashing', () => {
|
||||
const error = {
|
||||
status: 500,
|
||||
data: null,
|
||||
statusText: 'Internal Server Error',
|
||||
config: { url: '/api/test', method: 'POST' },
|
||||
} satisfies FetchError<null>;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('Internal Server Error');
|
||||
});
|
||||
|
||||
it('should handle undefined data safely without crashing', () => {
|
||||
const error = {
|
||||
status: 500,
|
||||
data: undefined,
|
||||
statusText: 'Internal Server Error',
|
||||
config: { url: '/api/test', method: 'PUT' },
|
||||
} satisfies FetchError<undefined>;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('Internal Server Error');
|
||||
});
|
||||
|
||||
it('should handle data without message property safely', () => {
|
||||
const error = {
|
||||
status: 400,
|
||||
data: { error: 'validation failed', code: 400 },
|
||||
statusText: 'Bad Request',
|
||||
config: { url: '/api/validate', method: 'POST' },
|
||||
} satisfies FetchError;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('Bad Request');
|
||||
});
|
||||
|
||||
it('should prioritize error.message over data.message', () => {
|
||||
const error = {
|
||||
status: 403,
|
||||
message: 'Error message property',
|
||||
data: { message: 'Data message property' },
|
||||
statusText: 'Forbidden',
|
||||
config: { url: '/api/test', method: 'POST' },
|
||||
} satisfies FetchError;
|
||||
|
||||
expect(stringifyErrorLike(error)).toBe('Error message property');
|
||||
});
|
||||
});
|
||||
|
@ -288,6 +288,23 @@ export function isErrorLike(error: unknown): error is Error {
|
||||
return Boolean(error && typeof error === 'object' && 'message' in error);
|
||||
}
|
||||
|
||||
// Small composable guards to safely inspect nested shapes without broad assertions
|
||||
function isObject(value: unknown): value is object {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
function hasData(value: unknown): value is { data: unknown } {
|
||||
return isObject(value) && 'data' in value;
|
||||
}
|
||||
|
||||
function hasMessage(value: unknown): value is { message: string } {
|
||||
if (!isObject(value)) {
|
||||
return false;
|
||||
}
|
||||
const desc = Object.getOwnPropertyDescriptor(value, 'message');
|
||||
return typeof desc?.value === 'string';
|
||||
}
|
||||
|
||||
export function getErrorCode(error: unknown): string | undefined {
|
||||
if (isApiMachineryError(error) && error.data.details) {
|
||||
return error.data.details.uid;
|
||||
@ -310,8 +327,7 @@ export function isErrorMatchingCode(error: Error | undefined, code: KnownErrorCo
|
||||
}
|
||||
|
||||
export function stringifyErrorLike(error: unknown): string {
|
||||
const fetchError = isFetchError(error);
|
||||
if (fetchError) {
|
||||
if (isFetchError(error)) {
|
||||
if (isApiMachineryError(error)) {
|
||||
const message = getErrorMessageFromApiMachineryErrorResponse(error);
|
||||
if (message) {
|
||||
@ -323,7 +339,8 @@ export function stringifyErrorLike(error: unknown): string {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
if ('message' in error.data && typeof error.data.message === 'string') {
|
||||
// Runtime check for error.data.message without narrow typing - prioritize over statusText
|
||||
if (hasData(error) && hasMessage(error.data)) {
|
||||
const status = getStatusFromError(error);
|
||||
const message = getMessageFromError(error);
|
||||
|
||||
|
Reference in New Issue
Block a user