42 lines
2.0 KiB
TypeScript
42 lines
2.0 KiB
TypeScript
|
|
/**
|
||
|
|
* Чтение ошибки, пришедшей от бэкенда.
|
||
|
|
*
|
||
|
|
* До 02.08.2026 экраны читали её так: `(e as any)?.response?.data?.error` — «неизвестный тип»
|
||
|
|
* в четырёх местах подряд. Опечатка в имени поля при этом не ловилась ничем.
|
||
|
|
*
|
||
|
|
* 🪤 Намеренно НЕ через `axios.isAxiosError`: тесты экранов подсовывают ошибку простым объектом
|
||
|
|
* той же формы (`mockRejectedValue({ response: { data: { error: ... } } })`). Проверка на
|
||
|
|
* настоящую ошибку axios отсекла бы их, и экран вёл бы себя в тестах не так, как в бою.
|
||
|
|
* Читаем форму — она одна и та же в обоих случаях.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/** Тело ответа с ошибкой — то, что бэкенд кладёт в `data`. */
|
||
|
|
export interface ApiErrorBody {
|
||
|
|
error?: string;
|
||
|
|
message?: string;
|
||
|
|
errors?: Record<string, string[]>;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Тело ошибки целиком, либо undefined, если пришло что-то другой формы. */
|
||
|
|
export function apiErrorBody(error: unknown): ApiErrorBody | undefined {
|
||
|
|
if (typeof error !== 'object' || error === null) return undefined;
|
||
|
|
|
||
|
|
const response = (error as { response?: unknown }).response;
|
||
|
|
if (typeof response !== 'object' || response === null) return undefined;
|
||
|
|
|
||
|
|
const data = (response as { data?: unknown }).data;
|
||
|
|
if (typeof data !== 'object' || data === null) return undefined;
|
||
|
|
|
||
|
|
return data as ApiErrorBody;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Код ошибки (`balance_insufficient`, `run_in_flight` и т.п.). */
|
||
|
|
export function apiErrorCode(error: unknown): string | undefined {
|
||
|
|
return apiErrorBody(error)?.error;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Первая ошибка по конкретному полю формы. */
|
||
|
|
export function apiFieldError(error: unknown, field: string): string | undefined {
|
||
|
|
return apiErrorBody(error)?.errors?.[field]?.[0];
|
||
|
|
}
|