From 1723f16b9c4b008e9e44578c2587c7a1bd6560b4 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 25 Feb 2023 19:43:45 -0300 Subject: [PATCH] some fixes afte testing demobank with ms --- packages/demobank-ui/src/hooks/circuit.ts | 15 +- .../demobank-ui/src/pages/AccountPage.tsx | 4 +- packages/demobank-ui/src/pages/AdminPage.tsx | 45 ++- .../demobank-ui/src/pages/BusinessAccount.tsx | 257 +++++++++++++----- packages/demobank-ui/src/pages/HomePage.tsx | 56 +++- .../src/pages/PaytoWireTransferForm.tsx | 14 +- .../src/pages/PublicHistoriesPage.tsx | 4 +- .../src/pages/RegistrationPage.tsx | 69 +++-- packages/demobank-ui/src/pages/Routing.tsx | 56 +++- .../src/pages/WithdrawalQRCode.tsx | 4 +- .../src/InstanceRoutes.tsx | 50 ++-- packages/web-util/src/utils/request.ts | 47 +++- 12 files changed, 462 insertions(+), 159 deletions(-) diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts index 3abe8cd54..6cf543a3c 100644 --- a/packages/demobank-ui/src/hooks/circuit.ts +++ b/packages/demobank-ui/src/hooks/circuit.ts @@ -33,7 +33,7 @@ const useSWR = _useSWR as unknown as SWRHook; export function useAdminAccountAPI(): AdminAccountAPI { const { request } = useAuthenticatedBackend(); const mutateAll = useMatchMutate(); - const { state } = useBackendContext(); + const { state, logIn } = useBackendContext(); if (state.status === "loggedOut") { throw Error("access-api can't be used when the user is not logged In"); } @@ -81,6 +81,13 @@ export function useAdminAccountAPI(): AdminAccountAPI { data, contentType: "json", }); + if (account === state.username) { + await mutateAll(/.*/) + logIn({ + username: account, + password: data.new_password + }) + } return res; }; @@ -288,6 +295,12 @@ export function useRatiosAndFeeConfig(): HttpResponse< keepPreviousData: true, }); + if (data) { + // data.data.ratios_and_fees.sell_out_fee = 2 + if (!data.data.ratios_and_fees.fiat_currency) { + data.data.ratios_and_fees.fiat_currency = "FIAT" + } + } if (data) return data; if (error) return error.info; return { loading: true }; diff --git a/packages/demobank-ui/src/pages/AccountPage.tsx b/packages/demobank-ui/src/pages/AccountPage.tsx index ae0c2b1f8..bd9a5acd7 100644 --- a/packages/demobank-ui/src/pages/AccountPage.tsx +++ b/packages/demobank-ui/src/pages/AccountPage.tsx @@ -28,7 +28,9 @@ import { PaymentOptions } from "./PaymentOptions.js"; interface Props { account: string; - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } /** * Query account information and show QR code if there is pending withdrawal diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx index b4ce58588..0a1dc26ec 100644 --- a/packages/demobank-ui/src/pages/AdminPage.tsx +++ b/packages/demobank-ui/src/pages/AdminPage.tsx @@ -59,7 +59,9 @@ function randomPassword(): string { } interface Props { - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } /** * Query account information and show QR code if there is pending withdrawal @@ -109,6 +111,11 @@ export function AdminPage({ onLoadNotOk }: Props): VNode { if (showCashouts) { return (
+
+

+ Cashout for account {showCashouts} +

+
{ @@ -116,15 +123,17 @@ export function AdminPage({ onLoadNotOk }: Props): VNode { setShowCashouts(undefined); }} /> - { - e.preventDefault(); - setShowCashouts(undefined); - }} - /> +

+ { + e.preventDefault(); + setShowCashouts(undefined); + }} + /> +

); } @@ -184,7 +193,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode { onClose={() => setCreateAccount(false)} onCreateSuccess={(password) => { showInfoMessage( - i18n.str`Account created with password "${password}"`, + i18n.str`Account created with password "${password}". The user must change the password on the next login.`, ); setCreateAccount(false); }} @@ -326,7 +335,9 @@ export function UpdateAccountPassword({ onUpdateSuccess, onLoadNotOk, }: { - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; onClear: () => void; onUpdateSuccess: () => void; account: string; @@ -521,7 +532,9 @@ export function ShowAccountDetails({ onLoadNotOk, onChangePassword, }: { - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; onClear?: () => void; onChangePassword: () => void; onUpdateSuccess: () => void; @@ -628,7 +641,9 @@ function RemoveAccount({ onUpdateSuccess, onLoadNotOk, }: { - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; onClear: () => void; onUpdateSuccess: () => void; account: string; @@ -806,7 +821,7 @@ function AccountForm({ />
- + void; onRegister: () => void; - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } export function BusinessAccount({ onClose, @@ -79,6 +82,9 @@ export function BusinessAccount({ setNewcashout(false); }} onComplete={(id) => { + showInfoMessage( + i18n.str`Cashout created. You need to confirm the operation to complete the transaction.`, + ); setNewcashout(false); setShowCashoutDetails(id); }} @@ -156,7 +162,9 @@ interface PropsCashout { account: string; onComplete: (id: string) => void; onCancel: () => void; - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } type FormType = { @@ -180,7 +188,7 @@ function CreateCashout({ const result = useAccountDetails(account); const [error, saveError] = useState(); - const [form, setForm] = useState>({}); + const [form, setForm] = useState>({ isDebit: true }); const { createCashout } = useCircuitAccountAPI(); if (!result.ok) return onLoadNotOk(result); @@ -277,10 +285,14 @@ function CreateCashout({ type="text" readonly class="currency-indicator" - size={balance.currency.length} - maxLength={balance.currency.length} + size={ + !form.isDebit ? fiatCurrency.length : balance.currency.length + } + maxLength={ + !form.isDebit ? fiatCurrency.length : balance.currency.length + } tabIndex={-1} - value={balance.currency} + value={!form.isDebit ? fiatCurrency : balance.currency} />  
- +
      ; + switch (e.cause.type) { + case ErrorType.TIMEOUT: { + saveError({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = e.cause.error; + + if ( + e.cause.status === HttpStatusCode.PreconditionFailed + ) { + saveError({ + title: i18n.str`The account does not have sufficient funds`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } else if (e.cause.status === HttpStatusCode.Conflict) { + saveError({ + title: i18n.str`No contact information for this channel`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } else { + saveError({ + title: i18n.str`New cashout gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } + break; + } + case ErrorType.SERVER: { + const errorData = e.cause.error; + if ( + e.cause.status === HttpStatusCode.ServiceUnavailable + ) { + saveError({ + title: i18n.str`The bank does not support the TAN channel for this operation`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } else { + saveError({ + title: i18n.str`Creating cashout returned with a server error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + } + break; + } + case ErrorType.UNEXPECTED: { + saveError({ + title: i18n.str`Unexpected error trying to create cashout.`, + debug: JSON.stringify(error.cause), + }); + break; + } + default: { + assertUnreachable(e.cause); + } } } else if (error instanceof Error) { saveError({ @@ -592,7 +639,9 @@ function CreateCashout({ interface ShowCashoutProps { id: string; onCancel: () => void; - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } export function ShowCashoutDetails({ id, @@ -699,22 +748,59 @@ export function ShowCashoutDetails({ onCancel(); } catch (error) { if (error instanceof RequestError) { - const errorData: SandboxBackend.SandboxError = - error.info.error; - if ( - error.info.status === HttpStatusCode.PreconditionFailed - ) { - saveError({ - title: i18n.str`Cashout was already aborted`, - description: errorData.error.description, - debug: JSON.stringify(error.info), - }); - } else { - saveError({ - title: i18n.str`Aborting cashout gave response error`, - description: errorData.error.description, - debug: JSON.stringify(error.info), - }); + const e = + error as RequestError; + switch (e.cause.type) { + case ErrorType.TIMEOUT: { + saveError({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = e.cause.error; + if ( + e.cause.status === HttpStatusCode.PreconditionFailed + ) { + saveError({ + title: i18n.str`Cashout was already aborted`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } else { + saveError({ + title: i18n.str`Aborting cashout gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.info), + }); + } + + saveError({ + title: i18n.str`Aborting cashout gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + break; + } + case ErrorType.SERVER: { + const errorData = e.cause.error; + saveError({ + title: i18n.str`Aborting cashout returned with a server error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + break; + } + case ErrorType.UNEXPECTED: { + saveError({ + title: i18n.str`Unexpected error trying to abort cashout.`, + debug: JSON.stringify(error.cause), + }); + break; + } + default: { + assertUnreachable(e.cause); + } } } else if (error instanceof Error) { saveError({ @@ -741,13 +827,44 @@ export function ShowCashoutDetails({ }); } catch (error) { if (error instanceof RequestError) { - const errorData: SandboxBackend.SandboxError = - error.info.error; - saveError({ - title: i18n.str`Confirmation of cashout gave response error`, - description: errorData.error.description, - debug: JSON.stringify(error.info), - }); + const e = + error as RequestError; + switch (e.cause.type) { + case ErrorType.TIMEOUT: { + saveError({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = e.cause.error; + saveError({ + title: i18n.str`Confirmation of cashout gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + break; + } + case ErrorType.SERVER: { + const errorData = e.cause.error; + saveError({ + title: i18n.str`Confirmation of cashout gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + break; + } + case ErrorType.UNEXPECTED: { + saveError({ + title: i18n.str`Unexpected error trying to cashout.`, + debug: JSON.stringify(error.cause), + }); + break; + } + default: { + assertUnreachable(e.cause); + } + } } else if (error instanceof Error) { saveError({ title: i18n.str`Confirmation failed, please report`, @@ -767,3 +884,7 @@ export function ShowCashoutDetails({
); } + +export function assertUnreachable(x: never): never { + throw new Error("Didn't expect to get here"); +} diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx index 5af195f48..a360bd64c 100644 --- a/packages/demobank-ui/src/pages/HomePage.tsx +++ b/packages/demobank-ui/src/pages/HomePage.tsx @@ -14,9 +14,11 @@ GNU Taler; see the file COPYING. If not, see */ -import { Logger } from "@gnu-taler/taler-util"; +import { HttpStatusCode, Logger } from "@gnu-taler/taler-util"; import { + ErrorType, HttpResponsePaginated, + RequestError, useTranslationContext, } from "@gnu-taler/web-util/lib/index.browser"; import { Fragment, h, VNode } from "preact"; @@ -119,9 +121,9 @@ function handleNotOkResult( onErrorHandler: (state: PageStateType["error"]) => void, i18n: ReturnType["i18n"], onRegister: () => void, -): (result: HttpResponsePaginated) => VNode { - return function handleNotOkResult2( - result: HttpResponsePaginated, +): (result: HttpResponsePaginated) => VNode { + return function handleNotOkResult2( + result: HttpResponsePaginated, ): VNode { if (result.clientError && result.isUnauthorized) { onErrorHandler({ @@ -137,13 +139,49 @@ function handleNotOkResult( } if (result.loading) return ; if (!result.ok) { - onErrorHandler({ - title: i18n.str`The backend reported a problem: HTTP status #${result.status}`, - description: `Diagnostic from ${result.info?.url} is "${result.message}"`, - debug: JSON.stringify(result.error), - }); + switch (result.type) { + case ErrorType.TIMEOUT: { + onErrorHandler({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = result.error; + onErrorHandler({ + title: i18n.str`Could not load due to a client error`, + description: errorData.error.description, + debug: JSON.stringify(result), + }); + break; + } + case ErrorType.SERVER: { + const errorData = result.error; + onErrorHandler({ + title: i18n.str`Server returned with error`, + description: errorData.error.description, + debug: JSON.stringify(result), + }); + break; + } + case ErrorType.UNEXPECTED: { + onErrorHandler({ + title: i18n.str`Unexpected error.`, + description: `Diagnostic from ${result.info?.url} is "${result.message}"`, + debug: JSON.stringify(result.error), + }); + break; + } + default: { + assertUnreachable(result); + } + } + return ; } return
; }; } +export function assertUnreachable(x: never): never { + throw new Error("Didn't expect to get here"); +} diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index d859b1cc7..07b011a00 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -48,7 +48,7 @@ export function PaytoWireTransferForm({ onSuccess: () => void; currency: string; }): VNode { - const backend = useBackendContext(); + // const backend = useBackendContext(); // const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button? const [isRawPayto, setIsRawPayto] = useState(false); @@ -188,17 +188,7 @@ export function PaytoWireTransferForm({ paytoUri, amount: `${currency}:${amount}`, }); - // return await createTransactionCall( - // transactionData, - // backend.state, - // pageStateSetter, - // () => { - // setAmount(undefined); - // setIban(undefined); - // setSubject(undefined); - // }, - // i18n, - // ); + onSuccess(); }} /> (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } /** diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx index 247ef8d80..c6bc3c327 100644 --- a/packages/demobank-ui/src/pages/RegistrationPage.tsx +++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx @@ -15,6 +15,7 @@ */ import { HttpStatusCode, Logger } from "@gnu-taler/taler-util"; import { + ErrorType, RequestError, useTranslationContext, } from "@gnu-taler/web-util/lib/index.browser"; @@ -176,26 +177,52 @@ function RegistrationForm({ onComplete(); } catch (error) { if (error instanceof RequestError) { - const errorData: SandboxBackend.SandboxError = - error.info.error; - if (error.info.status === HttpStatusCode.Conflict) { - onError({ - title: i18n.str`That username is already taken`, - description: errorData.error.description, - debug: JSON.stringify(error.info), - }); - } else { - onError({ - title: i18n.str`New registration gave response error`, - description: errorData.error.description, - debug: JSON.stringify(error.info), - }); + const e = + error as RequestError; + switch (e.cause.type) { + case ErrorType.TIMEOUT: { + onError({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = e.cause.error; + if (e.cause.status === HttpStatusCode.Conflict) { + onError({ + title: i18n.str`That username is already taken`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + } else { + onError({ + title: i18n.str`New registration gave response error`, + description: errorData.error.description, + debug: JSON.stringify(error.cause), + }); + } + break; + } + case ErrorType.SERVER: { + const errorData = e.cause.error; + onError({ + title: i18n.str`New registration gave response error`, + description: errorData?.error?.description, + debug: JSON.stringify(error.cause), + }); + break; + } + case ErrorType.UNEXPECTED: { + onError({ + title: i18n.str`Unexpected error doing the registration.`, + debug: JSON.stringify(error.cause), + }); + break; + } + default: { + assertUnreachable(e.cause); + } } - } else if (error instanceof Error) { - onError({ - title: i18n.str`Registration failed, please report`, - description: error.message, - }); } } }} @@ -222,3 +249,7 @@ function RegistrationForm({ ); } + +export function assertUnreachable(x: never): never { + throw new Error("Didn't expect to get here"); +} diff --git a/packages/demobank-ui/src/pages/Routing.tsx b/packages/demobank-ui/src/pages/Routing.tsx index 48f226574..8234d8988 100644 --- a/packages/demobank-ui/src/pages/Routing.tsx +++ b/packages/demobank-ui/src/pages/Routing.tsx @@ -15,6 +15,7 @@ */ import { + ErrorType, HttpResponsePaginated, useTranslationContext, } from "@gnu-taler/web-util/lib/index.browser"; @@ -34,9 +35,9 @@ function handleNotOkResult( safe: string, saveError: (state: PageStateType["error"]) => void, i18n: ReturnType["i18n"], -): (result: HttpResponsePaginated) => VNode { - return function handleNotOkResult2( - result: HttpResponsePaginated, +): (result: HttpResponsePaginated) => VNode { + return function handleNotOkResult2( + result: HttpResponsePaginated, ): VNode { if (result.clientError && result.isUnauthorized) { route(safe); @@ -50,12 +51,45 @@ function handleNotOkResult( } if (result.loading) return ; if (!result.ok) { - saveError({ - title: i18n.str`The backend reported a problem: HTTP status #${result.status}`, - description: i18n.str`Diagnostic from ${result.info?.url} is "${result.message}"`, - debug: JSON.stringify(result.error), - }); - route(safe); + switch (result.type) { + case ErrorType.TIMEOUT: { + saveError({ + title: i18n.str`Request timeout, try again later.`, + }); + break; + } + case ErrorType.CLIENT: { + const errorData = result.error; + saveError({ + title: i18n.str`Could not load due to a client error`, + description: errorData.error.description, + debug: JSON.stringify(result), + }); + break; + } + case ErrorType.SERVER: { + const errorData = result.error; + saveError({ + title: i18n.str`Server returned with error`, + description: errorData.error.description, + debug: JSON.stringify(result), + }); + break; + } + case ErrorType.UNEXPECTED: { + saveError({ + title: i18n.str`Unexpected error.`, + description: `Diagnostic from ${result.info?.url} is "${result.message}"`, + debug: JSON.stringify(result.error), + }); + break; + } + default: + { + assertUnreachable(result); + } + route(safe); + } } return
; }; @@ -137,3 +171,7 @@ function Redirect({ to }: { to: string }): VNode { }, []); return
being redirected to {to}
; } + +export function assertUnreachable(x: never): never { + throw new Error("Didn't expect to get here"); +} diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index fd91c0e1a..d4c95d830 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -33,7 +33,9 @@ interface Props { withdrawalId: string; talerWithdrawUri: string; onAbort: () => void; - onLoadNotOk: (error: HttpResponsePaginated) => VNode; + onLoadNotOk: ( + error: HttpResponsePaginated, + ) => VNode; } /** * Offer the QR code (and a clickable taler://-link) to diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx index 5929b031a..e31ff4513 100644 --- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx +++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx @@ -22,6 +22,7 @@ import { useTranslationContext, HttpError, + ErrorType, } from "@gnu-taler/web-util/lib/index.browser"; import { format } from "date-fns"; import { Fragment, FunctionComponent, h, VNode } from "preact"; @@ -163,16 +164,25 @@ export function InstanceRoutes({ return function ServerErrorRedirectToImpl( error: HttpError, ) { - setGlobalNotification({ - message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, - description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`, - details: - error.clientError || error.serverError - ? error.error?.detail - : undefined, - type: "ERROR", - to, - }); + if (error.type === ErrorType.TIMEOUT) { + setGlobalNotification({ + message: i18n.str`The request to the backend take too long and was cancelled`, + description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`, + type: "ERROR", + to, + }); + } else { + setGlobalNotification({ + message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, + description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`, + details: + error.clientError || error.serverError + ? error.error?.detail + : undefined, + type: "ERROR", + to, + }); + } return ; }; } @@ -572,19 +582,25 @@ function AdminInstanceUpdatePage({ {...rest} instanceId={id} onLoadError={(error: HttpError) => { - return ( - - + type: "ERROR" as const, + }; + return ( + + ); diff --git a/packages/web-util/src/utils/request.ts b/packages/web-util/src/utils/request.ts index 24342bb80..3d91024dc 100644 --- a/packages/web-util/src/utils/request.ts +++ b/packages/web-util/src/utils/request.ts @@ -17,6 +17,10 @@ import { HttpStatusCode } from "@gnu-taler/taler-util"; import { base64encode } from "./base64.js"; +export enum ErrorType { + CLIENT, SERVER, TIMEOUT, UNEXPECTED +} + /** * * @param baseUrl URL where the service is located @@ -39,7 +43,7 @@ export async function defaultRequestHandler( const requestMethod = options?.method ?? "GET"; const requestBody = options?.data; - const requestTimeout = options?.timeout ?? 2 * 1000; + const requestTimeout = options?.timeout ?? 5 * 1000; const requestParams = options.params ?? {}; const _url = new URL(`${baseUrl}${endpoint}`); @@ -85,10 +89,13 @@ export async function defaultRequestHandler( hasToken: !!options.token, status: 0, }; - const error: HttpResponseUnexpectedError = { + const error: HttpRequestTimeoutError = { + clientError: true, + isNotfound: false, + isUnauthorized: false, + error: undefined, info, - status: 0, - error: ex, + type: ErrorType.TIMEOUT, message: "Request timeout", }; throw new RequestError(error); @@ -166,32 +173,50 @@ export interface WithPagination { } export type HttpError = + | HttpRequestTimeoutError | HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError; + export interface HttpResponseServerError { ok?: false; loading?: false; clientError?: false; serverError: true; + type: ErrorType.SERVER, - error?: ErrorDetail; + error: ErrorDetail; status: HttpStatusCode; message: string; info?: RequestInfo; } +interface HttpRequestTimeoutError { + ok?: false; + loading?: false; + clientError: true; + serverError?: false; + type: ErrorType.TIMEOUT, + + info?: RequestInfo; + error: undefined, + + isUnauthorized: false; + isNotfound: false; + message: string; +} interface HttpResponseClientError { ok?: false; loading?: false; clientError: true; serverError?: false; + type: ErrorType.CLIENT, info?: RequestInfo; isUnauthorized: boolean; isNotfound: boolean; status: HttpStatusCode; - error?: ErrorDetail; + error: ErrorDetail; message: string; } @@ -200,6 +225,7 @@ interface HttpResponseUnexpectedError { loading?: false; clientError?: false; serverError?: false; + type: ErrorType.UNEXPECTED, info?: RequestInfo; status?: HttpStatusCode; @@ -208,10 +234,15 @@ interface HttpResponseUnexpectedError { } export class RequestError extends Error { + /** + * @deprecated use cause + */ info: HttpError; + cause: HttpError; constructor(d: HttpError) { super(d.message) this.info = d + this.cause = d } } @@ -277,6 +308,7 @@ async function buildRequestFailed( clientError: true, isNotfound: status === 404, isUnauthorized: status === 401, + type: ErrorType.CLIENT, status, info, message: data?.hint, @@ -287,6 +319,7 @@ async function buildRequestFailed( if (status && status >= 500 && status < 600) { const error: HttpResponseServerError = { serverError: true, + type: ErrorType.SERVER, status, info, message: `${data?.hint} (code ${data?.code})`, @@ -296,6 +329,7 @@ async function buildRequestFailed( } return { info, + type: ErrorType.UNEXPECTED, status, error: {}, message: "NOT DEFINED", @@ -304,6 +338,7 @@ async function buildRequestFailed( const error: HttpResponseUnexpectedError = { info, status, + type: ErrorType.UNEXPECTED, error: ex, message: "NOT DEFINED", };