import { type Expense, EXPENSE_TYPE, type ExpenseType } from "src/modules/timesheets/models/expense.models"; import type { Normalizer } from "src/utils/normalize-object"; export interface ApiErrorPayload { status_code: number; error_code?: string; message?: string; context?: Record; }; export abstract class ApiError extends Error { status_code: number; error_code?: string; context?: Record; constructor(payload: ApiErrorPayload, defaultMessage: string) { super(payload.message || defaultMessage); this.status_code = payload.status_code; this.error_code = payload.error_code ?? "unknown"; this.context = payload.context ?? {'unknown': 'unknown error has occured', }; } }; export class GenericApiError extends ApiError { constructor(payload: ApiErrorPayload) { super(payload, 'Encountered an error processing request'); this.name = 'GenericApiError'; } }; export class ExpensesValidationError extends ApiError { constructor(payload: ApiErrorPayload) { super(payload, 'Invalid expense payload'); this.name = 'ExpensesValidationError'; } }; export class ExpensesApiError extends ApiError { constructor(payload: ApiErrorPayload) { super(payload, 'Request failed'); this.name = 'ExpensesApiError'; } }; export const expense_validation_schema: Normalizer = { id: v => typeof v === 'number' ? v : -1, date: v => typeof v === 'string' ? v.trim() : '1970-01-01', type: v => EXPENSE_TYPE.includes(v as ExpenseType) ? v as ExpenseType : "EXPENSES", amount: v => typeof v === "number" ? v : -1, mileage: v => typeof v === "number" ? v : undefined, comment: v => typeof v === 'string' ? v.trim() : '', supervisor_comment: v => typeof v === 'string' ? v.trim() : '', is_approved: v => !!v, }; export function toExpensesError(err: unknown): ExpensesValidationError | ExpensesApiError { if (err instanceof ExpensesValidationError || err instanceof ExpensesApiError) { return err; } if (typeof err === 'object' && err !== null && 'status_code' in err) { const payload = err as ApiErrorPayload; // Don't know how to differentiate both types of errors, can be updated here if (payload.error_code?.startsWith('API_')) { return new ExpensesApiError(payload); } return new ExpensesValidationError(payload); } // Fallback with ValidationError as default return new ExpensesValidationError({ status_code: 500, message: err instanceof Error ? err.message : 'Unknown error', context: { original: err } }); }