83 lines
2.1 KiB
TypeScript
83 lines
2.1 KiB
TypeScript
import type { AxiosError } from 'axios';
|
|
|
|
import { appConfig } from '../../config/appConfig';
|
|
import type { ApiError } from '../../types/api';
|
|
import { sleep } from '../../utils/async';
|
|
import { isCbsApiError } from './cbsErrors';
|
|
|
|
const isApiError = (value: unknown): value is ApiError =>
|
|
typeof value === 'object' &&
|
|
value !== null &&
|
|
'status' in value &&
|
|
'message' in value;
|
|
|
|
export const toApiError = (error: unknown): ApiError => {
|
|
// A named backend failure. Keep the code and the field map — they are what
|
|
// the screens branch on and paint onto forms.
|
|
if (isCbsApiError(error)) {
|
|
return {
|
|
status: error.status,
|
|
message: error.message,
|
|
code: error.code,
|
|
fieldErrors: error.fieldErrors,
|
|
};
|
|
}
|
|
|
|
if (isApiError(error)) {
|
|
return error;
|
|
}
|
|
|
|
const axiosError = error as AxiosError;
|
|
|
|
return {
|
|
status: Number(axiosError.response?.status ?? 500),
|
|
message: axiosError.message || 'Unexpected service error',
|
|
code: 'UNKNOWN_ERROR',
|
|
data: axiosError.response?.data,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Fixture-backed resolution for the modules that have no backend behind them
|
|
* yet. Endpoints the CBS API actually implements must not go through here —
|
|
* they call `apiClient` directly so a failure surfaces instead of being
|
|
* papered over with demo data.
|
|
*/
|
|
export const resolveWithMock = async <T>({
|
|
mockData,
|
|
request,
|
|
delayMs = 320,
|
|
}: {
|
|
mockData: T | (() => T);
|
|
request: () => Promise<T>;
|
|
delayMs?: number;
|
|
}): Promise<{ data: T } | { error: ApiError }> => {
|
|
if (appConfig.features.useMockServer) {
|
|
await sleep(delayMs);
|
|
return {
|
|
data: typeof mockData === 'function' ? (mockData as () => T)() : mockData,
|
|
};
|
|
}
|
|
|
|
try {
|
|
return {
|
|
data: await request(),
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
error: toApiError(error),
|
|
};
|
|
}
|
|
};
|
|
|
|
/** Same contract as `resolveWithMock`, but always live. */
|
|
export const resolveLive = async <T>(
|
|
request: () => Promise<T>,
|
|
): Promise<{ data: T } | { error: ApiError }> => {
|
|
try {
|
|
return { data: await request() };
|
|
} catch (error) {
|
|
return { error: toApiError(error) };
|
|
}
|
|
};
|