gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 03/03: test login with an endpoint and cleaner calcu


From: gnunet
Subject: [taler-wallet-core] 03/03: test login with an endpoint and cleaner calculation
Date: Wed, 15 Mar 2023 13:25:40 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 0bf92a44df14f1946df2b1cd58a6b0b92b5befa2
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Mar 15 09:25:23 2023 -0300

    test login with an endpoint and cleaner calculation
---
 packages/demobank-ui/src/hooks/backend.ts          |  28 ++++++
 packages/demobank-ui/src/hooks/circuit.ts          |   3 +-
 packages/demobank-ui/src/pages/AdminPage.tsx       |   5 +-
 packages/demobank-ui/src/pages/BusinessAccount.tsx | 111 +++++++++++++--------
 packages/demobank-ui/src/pages/LoginForm.tsx       |  48 ++++++++-
 5 files changed, 146 insertions(+), 49 deletions(-)

diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index 3eaf1f186..851a3fb5b 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -16,6 +16,7 @@
 
 import { canonicalizeBaseUrl } from "@gnu-taler/taler-util";
 import {
+  ErrorType,
   RequestError,
   useLocalStorage,
 } from "@gnu-taler/web-util/lib/index.browser";
@@ -192,6 +193,33 @@ export function usePublicBackend(): useBackendType {
   };
 }
 
+export function useCredentialsChecker() {
+  const { request } = useApiContext();
+  const baseUrl = getInitialBackendBaseURL();
+  //check against account details endpoint
+  //while sandbox backend doesn't have a login endpoint
+  return async function testLogin(
+    username: string,
+    password: string,
+  ): Promise<{
+    valid: boolean;
+    cause?: ErrorType;
+  }> {
+    try {
+      await request(baseUrl, `access-api/accounts/${username}/`, {
+        basicAuth: { username, password },
+        preventCache: true,
+      });
+      return { valid: true };
+    } catch (error) {
+      if (error instanceof RequestError) {
+        return { valid: false, cause: error.cause.type };
+      }
+      return { valid: false, cause: ErrorType.UNEXPECTED };
+    }
+  };
+}
+
 export function useAuthenticatedBackend(): useBackendType {
   const { state } = useBackendContext();
   const { request: requestHandler } = useApiContext();
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index 548862d85..137a7aee2 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -288,9 +288,10 @@ export function useRatiosAndFeeConfig(): HttpResponse<
     HttpResponseOk<SandboxBackend.Circuit.Config>,
     RequestError<SandboxBackend.SandboxError>
   >([`circuit-api/config`], fetcher, {
-    refreshInterval: 1000,
+    refreshInterval: 60 * 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
+    revalidateIfStale: false,
     revalidateOnReconnect: false,
     refreshWhenOffline: false,
     errorRetryCount: 0,
diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx 
b/packages/demobank-ui/src/pages/AdminPage.tsx
index f565455bb..ac0457e9b 100644
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ b/packages/demobank-ui/src/pages/AdminPage.tsx
@@ -36,9 +36,9 @@ import {
 } from "../context/pageState.js";
 import { useAccountDetails } from "../hooks/access.js";
 import {
+  useAdminAccountAPI,
   useBusinessAccountDetails,
   useBusinessAccounts,
-  useAdminAccountAPI,
 } from "../hooks/circuit.js";
 import {
   buildRequestErrorMessage,
@@ -50,7 +50,6 @@ import {
 } from "../utils.js";
 import { ErrorBannerFloat } from "./BankFrame.js";
 import { ShowCashoutDetails } from "./BusinessAccount.js";
-import { PaymentOptions } from "./PaymentOptions.js";
 import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
 import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
 
@@ -581,7 +580,6 @@ function CreateNewAccount({
           template={undefined}
           purpose="create"
           onChange={(a) => {
-            console.log(a);
             setSubmitAccount(a);
           }}
         />
@@ -831,6 +829,7 @@ function RemoveAccount({
             title: i18n.str`Can't delete the account`,
             description: i18n.str`Balance is not empty`,
           }}
+          onClear={() => saveError(undefined)}
         />
       )}
       {error && (
diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx 
b/packages/demobank-ui/src/pages/BusinessAccount.tsx
index 128b47114..258802e58 100644
--- a/packages/demobank-ui/src/pages/BusinessAccount.tsx
+++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx
@@ -237,6 +237,7 @@ function CreateCashout({
   if (!result.ok) return onLoadNotOk(result);
   if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
   const config = ratiosResult.data;
+
   const balance = Amounts.parseOrThrow(result.data.balance.amount);
   const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
   const zero = Amounts.zeroOfCurrency(balance.currency);
@@ -254,23 +255,14 @@ function CreateCashout({
   if (!sellRate || sellRate < 0) return <div>error rate</div>;
 
   const amount = Amounts.parse(`${balance.currency}:${form.amount}`);
-  const amount_debit = !amount
-    ? zero
-    : form.isDebit
-    ? amount
-    : truncate(Amounts.divide(Amounts.add(amount, sellFee).amount, sellRate));
-  const credit_before_fee = !amount
-    ? zero
-    : form.isDebit
-    ? truncate(Amounts.divide(amount, 1 / sellRate))
-    : Amounts.add(amount, sellFee).amount;
 
-  const __amount_credit = Amounts.sub(credit_before_fee, sellFee).amount;
-  const amount_credit = Amounts.parseOrThrow(
-    `${fiatCurrency}:${Amounts.stringifyValue(__amount_credit)}`,
-  );
+  const calc = !amount
+    ? { debit: zero, credit: zero, beforeFee: zero }
+    : !form.isDebit
+    ? calculateFromCredit(amount, sellFee, sellRate)
+    : calculateFromDebit(amount, sellFee, sellRate);
 
-  const balanceAfter = Amounts.sub(balance, amount_debit).amount;
+  const balanceAfter = Amounts.sub(balance, calc.debit).amount;
 
   function updateForm(newForm: typeof form): void {
     setForm(newForm);
@@ -280,11 +272,11 @@ function CreateCashout({
       ? i18n.str`required`
       : !amount
       ? i18n.str`could not be parsed`
-      : Amounts.cmp(limit, amount_debit) === -1
+      : Amounts.cmp(limit, calc.debit) === -1
       ? i18n.str`balance is not enough`
-      : Amounts.cmp(credit_before_fee, sellFee) === -1
+      : Amounts.cmp(calc.beforeFee, sellFee) === -1
       ? i18n.str`the total amount to transfer does not cover the fees`
-      : Amounts.isZero(amount_credit)
+      : Amounts.isZero(calc.credit)
       ? i18n.str`the total transfer at destination will be zero`
       : undefined,
     channel: !form.channel ? i18n.str`required` : undefined,
@@ -408,7 +400,7 @@ function CreateCashout({
               id="withdraw-amount"
               disabled
               name="withdraw-amount"
-              value={amount_debit ? Amounts.stringifyValue(amount_debit) : ""}
+              value={Amounts.stringifyValue(calc.debit)}
             />
           </div>
         </fieldset>
@@ -454,7 +446,7 @@ function CreateCashout({
                   // type="number"
                   style={{ color: "black" }}
                   disabled
-                  value={Amounts.stringifyValue(credit_before_fee)}
+                  value={Amounts.stringifyValue(calc.beforeFee)}
                 />
               </div>
             </fieldset>
@@ -503,7 +495,7 @@ function CreateCashout({
               id="withdraw-amount"
               disabled
               name="withdraw-amount"
-              value={amount_credit ? Amounts.stringifyValue(amount_credit) : 
""}
+              value={Amounts.stringifyValue(calc.credit)}
             />
           </div>
         </fieldset>
@@ -584,8 +576,10 @@ function CreateCashout({
               if (errors) return;
               try {
                 const res = await createCashout({
-                  amount_credit: Amounts.stringify(amount_credit),
-                  amount_debit: Amounts.stringify(amount_debit),
+                  amount_credit: `${fiatCurrency}:${Amounts.stringifyValue(
+                    calc.credit,
+                  )}`,
+                  amount_debit: Amounts.stringify(calc.debit),
                   subject: form.subject,
                   tan_channel: form.channel,
                 });
@@ -630,25 +624,6 @@ function CreateCashout({
   );
 }
 
-const MAX_AMOUNT_DIGIT = 2;
-/**
- * Truncate the amount of digits to display
- * in the form based on the fee calculations
- *
- * Backend must have the same truncation
- * @param a
- * @returns
- */
-function truncate(a: AmountJson): AmountJson {
-  const str = Amounts.stringify(a);
-  const idx = str.indexOf(".");
-  if (idx === -1) {
-    return a;
-  }
-  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT);
-  return Amounts.parseOrThrow(truncated);
-}
-
 interface ShowCashoutProps {
   id: string;
   onCancel: () => void;
@@ -836,6 +811,58 @@ export function ShowCashoutDetails({
   );
 }
 
+const MAX_AMOUNT_DIGIT = 2;
+/**
+ * Truncate the amount of digits to display
+ * in the form based on the fee calculations
+ *
+ * Backend must have the same truncation
+ * @param a
+ * @returns
+ */
+function truncate(a: AmountJson): AmountJson {
+  const str = Amounts.stringify(a);
+  const idx = str.indexOf(".");
+  if (idx === -1) {
+    return a;
+  }
+  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT);
+  return Amounts.parseOrThrow(truncated);
+}
+
+type TransferCalculation = {
+  debit: AmountJson;
+  credit: AmountJson;
+  beforeFee: AmountJson;
+};
+
+function calculateFromDebit(
+  amount: AmountJson,
+  sellFee: AmountJson,
+  sellRate: number,
+): TransferCalculation {
+  const debit = amount;
+
+  const beforeFee = truncate(Amounts.divide(debit, 1 / sellRate));
+
+  const credit = Amounts.sub(beforeFee, sellFee).amount;
+  return { debit, credit, beforeFee };
+}
+
+function calculateFromCredit(
+  amount: AmountJson,
+  sellFee: AmountJson,
+  sellRate: number,
+): TransferCalculation {
+  const credit = amount;
+
+  const beforeFee = Amounts.add(credit, sellFee).amount;
+
+  const debit = truncate(Amounts.divide(beforeFee, sellRate));
+
+  return { debit, credit, beforeFee };
+}
+
 export function assertUnreachable(x: never): never {
   throw new Error("Didn't expect to get here");
 }
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 2452745a5..16d2373da 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -14,12 +14,18 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import {
+  ErrorType,
+  useTranslationContext,
+} from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
+import { ErrorMessage } from "../context/pageState.js";
+import { useCredentialsChecker } from "../hooks/backend.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
+import { ErrorBannerFloat } from "./BankFrame.js";
 import { USERNAME_REGEX } from "./RegistrationPage.js";
 import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
 
@@ -31,6 +37,8 @@ export function LoginForm({ onRegister }: { onRegister: () => 
void }): VNode {
   const [username, setUsername] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
+  const testLogin = useCredentialsChecker();
+  const [error, saveError] = useState<ErrorMessage | undefined>();
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
     ref.current?.focus();
@@ -48,6 +56,9 @@ export function LoginForm({ onRegister }: { onRegister: () => 
void }): VNode {
   return (
     <Fragment>
       <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
+      {error && (
+        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
+      )}
       <div class="login-div">
         <form
           class="login-form"
@@ -105,10 +116,41 @@ export function LoginForm({ onRegister }: { onRegister: 
() => void }): VNode {
               type="submit"
               class="pure-button pure-button-primary"
               disabled={!!errors}
-              onClick={(e) => {
+              onClick={async (e) => {
                 e.preventDefault();
                 if (!username || !password) return;
-                backend.logIn({ username, password });
+                const { valid, cause } = await testLogin(username, password);
+                if (valid) {
+                  backend.logIn({ username, password });
+                } else {
+                  switch (cause) {
+                    case ErrorType.CLIENT: {
+                      saveError({
+                        title: i18n.str`Wrong credentials or username`,
+                      });
+                      break;
+                    }
+                    case ErrorType.SERVER: {
+                      saveError({
+                        title: i18n.str`Server had a problem, try again later 
or report.`,
+                      });
+                      break;
+                    }
+                    case ErrorType.TIMEOUT: {
+                      saveError({
+                        title: i18n.str`Could not reach the server, please 
report.`,
+                      });
+                      break;
+                    }
+                    default: {
+                      saveError({
+                        title: i18n.str`Unexpected error, please report.`,
+                      });
+                      break;
+                    }
+                  }
+                  backend.logOut();
+                }
                 setUsername(undefined);
                 setPassword(undefined);
               }}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]