gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 02/05: exchange api for web


From: gnunet
Subject: [taler-wallet-core] 02/05: exchange api for web
Date: Fri, 26 Apr 2024 19:31:56 +0200

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

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

commit dbe5a5e5ee646b0d6824bc461c95f3c8c1572ced
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Apr 26 13:54:34 2024 -0300

    exchange api for web
---
 packages/web-util/src/context/activity.ts     |   6 +-
 packages/web-util/src/context/exchange-api.ts | 204 ++++++++++++++++++++++++++
 packages/web-util/src/context/index.ts        |   1 +
 packages/web-util/src/forms/DefaultForm.tsx   |   2 +-
 4 files changed, 211 insertions(+), 2 deletions(-)

diff --git a/packages/web-util/src/context/activity.ts 
b/packages/web-util/src/context/activity.ts
index fd366cbe5..d12d1efb6 100644
--- a/packages/web-util/src/context/activity.ts
+++ b/packages/web-util/src/context/activity.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { ChallengerHttpClient, ObservabilityEvent, 
TalerAuthenticationHttpClient, TalerBankConversionHttpClient, 
TalerCoreBankHttpClient, TalerMerchantInstanceHttpClient, 
TalerMerchantManagementHttpClient } from "@gnu-taler/taler-util";
+import { ChallengerHttpClient, ObservabilityEvent, 
TalerAuthenticationHttpClient, TalerBankConversionHttpClient, 
TalerCoreBankHttpClient, TalerExchangeHttpClient, 
TalerMerchantInstanceHttpClient, TalerMerchantManagementHttpClient } from 
"@gnu-taler/taler-util";
 
 type Listener<Event> = (e: Event) => void;
 type Unsuscriber = () => void;
@@ -60,6 +60,10 @@ export interface MerchantLib {
   subInstanceApi: (instanceId: string) => MerchantLib;
 }
 
+export interface ExchangeLib {
+  exchange: TalerExchangeHttpClient;
+}
+
 export interface BankLib {
   bank: TalerCoreBankHttpClient;
   conversion: TalerBankConversionHttpClient;
diff --git a/packages/web-util/src/context/exchange-api.ts 
b/packages/web-util/src/context/exchange-api.ts
new file mode 100644
index 000000000..6f0b6b9f4
--- /dev/null
+++ b/packages/web-util/src/context/exchange-api.ts
@@ -0,0 +1,204 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+  CacheEvictor,
+  LibtoolVersion,
+  ObservabilityEvent,
+  ObservableHttpClientLibrary,
+  TalerError,
+  TalerExchangeApi,
+  TalerExchangeCacheEviction,
+  TalerExchangeHttpClient
+} from "@gnu-taler/taler-util";
+import {
+  ComponentChildren,
+  FunctionComponent,
+  VNode,
+  createContext,
+  h,
+} from "preact";
+import { useContext, useEffect, useState } from "preact/hooks";
+import { BrowserFetchHttpLib } from "../index.browser.js";
+import {
+  APIClient,
+  ActiviyTracker,
+  ExchangeLib,
+  Subscriber,
+} from "./activity.js";
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+export type ExchangeContextType = {
+  url: URL;
+  config: TalerExchangeApi.ExchangeVersionResponse;
+  lib: ExchangeLib;
+  hints: VersionHint[];
+  onActivity: Subscriber<ObservabilityEvent>;
+  cancelRequest: (eventId: string) => void;
+};
+
+// FIXME: below
+// @ts-expect-error default value to undefined, should it be another thing?
+const ExchangeContext = createContext<ExchangeContextType>(undefined);
+
+export const useExchangeApiContext = (): ExchangeContextType =>
+  useContext(ExchangeContext);
+
+enum VersionHint {
+  NONE,
+}
+
+type Evictors = {
+  exchange?: CacheEvictor<TalerExchangeCacheEviction>;
+};
+
+type ConfigResult<T> =
+  | undefined
+  | { type: "ok"; config: T; hints: VersionHint[] }
+  | ConfigResultFail<T>;
+
+type ConfigResultFail<T> =
+  | { type: "incompatible"; result: T; supported: string }
+  | { type: "error"; error: TalerError };
+
+const CONFIG_FAIL_TRY_AGAIN_MS = 5000;
+
+export const ExchangeApiProvider = ({
+  baseUrl,
+  children,
+  evictors = {},
+  frameOnError,
+}: {
+  baseUrl: URL;
+  evictors?: Evictors;
+  children: ComponentChildren;
+  frameOnError: FunctionComponent<{
+    state:
+      | ConfigResultFail<TalerExchangeApi.ExchangeVersionResponse>
+      | undefined;
+  }>;
+}): VNode => {
+  const [checked, setChecked] =
+    useState<ConfigResult<TalerExchangeApi.ExchangeVersionResponse>>();
+
+  const { getRemoteConfig, VERSION, lib, cancelRequest, onActivity } =
+    buildExchangeApiClient(baseUrl, evictors);
+
+  useEffect(() => {
+    let keepRetrying = true;
+    async function testConfig(): Promise<void> {
+      try {
+        const config = await getRemoteConfig();
+        if (LibtoolVersion.compare(VERSION, config.version)) {
+          setChecked({ type: "ok", config, hints: [] });
+        } else {
+          setChecked({
+            type: "incompatible",
+            result: config,
+            supported: VERSION,
+          });
+        }
+      } catch (error) {
+        if (error instanceof TalerError) {
+          if (keepRetrying) {
+            setTimeout(() => {
+              testConfig();
+            }, CONFIG_FAIL_TRY_AGAIN_MS);
+          }
+          setChecked({ type: "error", error });
+        } else {
+          setChecked({ type: "error", error: TalerError.fromException(error) 
});
+        }
+      }
+    }
+    testConfig();
+    return () => {
+      // on unload, stop retry
+      keepRetrying = false;
+    };
+  }, []);
+
+  if (!checked || checked.type !== "ok") {
+    return h(frameOnError, { state: checked }, []);
+  }
+
+  const value: ExchangeContextType = {
+    url: baseUrl,
+    config: checked.config,
+    onActivity: onActivity,
+    lib,
+    cancelRequest,
+    hints: checked.hints,
+  };
+  return h(ExchangeContext.Provider, {
+    value,
+    children,
+  });
+};
+
+function buildExchangeApiClient(
+  url: URL,
+  evictors: Evictors,
+): APIClient<ExchangeLib, TalerExchangeApi.ExchangeVersionResponse> {
+  const httpFetch = new BrowserFetchHttpLib({
+    enableThrottling: true,
+    requireTls: false,
+  });
+  const tracker = new ActiviyTracker<ObservabilityEvent>();
+
+  const httpLib = new ObservableHttpClientLibrary(httpFetch, {
+    observe(ev) {
+      tracker.notify(ev);
+    },
+  });
+
+  const ex = new TalerExchangeHttpClient(url.href, httpLib, evictors.exchange);
+
+  async function getRemoteConfig(): 
Promise<TalerExchangeApi.ExchangeVersionResponse> {
+    const resp = await ex.getConfig();
+    if (resp.type === "fail") {
+      throw TalerError.fromUncheckedDetail(resp.detail);
+    }
+    return resp.body;
+  }
+
+  return {
+    getRemoteConfig,
+    VERSION: ex.PROTOCOL_VERSION,
+    lib: {
+      exchange: ex,
+    },
+    onActivity: tracker.subscribe,
+    cancelRequest: httpLib.cancelRequest,
+  };
+}
+
+export const ExchangeApiProviderTesting = ({
+  children,
+  value,
+}: {
+  value: ExchangeContextType;
+  children: ComponentChildren;
+}): VNode => {
+  return h(ExchangeContext.Provider, {
+    value,
+    children,
+  });
+};
diff --git a/packages/web-util/src/context/index.ts 
b/packages/web-util/src/context/index.ts
index 8e7f096da..7e30ecd09 100644
--- a/packages/web-util/src/context/index.ts
+++ b/packages/web-util/src/context/index.ts
@@ -7,5 +7,6 @@ export {
 export * from "./bank-api.js";
 export * from "./challenger-api.js";
 export * from "./merchant-api.js";
+export * from "./exchange-api.js";
 export * from "./navigation.js";
 export * from "./wallet-integration.js";
diff --git a/packages/web-util/src/forms/DefaultForm.tsx 
b/packages/web-util/src/forms/DefaultForm.tsx
index 1155401f5..118699487 100644
--- a/packages/web-util/src/forms/DefaultForm.tsx
+++ b/packages/web-util/src/forms/DefaultForm.tsx
@@ -52,7 +52,7 @@ export function DefaultForm<T extends object>({
         {form.design.map((section, i) => {
           if (!section) return <Fragment />;
           return (
-            <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3">
+            <div key={i} class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 
md:grid-cols-3">
               <div class="px-4 sm:px-0">
                 <h2 class="text-base font-semibold leading-7 text-gray-900">
                   {section.title}

-- 
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]