aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-02-25 19:43:45 -0300
committerSebastian <sebasjm@gmail.com>2023-02-25 19:43:59 -0300
commit1723f16b9c4b008e9e44578c2587c7a1bd6560b4 (patch)
tree4c10315d97edcdf215e3cd329ffb67231adf135e
parentdd2599ff94f4ecc878abc9f6c87603fd180ac3de (diff)
downloadwallet-core-1723f16b9c4b008e9e44578c2587c7a1bd6560b4.tar.xz
some fixes afte testing demobank with ms
-rw-r--r--packages/demobank-ui/src/hooks/circuit.ts15
-rw-r--r--packages/demobank-ui/src/pages/AccountPage.tsx4
-rw-r--r--packages/demobank-ui/src/pages/AdminPage.tsx45
-rw-r--r--packages/demobank-ui/src/pages/BusinessAccount.tsx257
-rw-r--r--packages/demobank-ui/src/pages/HomePage.tsx56
-rw-r--r--packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx14
-rw-r--r--packages/demobank-ui/src/pages/PublicHistoriesPage.tsx4
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx69
-rw-r--r--packages/demobank-ui/src/pages/Routing.tsx56
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalQRCode.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/InstanceRoutes.tsx50
-rw-r--r--packages/web-util/src/utils/request.ts47
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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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 (
<div>
+ <div>
+ <h1 class="nav welcome-text">
+ <i18n.Translate>Cashout for account {showCashouts}</i18n.Translate>
+ </h1>
+ </div>
<Cashouts
account={showCashouts}
onSelected={(id) => {
@@ -116,15 +123,17 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
setShowCashouts(undefined);
}}
/>
- <input
- class="pure-button"
- type="submit"
- value={i18n.str`Close`}
- onClick={async (e) => {
- e.preventDefault();
- setShowCashouts(undefined);
- }}
- />
+ <p>
+ <input
+ class="pure-button"
+ type="submit"
+ value={i18n.str`Close`}
+ onClick={async (e) => {
+ e.preventDefault();
+ setShowCashouts(undefined);
+ }}
+ />
+ </p>
</div>
);
}
@@ -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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear: () => void;
onUpdateSuccess: () => void;
account: string;
@@ -521,7 +532,9 @@ export function ShowAccountDetails({
onLoadNotOk,
onChangePassword,
}: {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear?: () => void;
onChangePassword: () => void;
onUpdateSuccess: () => void;
@@ -628,7 +641,9 @@ function RemoveAccount({
onUpdateSuccess,
onLoadNotOk,
}: {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear: () => void;
onUpdateSuccess: () => void;
account: string;
@@ -806,7 +821,7 @@ function AccountForm({
/>
</fieldset>
<fieldset>
- <label>{i18n.str`IBAN`}</label>
+ <label>{i18n.str`Internal IBAN`}</label>
<input
disabled={purpose !== "create"}
value={form.iban ?? ""}
diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx
index 23a03cc9b..6278fe08b 100644
--- a/packages/demobank-ui/src/pages/BusinessAccount.tsx
+++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx
@@ -20,6 +20,7 @@ import {
TranslatedString,
} from "@gnu-taler/taler-util";
import {
+ ErrorType,
HttpResponsePaginated,
RequestError,
useTranslationContext,
@@ -44,7 +45,9 @@ import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
interface Props {
onClose: () => void;
onRegister: () => void;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
type FormType = {
@@ -180,7 +188,7 @@ function CreateCashout({
const result = useAccountDetails(account);
const [error, saveError] = useState<ErrorMessage | undefined>();
- const [form, setForm] = useState<Partial<FormType>>({});
+ const [form, setForm] = useState<Partial<FormType>>({ 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}
/>
&nbsp;
<input
@@ -389,16 +401,16 @@ function CreateCashout({
{Amounts.isZero(sellFee) ? undefined : (
<Fragment>
<fieldset>
- <label>{i18n.str`Transfer before fee`}</label>
+ <label>{i18n.str`Amount after conversion`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -417,10 +429,10 @@ function CreateCashout({
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -442,10 +454,10 @@ function CreateCashout({
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -543,34 +555,69 @@ function CreateCashout({
onComplete(res.data.uuid);
} catch (error) {
if (error instanceof RequestError) {
- const errorData: SandboxBackend.SandboxError =
- error.info.error;
- if (error.info.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 (
- error.info.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 if (error.info.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),
- });
+ const e = error as RequestError<SandboxBackend.SandboxError>;
+ 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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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<SandboxBackend.SandboxError>;
+ 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<SandboxBackend.SandboxError>;
+ 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({
</div>
);
}
+
+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 <http://www.gnu.org/licenses/>
*/
-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<typeof useTranslationContext>["i18n"],
onRegister: () => void,
-): <T, E>(result: HttpResponsePaginated<T, E>) => VNode {
- return function handleNotOkResult2<T, E>(
- result: HttpResponsePaginated<T, E>,
+): <T>(result: HttpResponsePaginated<T, SandboxBackend.SandboxError>) => VNode {
+ return function handleNotOkResult2<T>(
+ result: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
): VNode {
if (result.clientError && result.isUnauthorized) {
onErrorHandler({
@@ -137,13 +139,49 @@ function handleNotOkResult(
}
if (result.loading) return <Loading />;
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 <LoginForm onRegister={onRegister} />;
}
return <div />;
};
}
+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();
}}
/>
<input
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index 54a77b42a..2b5f7e26c 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -36,7 +36,9 @@ const logger = new Logger("PublicHistoriesPage");
// }
interface Props {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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<SandboxBackend.SandboxError>;
+ 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({
</Fragment>
);
}
+
+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<typeof useTranslationContext>["i18n"],
-): <T, E>(result: HttpResponsePaginated<T, E>) => VNode {
- return function handleNotOkResult2<T, E>(
- result: HttpResponsePaginated<T, E>,
+): <T>(result: HttpResponsePaginated<T, SandboxBackend.SandboxError>) => VNode {
+ return function handleNotOkResult2<T>(
+ result: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
): VNode {
if (result.clientError && result.isUnauthorized) {
route(safe);
@@ -50,12 +51,45 @@ function handleNotOkResult(
}
if (result.loading) return <Loading />;
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 <div />;
};
@@ -137,3 +171,7 @@ function Redirect({ to }: { to: string }): VNode {
}, []);
return <div>being redirected to {to}</div>;
}
+
+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: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => 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<MerchantBackend.ErrorDetail>,
) {
- 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 <Redirect to={to} />;
};
}
@@ -572,19 +582,25 @@ function AdminInstanceUpdatePage({
{...rest}
instanceId={id}
onLoadError={(error: HttpError<MerchantBackend.ErrorDetail>) => {
- return (
- <Fragment>
- <NotificationCard
- notification={{
+ const notif =
+ error.type === ErrorType.TIMEOUT
+ ? {
+ 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" as const,
+ }
+ : {
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",
- }}
- />
+ type: "ERROR" as const,
+ };
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
<LoginPage onConfirm={updateLoginStatus} />
</Fragment>
);
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<T>(
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<T>(
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<ErrorDetail> =
+ | HttpRequestTimeoutError
| HttpResponseClientError<ErrorDetail>
| HttpResponseServerError<ErrorDetail>
| HttpResponseUnexpectedError;
+
export interface HttpResponseServerError<ErrorDetail> {
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<ErrorDetail> {
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<ErrorDetail> extends Error {
+ /**
+ * @deprecated use cause
+ */
info: HttpError<ErrorDetail>;
+ cause: HttpError<ErrorDetail>;
constructor(d: HttpError<ErrorDetail>) {
super(d.message)
this.info = d
+ this.cause = d
}
}
@@ -277,6 +308,7 @@ async function buildRequestFailed<ErrorDetail>(
clientError: true,
isNotfound: status === 404,
isUnauthorized: status === 401,
+ type: ErrorType.CLIENT,
status,
info,
message: data?.hint,
@@ -287,6 +319,7 @@ async function buildRequestFailed<ErrorDetail>(
if (status && status >= 500 && status < 600) {
const error: HttpResponseServerError<ErrorDetail> = {
serverError: true,
+ type: ErrorType.SERVER,
status,
info,
message: `${data?.hint} (code ${data?.code})`,
@@ -296,6 +329,7 @@ async function buildRequestFailed<ErrorDetail>(
}
return {
info,
+ type: ErrorType.UNEXPECTED,
status,
error: {},
message: "NOT DEFINED",
@@ -304,6 +338,7 @@ async function buildRequestFailed<ErrorDetail>(
const error: HttpResponseUnexpectedError = {
info,
status,
+ type: ErrorType.UNEXPECTED,
error: ex,
message: "NOT DEFINED",
};