taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit d54351d0804f6f99b5deb1fc88cb0655029e9d9f
parent dbdc6247859f4f13f93e06f8263b30e433eaa20b
Author: Sebastian <sebasjm@gmail.com>
Date:   Sun,  4 May 2025 16:37:03 -0300

long polling function

Diffstat:
Mpackages/aml-backoffice-ui/src/pages/decision/Summary.tsx | 5++++-
Mpackages/bank-ui/src/hooks/account.ts | 108+++++++++++++++++++++++--------------------------------------------------------
Mpackages/bank-ui/src/hooks/preferences.ts | 8+++++---
Mpackages/kyc-ui/src/hooks/kyc.ts | 54+++++++++++++++++++++++++++++++++---------------------
Mpackages/taler-util/src/bank-api-client.ts | 5+++--
Mpackages/taler-util/src/http-client/bank-conversion.ts | 15++++++++-------
Mpackages/taler-util/src/http-client/bank-core.ts | 106++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpackages/taler-util/src/http-client/bank-integration.ts | 13+++++++------
Mpackages/taler-util/src/http-client/bank-revenue.ts | 5+++--
Mpackages/taler-util/src/http-client/bank-wire.ts | 15++++++++-------
Mpackages/taler-util/src/http-client/challenger.ts | 19++++++++++---------
Mpackages/taler-util/src/http-client/exchange-client.ts | 48++++++++++++++++++++++++------------------------
Mpackages/taler-util/src/http-client/exchange.ts | 102++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpackages/taler-util/src/http-client/merchant.ts | 228++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpackages/taler-util/src/operation.ts | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mpackages/web-util/src/hooks/index.ts | 1+
Apackages/web-util/src/hooks/useAsync.ts | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/web-util/src/hooks/useAsyncAsHook.ts | 3+++
18 files changed, 610 insertions(+), 389 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx b/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx @@ -150,7 +150,10 @@ export function Summary({ events: decision.triggering_events, attributes: decision.attributes?.data, properties: decision.properties!, - new_measures: decision.new_measures!.join(" "), + new_measures: + !decision.new_measures || !decision.new_measures.length + ? undefined + : decision.new_measures.join(" "), }; return lib.exchange.makeAmlDesicion(session, request); }, diff --git a/packages/bank-ui/src/hooks/account.ts b/packages/bank-ui/src/hooks/account.ts @@ -25,11 +25,17 @@ import { TalerHttpError, WithdrawalOperationStatusFlag, } from "@gnu-taler/taler-util"; -import { useEffect, useMemo, useState } from "preact/hooks"; +import { useState, useMemo } from "preact/hooks"; import { useSessionState } from "./session.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; +import { + delayMs, + preventBurst, + useAsync, + useLongPolling, + useBankCoreApiContext, +} from "@gnu-taler/web-util/browser"; import _useSWR, { mutate, SWRHook } from "swr"; import { PAGINATED_LIST_REQUEST } from "../utils.js"; const useSWR = _useSWR as unknown as SWRHook; @@ -76,90 +82,38 @@ export function revalidateWithdrawalDetails() { ); } -// FIXME: move to web-utils and reuse -function useAsyncWithRetry<Res>( - fetcher: (() => Promise<Res>) | undefined, - retry?: (res: Res | undefined, err?: TalerHttpError | undefined) => boolean, -): { result: Res | undefined; error: TalerHttpError | undefined } { - const [result, setResult] = useState<Res>(); - const [error, setError] = useState<TalerHttpError>(); - const [retryCounter, setRetryCounter] = useState(0); - - useEffect(() => { - let unloaded = false; - if (fetcher) { - fetcher() - .then((resp) => { - if (unloaded) return; - setResult(resp); - }) - .catch((error: TalerHttpError) => { - if (unloaded) return; - setError(error); - }); - } - - return () => { - unloaded = true; - }; - }, [fetcher, retryCounter]); - - // retry on result or error - useEffect(() => { - if (retry && retry(result, error)) { - setRetryCounter((c) => c + 1); - } - }, [result, error]); - - return { result, error }; -} - export function useWithdrawalDetails(wid: string | undefined) { const { lib: { bank: api }, } = useBankCoreApiContext(); - const [latestStatus, setLatestStatus] = - useState<WithdrawalOperationStatusFlag>(); - - const fetcher = useMemo(() => { - return wid === undefined + const prev = useAsync( + wid === undefined ? undefined : () => { - return api.getWithdrawalById( - wid, - latestStatus === undefined - ? undefined - : { old_state: latestStatus, timeoutMs: 5000 }, - ); - }; - }, [wid, latestStatus]); - - const { result: data, error } = useAsyncWithRetry(fetcher, (r, err) => { - // retry if error - let retry = err !== undefined; - // retry if still pending - retry = - r !== undefined && - r.type === "ok" && - (r.body.status === "pending" || r.body.status === "selected"); - return retry; - }); - - const currentStatus = - data !== undefined && data.type === "ok" ? data.body.status : undefined; + return api.getWithdrawalById(wid, undefined); + }, + ); - useEffect(() => { - if (currentStatus !== undefined && currentStatus !== latestStatus) { - // withdrawal has a new state, save the current - // and make the query again - setLatestStatus(currentStatus); - } - }, [currentStatus]); + const result = useLongPolling( + prev, + (result) => { + if (!result || result.type === "fail") return undefined; + const { status } = result.body; + return status === "pending" || status === "selected" + ? result.body + : undefined; + }, + (latestStatus: TalerCorebankApi.WithdrawalPublicInfo) => { + return api.getWithdrawalById(wid!, { + old_state: latestStatus.status, + timeoutMs: 5000, + }); + }, + [wid], + ); - if (data) return data; - if (error) return error; - return undefined; + return result; } export function revalidateTransactionDetails() { diff --git a/packages/bank-ui/src/hooks/preferences.ts b/packages/bank-ui/src/hooks/preferences.ts @@ -26,6 +26,7 @@ import { useTranslationContext, } from "@gnu-taler/web-util/browser"; import { UiSettings } from "../settings.js"; +import { codecOptionalDefault } from "@gnu-taler/taler-util"; const TALER_SCREEN_ID = 102; @@ -42,7 +43,7 @@ interface Preferences { export const codecForPreferences = (): Codec<Preferences> => buildCodecForObject<Preferences>() .property("showWithdrawalSuccess", codecForBoolean()) - .property("hideDemo", codecForBoolean()) + .property("hideDemo", codecOptionalDefault(codecForBoolean(), false)) .property("showInstallWallet", codecForBoolean()) .property("showDebugInfo", codecForBoolean()) .property("allowsSimplePassword", codecForBoolean()) @@ -85,7 +86,9 @@ export function usePreferences(): [ return [value, updateField]; } -export function getAllBooleanPreferences(settings: UiSettings): Array<keyof Preferences> { +export function getAllBooleanPreferences( + settings: UiSettings, +): Array<keyof Preferences> { if (settings.showDemoDescription) { return [ "showDebugInfo", @@ -103,7 +106,6 @@ export function getAllBooleanPreferences(settings: UiSettings): Array<keyof Pref "fastWithdrawalForm", "showCopyAccount", ]; - } export function getLabelForPreferences( diff --git a/packages/kyc-ui/src/hooks/kyc.ts b/packages/kyc-ui/src/hooks/kyc.ts @@ -15,10 +15,13 @@ */ import { AccessToken, - TalerExchangeResultByMethod, - TalerHttpError, + KycProcessClientInformationWithEtag, } from "@gnu-taler/taler-util"; -import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; +import { + useAsync, + useExchangeApiContext, + useLongPolling, +} from "@gnu-taler/web-util/browser"; import _useSWR, { SWRHook, mutate } from "swr"; const useSWR = _useSWR as unknown as SWRHook; @@ -30,26 +33,35 @@ export function revalidateKycInfo() { ); } -export function useKycInfo(token: AccessToken) { +export function useKycInfo(token?: AccessToken) { const { lib: { exchange: api }, } = useExchangeApiContext(); - async function fetcher([ac]: [AccessToken]) { - return await api.checkKycInfo(ac, []); - } - const { data, error } = useSWR< - TalerExchangeResultByMethod<"checkKycInfo">, - TalerHttpError - >([token, "checkKycInfo"], fetcher, { - revalidateIfStale: false, - errorRetryCount: 0, - errorRetryInterval: 1, - shouldRetryOnError: false, - keepPreviousData: true, - }); - - if (data) return data; - if (error) return error; - return undefined; + const prev = useAsync( + token === undefined + ? undefined + : () => { + return api.checkKycInfo(token, undefined); + }, + [token], + ); + + const result = useLongPolling( + prev, + (result) => { + if (!result || result.type === "fail") return undefined; + if (!result.body.requirements.length) return undefined; + return result.body; + }, + (latestStatus: KycProcessClientInformationWithEtag) => { + // + return api.checkKycInfo(token!, latestStatus.etag, { + timeoutMs: 5000 + }); + }, + [token], + ); + + return result; } diff --git a/packages/taler-util/src/bank-api-client.ts b/packages/taler-util/src/bank-api-client.ts @@ -36,6 +36,7 @@ import { opEmptySuccess, opKnownHttpFailure, opUnknownFailure, + opUnknownHttpFailure, PaytoString, stringToBytes, TalerCorebankApi, @@ -301,11 +302,11 @@ export class TalerCorebankApiClient { switch (resp.status) { case HttpStatusCode.Ok: case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts @@ -29,6 +29,7 @@ import { opKnownHttpFailure, opSuccessFromHttp, opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { @@ -94,7 +95,7 @@ export class TalerBankConversionHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -130,7 +131,7 @@ export class TalerBankConversionHttpClient { case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, body); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.Conflict: @@ -138,7 +139,7 @@ export class TalerBankConversionHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -177,7 +178,7 @@ export class TalerBankConversionHttpClient { case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, body); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.Conflict: @@ -185,7 +186,7 @@ export class TalerBankConversionHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -207,14 +208,14 @@ export class TalerBankConversionHttpClient { this.cacheEvictor.notifySuccess( TalerBankConversionCacheEviction.UPDATE_RATE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts @@ -32,7 +32,7 @@ import { codecForTalerCommonConfigResponse, codecForTokenInfoList, codecForTokenSuccessResponse, - opKnownAlternativeFailure, + opKnownAlternativeHttpFailure, opKnownHttpFailure, opKnownTalerFailure, } from "@gnu-taler/taler-util"; @@ -48,7 +48,7 @@ import { opEmptySuccess, opFixedSuccess, opSuccessFromHttp, - opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { WithdrawalOperationStatusFlag } from "../types-taler-bank-integration.js"; import { @@ -150,7 +150,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForTokenSuccessResponse()); case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), @@ -165,13 +165,13 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_ACCOUNT_LOCKED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -188,15 +188,15 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: - return opEmptySuccess(resp); + return opEmptySuccess(); // FIXME: missing in docs case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); // FIXME: missing in docs case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -218,7 +218,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opFixedSuccess({ public_accounts: [] }); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -268,7 +268,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -346,11 +346,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_PASSWORD_TOO_LONG: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } /** @@ -371,13 +371,13 @@ export class TalerCoreBankHttpClient { await this.cacheEvictor.notifySuccess( TalerCoreBankCacheEviction.DELETE_ACCOUNT, ); - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -390,11 +390,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -418,7 +418,7 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), @@ -427,7 +427,7 @@ export class TalerCoreBankHttpClient { await this.cacheEvictor.notifySuccess( TalerCoreBankCacheEviction.UPDATE_ACCOUNT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -452,11 +452,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_PASSWORD_TOO_LONG: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -480,13 +480,13 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: @@ -505,11 +505,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_PASSWORD_TOO_LONG: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -537,7 +537,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opFixedSuccess({ public_accounts: [] }); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -569,7 +569,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -593,7 +593,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -631,7 +631,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -658,7 +658,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -699,7 +699,7 @@ export class TalerCoreBankHttpClient { ); return opSuccessFromHttp(resp, codecForCreateTransactionResponse()); case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), @@ -724,11 +724,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -769,7 +769,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -797,7 +797,7 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), @@ -806,7 +806,7 @@ export class TalerCoreBankHttpClient { await this.cacheEvictor.notifySuccess( TalerCoreBankCacheEviction.CONFIRM_WITHDRAWAL, ); - return opEmptySuccess(resp); + return opEmptySuccess(); //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); @@ -826,11 +826,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_AMOUNT_REQUIRED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -854,7 +854,7 @@ export class TalerCoreBankHttpClient { await this.cacheEvictor.notifySuccess( TalerCoreBankCacheEviction.ABORT_WITHDRAWAL, ); - return opEmptySuccess(resp); + return opEmptySuccess(); //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); @@ -863,7 +863,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -897,7 +897,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -930,7 +930,7 @@ export class TalerCoreBankHttpClient { ); return opSuccessFromHttp(resp, codecForCashoutPending()); case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForChallenge(), @@ -951,7 +951,7 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.BadGateway: { @@ -960,7 +960,7 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.NotImplemented: @@ -972,7 +972,7 @@ export class TalerCoreBankHttpClient { return opKnownHttpFailure(resp.status, resp); } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -999,7 +999,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1024,7 +1024,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1049,7 +1049,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1112,11 +1112,11 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1171,7 +1171,7 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1184,13 +1184,13 @@ export class TalerCoreBankHttpClient { case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } case HttpStatusCode.TooManyRequests: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1236,7 +1236,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts @@ -29,6 +29,7 @@ import { opKnownTalerFailure, opSuccessFromHttp, opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { @@ -88,7 +89,7 @@ export class TalerBankIntegrationHttpClient { return opSuccessFromHttp(resp, codecForIntegrationBankConfig()); default: logger.warn(`config request failed, status ${resp.status}`); - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -122,7 +123,7 @@ export class TalerBankIntegrationHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -165,11 +166,11 @@ export class TalerBankIntegrationHttpClient { case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, details); default: - return opUnknownFailure(resp, details); + return opUnknownHttpFailure(resp, details); } } default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -184,13 +185,13 @@ export class TalerBankIntegrationHttpClient { }); switch (resp.status) { case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts @@ -29,6 +29,7 @@ import { opKnownHttpFailure, opSuccessFromHttp, opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { AccessToken, @@ -95,7 +96,7 @@ export class TalerRevenueHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } /** @@ -132,7 +133,7 @@ export class TalerRevenueHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts @@ -28,6 +28,7 @@ import { opKnownHttpFailure, opSuccessFromHttp, opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { codecForAddIncomingResponse, @@ -105,7 +106,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -153,7 +154,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -184,7 +185,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -226,7 +227,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -268,7 +269,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -305,7 +306,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -340,7 +341,7 @@ export class TalerWireGatewayHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/http-client/challenger.ts b/packages/taler-util/src/http-client/challenger.ts @@ -26,10 +26,11 @@ import { LibtoolVersion } from "../libtool-version.js"; import { FailCasesByMethod, ResultByMethod, - opKnownAlternativeFailure, + opKnownAlternativeHttpFailure, opKnownHttpFailure, opSuccessFromHttp, opUnknownFailure, + opUnknownHttpFailure, } from "../operation.js"; import { codecForChallengeInvalidPinResponse, @@ -96,7 +97,7 @@ export class ChallengerHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } /** @@ -118,7 +119,7 @@ export class ChallengerHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -158,7 +159,7 @@ export class ChallengerHttpClient { case HttpStatusCode.InternalServerError: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -193,7 +194,7 @@ export class ChallengerHttpClient { case HttpStatusCode.InternalServerError: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -223,7 +224,7 @@ export class ChallengerHttpClient { case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Forbidden: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, HttpStatusCode.Forbidden, codecForChallengeInvalidPinResponse(), @@ -237,7 +238,7 @@ export class ChallengerHttpClient { case HttpStatusCode.InternalServerError: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -277,7 +278,7 @@ export class ChallengerHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -303,7 +304,7 @@ export class ChallengerHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts @@ -19,8 +19,7 @@ import { HttpRequestLibrary, HttpRequestOptions, HttpResponse, - readSuccessResponseJsonOrThrow, - readTalerErrorResponse, + readSuccessResponseJsonOrThrow } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; @@ -33,11 +32,10 @@ import { ResultByMethod, opEmptySuccess, opFixedSuccess, - opKnownAlternativeFailure, + opKnownAlternativeHttpFailure, opKnownHttpFailure, opSuccessFromHttp, - opUnknownFailure, - opUnknownHttpFailure, + opUnknownHttpFailure } from "../operation.js"; import { EddsaPrivP, encodeCrock } from "../taler-crypto.js"; import { @@ -438,7 +436,7 @@ export class TalerExchangeHttpClient2 { case HttpStatusCode.Gone: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), @@ -649,7 +647,7 @@ export class TalerExchangeHttpClient2 { // FIXME: parse PurseCreateSuccessResponse return opSuccessFromHttp(resp, codecForAny()); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflict(), @@ -684,7 +682,7 @@ export class TalerExchangeHttpClient2 { }); switch (resp.status) { case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: @@ -726,13 +724,13 @@ export class TalerExchangeHttpClient2 { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForExchangeMergeSuccessResponse()); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), ); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForExchangeMergeConflictResponse(), @@ -777,13 +775,13 @@ export class TalerExchangeHttpClient2 { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflictPartial(), ); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), @@ -837,7 +835,7 @@ export class TalerExchangeHttpClient2 { // FIXME: parse PurseDepositSuccessResponse return opSuccessFromHttp(resp, codecForAny()); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflict(), @@ -886,17 +884,17 @@ export class TalerExchangeHttpClient2 { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForAmlWalletKycCheckResponse()); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -942,13 +940,13 @@ export class TalerExchangeHttpClient2 { switch (resp.status) { case HttpStatusCode.Ok: case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForAccountKycStatus(), ); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: case HttpStatusCode.Conflict: @@ -982,7 +980,7 @@ export class TalerExchangeHttpClient2 { return opSuccessFromHttp(resp, codecForKycProcessClientInformation()); case HttpStatusCode.Accepted: case HttpStatusCode.NoContent: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForEmptyObject(), @@ -990,7 +988,7 @@ export class TalerExchangeHttpClient2 { case HttpStatusCode.NotModified: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1012,7 +1010,7 @@ export class TalerExchangeHttpClient2 { this.cacheEvictor.notifySuccess( TalerExchangeCacheEviction2.UPLOAD_KYC_FORM, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.NotFound: case HttpStatusCode.Conflict: @@ -1066,7 +1064,7 @@ export class TalerExchangeHttpClient2 { switch (resp.status) { case HttpStatusCode.SeeOther: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: @@ -1238,7 +1236,9 @@ export class TalerExchangeHttpClient2 { decision: Omit<AmlDecisionRequest, "officer_sig">, ) { const body: AmlDecisionRequest = { - officer_sig: encodeCrock(signAmlDecision(auth.signingKey, decision)) as any, + officer_sig: encodeCrock( + signAmlDecision(auth.signingKey, decision), + ) as any, ...decision, }; const resp = await this.fetch(`aml/${auth.id}/decision`, { @@ -1257,7 +1257,7 @@ export class TalerExchangeHttpClient2 { this.cacheEvictor.notifySuccess( TalerExchangeCacheEviction2.MAKE_AML_DECISION, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts @@ -18,7 +18,6 @@ import { Codec, codecForAny } from "../codec.js"; import { HttpRequestLibrary, readSuccessResponseJsonOrThrow, - readTalerErrorResponse } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; @@ -30,21 +29,22 @@ import { ResultByMethod, opEmptySuccess, opFixedSuccess, - opKnownAlternativeFailure, + opKnownAlternativeHttpFailure, + opKnownFailure, opKnownHttpFailure, opSuccessFromHttp, - opUnknownFailure, - opUnknownHttpFailure + opUnknownHttpFailure, } from "../operation.js"; import { EddsaPrivP, encodeCrock } from "../taler-crypto.js"; import { AccessToken, EddsaPublicKeyString, EddsaSignatureString, + LongPollParams, OfficerAccount, PaginationParams, PaytoHash, - codecForTalerCommonConfigResponse + codecForTalerCommonConfigResponse, } from "../types-taler-common.js"; import { AmlDecisionRequest, @@ -55,6 +55,7 @@ import { ExchangePurseMergeRequest, ExchangeReservePurseRequest, ExchangeVersionResponse, + KycProcessClientInformation, KycRequirementInformationId, PurseCreate, WalletKycRequest, @@ -75,9 +76,14 @@ import { codecForKycProcessStartInformation, codecForLegitimizationNeededResponse, codecForPurseConflict, - codecForPurseConflictPartial + codecForPurseConflictPartial, } from "../types-taler-exchange.js"; -import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js"; +import { + CacheEvictor, + addLongPollingParam, + addPaginationParams, + nullEvictor, +} from "./utils.js"; import { TalerError } from "../errors.js"; import { @@ -87,7 +93,7 @@ import { LongpollQueue, signAmlDecision, signAmlQuery, - signKycAuth + signKycAuth, } from "../index.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { AbsoluteTime } from "../time.js"; @@ -108,6 +114,9 @@ export enum TalerExchangeCacheEviction { declare const __pubId: unique symbol; export type ReservePub = string & { [__pubId]: true }; +export type KycProcessClientInformationWithEtag = + KycProcessClientInformation & { etag: string | undefined }; + /** * Client library for the GNU Taler exchange service. */ @@ -438,7 +447,7 @@ export class TalerExchangeHttpClient { case HttpStatusCode.Gone: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), @@ -636,7 +645,7 @@ export class TalerExchangeHttpClient { // FIXME: parse PurseCreateSuccessResponse return opSuccessFromHttp(resp, codecForAny()); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflict(), @@ -664,7 +673,7 @@ export class TalerExchangeHttpClient { }); switch (resp.status) { case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: @@ -691,13 +700,13 @@ export class TalerExchangeHttpClient { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForExchangeMergeSuccessResponse()); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), ); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForExchangeMergeConflictResponse(), @@ -734,13 +743,13 @@ export class TalerExchangeHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflictPartial(), ); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), @@ -789,7 +798,7 @@ export class TalerExchangeHttpClient { // FIXME: parse PurseDepositSuccessResponse return opSuccessFromHttp(resp, codecForAny()); case HttpStatusCode.Conflict: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPurseConflict(), @@ -822,13 +831,6 @@ export class TalerExchangeHttpClient { * */ async notifyKycBalanceLimit(body: WalletKycRequest) { - // const body: WalletKycRequest = { - // balance, - // reserve_pub: account.id, - // reserve_sig: encodeCrock( - // signWalletAccountSetup(account.signingKey, balance), - // ), - // }; const url = new URL(`kyc-wallet`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { @@ -840,17 +842,17 @@ export class TalerExchangeHttpClient { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForAmlWalletKycCheckResponse()); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForLegitimizationNeededResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -885,13 +887,13 @@ export class TalerExchangeHttpClient { switch (resp.status) { case HttpStatusCode.Ok: case HttpStatusCode.Accepted: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForAccountKycStatus(), ); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: case HttpStatusCode.Conflict: @@ -905,29 +907,45 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN * */ - async checkKycInfo(token: AccessToken, known: KycRequirementInformationId[]) { + async checkKycInfo( + token: AccessToken, + etag: string | undefined, + params: LongPollParams = {}, + ) { const url = new URL(`kyc-info/${token}`, this.baseUrl); + addLongPollingParam(url, params); + const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "If-None-Match": known.length ? known.join(",") : undefined, + "If-None-Match": etag, }, }); switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForKycProcessClientInformation()); - case HttpStatusCode.Accepted: - case HttpStatusCode.NoContent: - return opKnownAlternativeFailure( + case HttpStatusCode.Ok: { + // we need to add the etag to the response because the + // client needs it to repeat the request and + // do the long polling + const etag = resp.headers.get("etag") ?? undefined; + const body = await readSuccessResponseJsonOrThrow( resp, - resp.status, - codecForEmptyObject(), + codecForKycProcessClientInformation(), ); - case HttpStatusCode.NotModified: + return opFixedSuccess<KycProcessClientInformationWithEtag>({ + ...body, + etag, + }); + } + case HttpStatusCode.Accepted: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NoContent: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotModified: + // do not read details from response + return opKnownFailure(resp.status); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -951,7 +969,7 @@ export class TalerExchangeHttpClient { this.cacheEvictor.notifySuccess( TalerExchangeCacheEviction.UPLOAD_KYC_FORM, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.NotFound: case HttpStatusCode.Conflict: @@ -1007,7 +1025,7 @@ export class TalerExchangeHttpClient { switch (resp.status) { case HttpStatusCode.SeeOther: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: @@ -1203,7 +1221,7 @@ export class TalerExchangeHttpClient { this.cacheEvictor.notifySuccess( TalerExchangeCacheEviction.MAKE_AML_DECISION, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -70,17 +70,17 @@ import { codecForWebhookSummaryResponse, opEmptySuccess, opFixedSuccess, - opKnownAlternativeFailure, + opKnownAlternativeHttpFailure, opKnownHttpFailure, + opUnknownHttpFailure, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, HttpResponse, createPlatformHttpLib, - readSuccessResponseJsonOrThrow, - readTalerErrorResponse, + readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; -import { opSuccessFromHttp, opUnknownFailure } from "../operation.js"; +import { opSuccessFromHttp } from "../operation.js"; import { CacheEvictor, addPaginationParams, @@ -209,7 +209,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -251,7 +251,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -282,7 +282,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -325,13 +325,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.GatewayTimeout: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPaymentDeniedLegallyResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -393,7 +393,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotAcceptable: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -422,7 +422,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -454,7 +454,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -486,13 +486,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPaymentDeniedLegallyResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -521,15 +521,15 @@ export class TalerMerchantInstanceHttpClient { switch (resp.status) { case HttpStatusCode.Ok: // FIXME: missing in docs - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -556,14 +556,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_CURRENT_INSTANCE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -591,7 +591,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -622,7 +622,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_CURRENT_INSTANCE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); @@ -631,7 +631,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -683,13 +683,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Accepted: return opSuccessFromHttp(resp, codecForAccountKycRedirects()); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.BadGateway: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForAccountKycRedirects(), @@ -699,7 +699,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.GatewayTimeout: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -740,7 +740,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -768,14 +768,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_BANK_ACCOUNT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -807,7 +807,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -837,7 +837,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -861,14 +861,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_BANK_ACCOUNT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -904,7 +904,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -931,7 +931,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -959,7 +959,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_CATEGORY, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -968,7 +968,7 @@ export class TalerMerchantInstanceHttpClient { // case HttpStatusCode.Conflict: // return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -997,7 +997,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_CATEGORY, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1006,7 +1006,7 @@ export class TalerMerchantInstanceHttpClient { // case HttpStatusCode.Conflict: // return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1030,7 +1030,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_CATEGORY, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1039,7 +1039,7 @@ export class TalerMerchantInstanceHttpClient { // case HttpStatusCode.Conflict: // return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1067,7 +1067,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_PRODUCT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1076,7 +1076,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1105,7 +1105,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1114,7 +1114,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1146,7 +1146,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1171,7 +1171,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1198,7 +1198,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1227,7 +1227,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1236,7 +1236,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Gone: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1260,7 +1260,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_PRODUCT, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1269,7 +1269,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1311,7 +1311,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPaymentDeniedLegallyResponse(), @@ -1319,13 +1319,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Gone: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForOutOfStockResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1378,7 +1378,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1437,13 +1437,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.BadGateway: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.GatewayTimeout: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForOutOfStockResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1472,10 +1472,10 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_ORDER, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.BadRequest: @@ -1485,7 +1485,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1516,7 +1516,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_ORDER, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1525,7 +1525,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1571,13 +1571,13 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure( + return opKnownAlternativeHttpFailure( resp, resp.status, codecForPaymentDeniedLegallyResponse(), ); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1609,7 +1609,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_TRANSFER, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1618,7 +1618,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1662,7 +1662,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1686,7 +1686,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_TRANSFER, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1695,7 +1695,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1727,14 +1727,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_DEVICE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1762,7 +1762,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_DEVICE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1771,7 +1771,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1802,7 +1802,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1839,7 +1839,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1862,14 +1862,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_DEVICE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1900,14 +1900,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_TEMPLATE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1935,7 +1935,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_TEMPLATE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -1944,7 +1944,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -1975,7 +1975,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2001,7 +2001,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2024,14 +2024,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_TEMPLATE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2050,7 +2050,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2099,14 +2099,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_WEBHOOK, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2135,7 +2135,7 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.UPDATE_WEBHOOK, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -2144,7 +2144,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2174,7 +2174,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2200,7 +2200,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2223,14 +2223,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_WEBHOOK, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2262,14 +2262,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.CREATE_TOKENFAMILY, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2304,7 +2304,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2334,7 +2334,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2364,7 +2364,7 @@ export class TalerMerchantInstanceHttpClient { case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2387,14 +2387,14 @@ export class TalerMerchantInstanceHttpClient { this.cacheEvictor.notifySuccess( TalerMerchantInstanceCacheEviction.DELETE_TOKENFAMILY, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2464,14 +2464,14 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp this.cacheManagementEvictor.notifySuccess( TalerMerchantManagementCacheEviction.CREATE_INSTANCE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2500,13 +2500,13 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp switch (resp.status) { case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2534,14 +2534,14 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp this.cacheManagementEvictor.notifySuccess( TalerMerchantManagementCacheEviction.UPDATE_INSTANCE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2569,7 +2569,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2597,7 +2597,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp case HttpStatusCode.NotFound: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2628,7 +2628,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp this.cacheManagementEvictor.notifySuccess( TalerMerchantManagementCacheEviction.DELETE_INSTANCE, ); - return opEmptySuccess(resp); + return opEmptySuccess(); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); @@ -2637,7 +2637,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } @@ -2673,9 +2673,9 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp case HttpStatusCode.Accepted: return opSuccessFromHttp(resp, codecForAccountKycRedirects()); case HttpStatusCode.NoContent: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.NotFound: - return opEmptySuccess(resp); + return opEmptySuccess(); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.BadGateway: @@ -2685,7 +2685,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: - return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + return opUnknownHttpFailure(resp); } } } diff --git a/packages/taler-util/src/operation.ts b/packages/taler-util/src/operation.ts @@ -107,7 +107,7 @@ export function opFixedSuccess<T>(body: T): OperationOk<T> { return { type: "ok" as const, case: "ok", body }; } -export function opEmptySuccess(resp: HttpResponse): OperationOk<void> { +export function opEmptySuccess(): OperationOk<void> { return { type: "ok" as const, case: "ok", body: void 0 }; } @@ -122,7 +122,14 @@ export function opKnownFailureWithBody<T, B>( return { type: "fail", case: case_, body }; } -export async function opKnownAlternativeFailure<T extends HttpStatusCode, B>( +/** + * + * @param resp + * @param s + * @param codec + * @returns + */ +export async function opKnownAlternativeHttpFailure<T extends HttpStatusCode, B>( resp: HttpResponse, s: T, codec: Codec<B>, @@ -131,44 +138,73 @@ export async function opKnownAlternativeFailure<T extends HttpStatusCode, B>( return { type: "fail", case: s, body }; } +/** + * Constructor of a failure response of the API that is already documented in the spec. + * The `case` parameter is a reason of the error. + * + * @param case + * @param resp + * @returns + */ export async function opKnownHttpFailure<T extends HttpStatusCode>( - s: T, + _case: T, resp: HttpResponse, + detail?: TalerErrorDetail, ): Promise<OperationFail<T>> { - const detail = await readTalerErrorResponse(resp); - return { type: "fail", case: s, detail }; + if (!detail) { + detail = await readTalerErrorResponse(resp); + } + return { type: "fail", case: _case, detail }; } +/** + * Constructor of an unexpected error, usually when the response of the API + * is not in the spec. + * + * If the response hasn't already been read, this function will add the information + * as detail + * + * @param resp + * @param detail + */ export async function opUnknownHttpFailure( resp: HttpResponse, + detail?: TalerErrorDetail, ): Promise<never> { - const detail = await readTalerErrorResponse(resp); - return opUnknownFailure(resp, detail) -} - -export function opKnownTalerFailure<T extends TalerErrorCode>( - s: T, - detail: TalerErrorDetail, -): OperationFail<T> { - return { type: "fail", case: s, detail }; -} - -export function opUnknownFailure( - resp: HttpResponse, - error: TalerErrorDetail, -): never { + if (!detail) { + detail = await readTalerErrorResponse(resp); + } throw TalerError.fromDetail( TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, { requestUrl: resp.requestUrl, requestMethod: resp.requestMethod, httpStatusCode: resp.status, - errorResponse: error, + errorResponse: detail, }, `Unexpected HTTP status ${resp.status} in response`, ); } +/** + * Constructor of a failure response of the API that is already documented in the spec. + * The `case` parameter is a reason of the error. + * + * @param case + * @param resp + * @returns + */ +export function opKnownTalerFailure<T extends TalerErrorCode>( + _case: T, + detail: TalerErrorDetail, +): OperationFail<T> { + return { type: "fail", case: _case, detail }; +} + +export function opUnknownFailure(error: unknown): never { + throw TalerError.fromException(error); +} + export function succeedOrThrow<R>(resp: OperationResult<R, unknown>): R { if (isOperationOk(resp)) { return resp.body; diff --git a/packages/web-util/src/hooks/index.ts b/packages/web-util/src/hooks/index.ts @@ -23,3 +23,4 @@ export { HookGenericError, HookOperationalError, } from "./useAsyncAsHook.js"; +export * from "./useAsync.js"; diff --git a/packages/web-util/src/hooks/useAsync.ts b/packages/web-util/src/hooks/useAsync.ts @@ -0,0 +1,186 @@ +/* + This file is part of GNU Taler + (C) 2025 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 { useEffect, useState } from "preact/hooks"; +import { opFixedSuccess, opUnknownFailure } from "@gnu-taler/taler-util"; + +/** + * convert the async function into preact hook + * + * @param callback the async function + * + * @returns operation status + */ +export function useAsync<Res>( + callback: (() => Promise<Res>) | undefined, + deps: Array<any> = [], +) { + const [data, setData] = useState<Res>(); + const [error, setError] = useState<unknown>(); + + useEffect(() => { + let unloaded = false; + if (callback) { + callback() + .then((resp) => { + if (unloaded) return; + setData(resp); + }) + .catch((error: unknown) => { + if (unloaded) return; + setError(error); + }); + } + return () => { + unloaded = true; + }; + }, deps); + + if (error) return opUnknownFailure(error); + if (!data) return undefined; + return data; +} + +/** + * First start with `first` value + * Check if the it should do long-polling with `shouldRetryFn` + * Call `retryFn` if is required, long poll is expected for this function + * + * If `retryFn` returns faster than `minTime` (by error or because server + * returned a response faster) then wait until `minTime` + * + * @param fetcher fetcher should be a memoized function + * @param retry + * @param deps + * @returns + */ +export function useLongPolling<Res, Rt>( + first: Res, + shouldRetryFn: (res: Res, count: number) => Rt | undefined, + retryFn: (last: Rt) => Promise<Res>, + deps: Array<any> = [], + opts: { minTime?: number } = {}, +) { + const mt = opts?.minTime ?? 1000; // do not try faster than this + + const [retry, setRetry] = useState<{ + count: number; + fn: (() => Promise<Res>) | undefined; + startMs: number; + }>({ + count: 0, + fn: undefined, + startMs: new Date().getTime(), + }); + + const result = useAsync(retry.fn, [retry.count, ...deps]); + + const body = result ?? first; + + useEffect(() => { + if (!body) return; + const _body = body; + + function doChceck() { + const rt = shouldRetryFn(_body, retry.count); + if (!rt) return; + // call again + setRetry((lt) => ({ + count: lt.count + 1, + fn: () => retryFn(rt), + startMs: new Date().getTime(), + })); + } + + const diff = new Date().getTime() - retry.startMs; + if (diff < mt) { + // calling too fast, wait + delayMs(mt - diff).then(doChceck); + } else { + doChceck(); + } + }, [body]); + + return body; +} + +/** + * this should be in taler-utils + * @param ms + * @returns + */ +export async function delayMs(ms: number): Promise<void> { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(), ms); + }); +} + +/** + * If the function `fn` is called faster than `minTime` wait + * before calling again. + * + * Helps to prevent burst calling when the server does not implement + * long polling correctly. + * + * @returns a wrapper of `fn` function + */ +export function preventBurst<type, args>( + fn: (...args: args[]) => Promise<type>, + opts: { + minTimeMs?: number; + errorTimeMs?: number; + } = {}, +): typeof fn { + const mt = opts.minTimeMs ?? 3000; + const et = opts.errorTimeMs ?? mt; + + let nextCallShouldWait = 0; + console.log("nooo"); + return async (...args): Promise<type> => { + const start = new Date().getTime(); + console.log("que onda", nextCallShouldWait); + if (nextCallShouldWait > 0) { + console.log("waiting", nextCallShouldWait); + await delayMs(nextCallShouldWait); + nextCallShouldWait = 0; + } + const r = fn(...args) + .then((result) => { + const diff = new Date().getTime() - start; + nextCallShouldWait = Math.max(0, mt - diff); + console.log( + "next call", + nextCallShouldWait, + diff, + mt, + Math.max(0, mt - diff), + ); + return result; + }) + .catch((error) => { + const diff = new Date().getTime() - start; + console.log( + "error call", + nextCallShouldWait, + diff, + et, + Math.max(0, et - diff), + ); + nextCallShouldWait = Math.max(0, et - diff); + throw error; + }); + return r; + }; +} diff --git a/packages/web-util/src/hooks/useAsyncAsHook.ts b/packages/web-util/src/hooks/useAsyncAsHook.ts @@ -45,6 +45,9 @@ export type HookResponseWithRetry<T> = | ((HookOk<T> | HookError) & WithRetry) | undefined; + /** + * @deprecated use useAsyncWithRetry + */ export function useAsyncAsHook<T>( fn: () => Promise<T | false>, deps?: any[],