ErrorParser
import: @azure-net/kit
Основная задача парсера ошибок - привести все ошибки в приложении на @azure-net/kit к единому виду. Также его внутри себя используют createAsyncAction и createAsyncResource, при их создании вы можете кастомизировать парсер и передать свой кастомный парсер в createAsyncAction и createAsyncResource.
Глобально на уровне приложения создается три вида ошибок
- HttpServiceError
- SchemaFail
- Error (базовый js-ный)
Все эти ошибки проходя через парсер приходят к единому виду AppError
typescript
export type AppErrorType = 'http' | 'app' | 'schema';
// http означает что ошибка произошла на уровне запроса, скорее всего в репозитории, так как HttpServiceError рождается там.
// app означает что где то конкретно вы выбросили исключение через throw Error(), хотя и поймать в приложении такое реально, но сложно
// schema означает что ошибка появилась на уровне валидации в схеме, а следовательно валидация просто не пройдена
export interface AppError<T = unknown, CustomErrorField = never> {
type: AppErrorType; // тип ошибки
message: string; // сообщение
fields?: RequestErrors<T>; // поля валидации если ошибка появилась в схеме, или их кастомизировали и добавили вы
status?: number; // статус если это ошибка http, при schema 422, при app статус нет, если вы его не добавили
original?: HttpServiceError<T> | SchemaFail<T> | Error; // Оригинал ошибки
custom?: CustomErrorField; // Кастомное поле, изначально отсутствует, но вы можете добавить туда свои данные.
}Пример создания парсера
typescript
import { createErrorParser, createAsyncHelpers } from '@azure-net/kit';
import { createPresenterFactory } from '@azure-net/kit/edges';
export const ErrorParser = createErrorParser();
// Глобально предполагается что вы встраиваете этот парсер в AsyncActions и ваш базовый presenter.
// После этого все ваши запросы с ошибками будут приведены к единому виду, а также вы сможете единым образом парсить ошибки в презентере
export const AsyncHelpers = createAsyncHelpers({ parseError: ErrorParser });
export const AppPresenter = createPresenterFactory({ ...AsyncHelpers, parseError: ErrorParser });Пример кастомизации парсера
Парсер принимает в себя объект из трех ключей
- parseBaseError
- parseHttpError
- parseSchemaError
Все эти методы обязаны вернуть AppError чтобы парсер с ними работал.
typescript
import { createPresenterFactory } from '@azure-net/kit/edges';
import { EnvironmentUtil } from '@azure-net/kit/tools';
import { notificationManager } from '$widgets';
import { createAsyncHelpers, createErrorParser, type AppError, type SchemaFail, type RequestErrors } from '@azure-net/kit';
import { type HttpServiceError } from '@azure-net/kit/infra';
// parseBaseError - пример кастомизации
export const parseBaseError = <D = never>(error: Error): AppError<never, D> => {
if (EnvironmentUtil.isBrowser) {
const { alert } = notificationManager();
alert('shared.somethingWentWrong');
}
return {
type: 'app',
message: error.message,
original: error
};
};
// parseSchemaError - пример кастомизации, вы вольны изменять ключи или добавлять кастомные
export const parseSchemaError = <SchemaData = unknown, D = never>(error: SchemaFail<SchemaData>): AppError<SchemaData, D> => {
return {
type: 'schema',
message: 'schema validation error',
status: 422,
fields: error.getErrors(),
original: error
};
};
// parseHttpError - пример кастомизации с учетом что с бэка тоже летит валидация, которую мы например захэндлить не можем
// Бэкендер как обычно шлет валидацию массивом и мы единожды настраиваем парсер и больше его ошибки по 100 раз не распечатываем
const parseHttpError = <T = unknown, D = never>(error: HttpServiceError<T>): AppError<T, D> => {
const notFatalErrors = [400, 422, 401];
if (!notFatalErrors.includes(error.status) && EnvironmentUtil.isBrowser) {
const { alert } = notificationManager();
alert('shared.somethingWentWrong');
}
const parseValidationErrors = (): RequestErrors<T> => {
const fields: RequestErrors<T> = {};
const entries: [string, string[]][] = Object.entries((error?.data as { data: Record<keyof T, string[]> })?.data ?? {});
entries.forEach(([key, value]) => {
fields[key as keyof typeof fields] = (Array.isArray(value) ? value[0] : value) as (typeof fields)[keyof typeof fields];
});
return fields;
};
return {
type: 'http',
message: error.message ?? 'unexpected error',
status: error.status ?? 500,
original: error,
fields: notFatalErrors.includes(error.status) ? parseValidationErrors() : undefined
};
};
// Дальше кормим все в createErrorParser, AsyncHelpers и Presenter
export const ErrorHandler = createErrorParser({ parseBaseError, parseHttpError, parseSchemaError });
export const AsyncHelpers = createAsyncHelpers({ parseError: ErrorHandler });
export const AppPresenter = createPresenterFactory({ ...AsyncHelpers, handleError: ErrorHandler });