From 70151490bd79d38f8064b720fc124d1774a18235 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 5 Apr 2024 10:30:19 -0300 Subject: fix listing, add cache invalidation, fix codec for accounts --- packages/bank-ui/src/hooks/account.ts | 11 +- packages/bank-ui/src/hooks/regional.ts | 8 +- packages/bank-ui/src/utils.ts | 6 +- .../merchant-backoffice-ui/src/Application.tsx | 192 ++++++++++++++++++++- packages/merchant-backoffice-ui/src/Routing.tsx | 59 +------ packages/merchant-backoffice-ui/src/hooks/bank.ts | 3 +- .../merchant-backoffice-ui/src/hooks/order.test.ts | 2 +- packages/merchant-backoffice-ui/src/hooks/order.ts | 11 +- packages/merchant-backoffice-ui/src/hooks/otp.ts | 4 +- .../merchant-backoffice-ui/src/hooks/product.ts | 4 +- .../merchant-backoffice-ui/src/hooks/templates.ts | 6 +- .../src/hooks/transfer.test.ts | 4 +- .../merchant-backoffice-ui/src/hooks/transfer.ts | 4 +- .../merchant-backoffice-ui/src/hooks/webhooks.ts | 8 +- .../src/paths/instance/orders/list/Table.tsx | 12 +- .../paths/instance/otp_devices/list/ListPage.tsx | 2 - .../src/paths/instance/otp_devices/list/Table.tsx | 18 +- .../src/paths/instance/products/list/Table.tsx | 6 - .../src/paths/instance/templates/list/ListPage.tsx | 2 - .../src/paths/instance/templates/list/Table.tsx | 18 +- .../paths/instance/templates/update/UpdatePage.tsx | 4 +- .../src/paths/instance/templates/update/index.tsx | 2 +- .../src/paths/instance/transfers/list/ListPage.tsx | 2 - .../src/paths/instance/transfers/list/Table.tsx | 20 +-- .../src/paths/instance/webhooks/list/ListPage.tsx | 2 - .../src/paths/instance/webhooks/list/Table.tsx | 18 +- .../merchant-backoffice-ui/src/utils/constants.ts | 9 +- .../bin/create_merchantAndBankAccount_pdf.sh | 13 ++ packages/taler-util/src/http-client/merchant.ts | 173 ++++++++++++++----- packages/taler-util/src/http-client/types.ts | 4 +- packages/taler-util/src/time.ts | 6 + packages/web-util/src/context/merchant-api.ts | 5 +- 32 files changed, 417 insertions(+), 221 deletions(-) diff --git a/packages/bank-ui/src/hooks/account.ts b/packages/bank-ui/src/hooks/account.ts index e1cd423de..43d43a3f2 100644 --- a/packages/bank-ui/src/hooks/account.ts +++ b/packages/bank-ui/src/hooks/account.ts @@ -22,12 +22,12 @@ import { WithdrawalOperationStatus, } from "@gnu-taler/taler-util"; import { useEffect, useState } from "preact/hooks"; -import { PAGE_SIZE } from "../utils.js"; import { useSessionState } from "./session.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import _useSWR, { SWRHook, mutate } from "swr"; import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; +import { PAGINATED_LIST_REQUEST } from "../utils.js"; const useSWR = _useSWR as unknown as SWRHook; export interface InstanceTemplateFilter { @@ -184,7 +184,7 @@ export function usePublicAccounts( return await api.getPublicAccounts( { account }, { - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: txid ? String(txid) : undefined, order: "asc", }, @@ -232,11 +232,12 @@ export function buildPaginatedResult( setOffset: (o: OffsetId | undefined) => void, getId: (r: DataType) => OffsetId, ): PaginatedResult { - const isLastPage = data.length <= PAGE_SIZE; + const isLastPage = data.length < PAGINATED_LIST_REQUEST; const isFirstPage = offset === undefined; const result = structuredClone(data); - if (result.length == PAGE_SIZE + 1) { + if (result.length == PAGINATED_LIST_REQUEST) { + //do now show the last element, used to know if this is the last page result.pop(); } return { @@ -280,7 +281,7 @@ export function useTransactions(account: string, initial?: number) { return await api.getTransactions( { username, token }, { - limit: PAGE_SIZE + 1, + limit: PAGINATED_LIST_REQUEST, offset: txid ? String(txid) : undefined, order: "dec", }, diff --git a/packages/bank-ui/src/hooks/regional.ts b/packages/bank-ui/src/hooks/regional.ts index 909bcfcec..e0c861a0f 100644 --- a/packages/bank-ui/src/hooks/regional.ts +++ b/packages/bank-ui/src/hooks/regional.ts @@ -14,7 +14,6 @@ GNU Taler; see the file COPYING. If not, see */ -import { PAGE_SIZE } from "../utils.js"; import { useSessionState } from "./session.js"; import { @@ -36,6 +35,7 @@ import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; import _useSWR, { SWRHook, mutate } from "swr"; import { buildPaginatedResult } from "./account.js"; +import { PAGINATED_LIST_REQUEST } from "../utils.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 const useSWR = _useSWR as unknown as SWRHook; @@ -232,14 +232,14 @@ export function useBusinessAccounts() { const [offset, setOffset] = useState(); - function fetcher([token, offset]: [AccessToken, number]) { + function fetcher([token, aid]: [AccessToken, number]) { // FIXME: add account name filter return api.getAccounts( token, {}, { - limit: PAGE_SIZE + 1, - offset: String(offset), + limit: PAGINATED_LIST_REQUEST, + offset: aid ? String(aid) : undefined, order: "asc", }, ); diff --git a/packages/bank-ui/src/utils.ts b/packages/bank-ui/src/utils.ts index 305f13803..2cc502416 100644 --- a/packages/bank-ui/src/utils.ts +++ b/packages/bank-ui/src/utils.ts @@ -120,7 +120,11 @@ export enum CashoutStatus { PENDING = "pending", } -export const PAGE_SIZE = 5; + +export const PAGINATED_LIST_SIZE = 5; +// when doing paginated request, ask for one more +// and use it to know if there are more to request +export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1; type Translator = ReturnType["i18n"]; diff --git a/packages/merchant-backoffice-ui/src/Application.tsx b/packages/merchant-backoffice-ui/src/Application.tsx index 497f49c0e..1a4bd6708 100644 --- a/packages/merchant-backoffice-ui/src/Application.tsx +++ b/packages/merchant-backoffice-ui/src/Application.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi, assertUnreachable, canonicalizeBaseUrl } from "@gnu-taler/taler-util"; +import { CacheEvictor, TalerMerchantApi, TalerMerchantInstanceCacheEviction, TalerMerchantManagementCacheEviction, assertUnreachable, canonicalizeBaseUrl } from "@gnu-taler/taler-util"; import { BrowserHashNavigationProvider, ConfigResultFail, @@ -33,13 +33,20 @@ import { useEffect, useState } from "preact/hooks"; import { SWRConfig } from "swr"; import { Routing } from "./Routing.js"; import { Loading } from "./components/exception/loading.js"; +import { NotificationCard } from "./components/menu/index.js"; import { SettingsProvider } from "./context/settings.js"; +import { revalidateBankAccountDetails, revalidateInstanceBankAccounts } from "./hooks/bank.js"; +import { revalidateBackendInstances, revalidateInstanceDetails, revalidateManagedInstanceDetails } from "./hooks/instance.js"; +import { revalidateInstanceOtpDevices, revalidateOtpDeviceDetails } from "./hooks/otp.js"; +import { revalidateInstanceProducts, revalidateProductDetails } from "./hooks/product.js"; +import { revalidateInstanceTemplates, revalidateTemplateDetails } from "./hooks/templates.js"; +import { revalidateInstanceTransfers } from "./hooks/transfer.js"; +import { revalidateInstanceWebhooks, revalidateWebhookDetails } from "./hooks/webhooks.js"; import { strings } from "./i18n/strings.js"; import { MerchantUiSettings, buildDefaultBackendBaseURL, fetchSettings } from "./settings.js"; -import { NotificationCard } from "./components/menu/index.js"; +import { revalidateInstanceOrders, revalidateOrderDetails } from "./hooks/order.js"; const WITH_LOCAL_STORAGE_CACHE = false; - export function Application(): VNode { const [settings, setSettings] = useState(); useEffect(() => { @@ -57,7 +64,9 @@ export function Application(): VNode { de: strings["de"].completeness, }} > - + { + async notifySuccess(op: TalerMerchantManagementCacheEviction | TalerMerchantInstanceCacheEviction) { + switch(op) { + case TalerMerchantManagementCacheEviction.CREATE_INSTANCE: { + await Promise.all([ + revalidateBackendInstances() + ]) + return + } + case TalerMerchantManagementCacheEviction.UPDATE_INSTANCE: { + await Promise.all([ + revalidateManagedInstanceDetails() + ]) + return + } + case TalerMerchantManagementCacheEviction.DELETE_INSTANCE:{ + await Promise.all([ + revalidateBackendInstances() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_CURRENT_INSTANCE:{ + await Promise.all([ + revalidateInstanceDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_CURRENT_INSTANCE:{ + await Promise.all([ + revalidateInstanceDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_BANK_ACCOUNT:{ + await Promise.all([ + revalidateInstanceBankAccounts() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_BANK_ACCOUNT:{ + await Promise.all([ + revalidateBankAccountDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_BANK_ACCOUNT:{ + await Promise.all([ + revalidateInstanceBankAccounts() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_PRODUCT:{ + await Promise.all([ + revalidateInstanceProducts() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT:{ + await Promise.all([ + revalidateProductDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_PRODUCT:{ + await Promise.all([ + revalidateInstanceProducts() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_TRANSFER:{ + await Promise.all([ + revalidateInstanceTransfers() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_TRANSFER:{ + await Promise.all([ + revalidateInstanceTransfers() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_DEVICE:{ + await Promise.all([ + revalidateInstanceOtpDevices() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_DEVICE:{ + await Promise.all([ + revalidateOtpDeviceDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_DEVICE:{ + await Promise.all([ + revalidateInstanceOtpDevices() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_TEMPLATE:{ + await Promise.all([ + revalidateInstanceTemplates() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_TEMPLATE:{ + await Promise.all([ + revalidateTemplateDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_TEMPLATE:{ + await Promise.all([ + revalidateInstanceTemplates() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_WEBHOOK:{ + await Promise.all([ + revalidateInstanceWebhooks() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_WEBHOOK:{ + await Promise.all([ + revalidateWebhookDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_WEBHOOK:{ + await Promise.all([ + revalidateInstanceWebhooks() + ]) + return + } + case TalerMerchantInstanceCacheEviction.CREATE_ORDER:{ + await Promise.all([ + revalidateInstanceOrders() + ]) + return + } + case TalerMerchantInstanceCacheEviction.UPDATE_ORDER: { + await Promise.all([ + revalidateOrderDetails() + ]) + return + } + case TalerMerchantInstanceCacheEviction.DELETE_ORDER: { + await Promise.all([ + revalidateInstanceOrders() + ]) + return + } + case TalerMerchantInstanceCacheEviction.LAST: + // case TalerMerchantInstanceCacheEviction.CREATE_TOKENFAMILY:{ + // await Promise.all([ + // reva + // ]) + // return + // } + // case TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY:{ + // await Promise.all([ + // ]) + // return + // } + // case TalerMerchantInstanceCacheEviction.DELETE_TOKENFAMILY:{ + // await Promise.all([ + // ]) + // return + // } + } + } + +} diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx index 874c2b0f2..7398aaeec 100644 --- a/packages/merchant-backoffice-ui/src/Routing.tsx +++ b/packages/merchant-backoffice-ui/src/Routing.tsx @@ -312,8 +312,6 @@ export function Routing(_p: Props): VNode { onUpdate={(id: string): void => { route(`/instance/${id}/update`); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> )} {state.isAdmin && ( @@ -322,7 +320,7 @@ export function Routing(_p: Props): VNode { component={InstanceCreatePage} onBack={() => route(AdminPaths.list_instances)} onConfirm={() => { - route(InstancePaths.order_list); + route(AdminPaths.list_instances); }} /> )} @@ -334,9 +332,6 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(AdminPaths.list_instances); }} - // onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)} - // onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)} - // onNotFound={NotFoundPage} /> )} {/** @@ -351,10 +346,6 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(`/`); }} - // onUpdateError={noop} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> {/** * Update instance page @@ -368,9 +359,6 @@ export function Routing(_p: Props): VNode { onCancel={() => { route(InstancePaths.order_list); }} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> {/** * Inventory pages @@ -384,9 +372,6 @@ export function Routing(_p: Props): VNode { onSelect={(id: string) => { route(InstancePaths.inventory_update.replace(":pid", id)); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> { route(InstancePaths.inventory_list); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> { route(InstancePaths.bank_update.replace(":bid", id)); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> { route(InstancePaths.bank_list); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> { route(InstancePaths.order_details.replace(":oid", id)); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> { route(InstancePaths.order_list); }} @@ -492,9 +462,6 @@ export function Routing(_p: Props): VNode { { route(InstancePaths.transfers_new); }} @@ -515,9 +482,6 @@ export function Routing(_p: Props): VNode { { route(InstancePaths.webhooks_new); }} @@ -531,9 +495,6 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.webhooks_list); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.webhooks_list); }} @@ -554,9 +515,6 @@ export function Routing(_p: Props): VNode { { route(InstancePaths.otp_devices_new); }} @@ -570,9 +528,6 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.otp_devices_list); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.otp_devices_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.otp_devices_list); }} @@ -593,9 +548,6 @@ export function Routing(_p: Props): VNode { { route(InstancePaths.templates_new); }} @@ -615,9 +567,6 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.templates_list); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.templates_list); }} @@ -638,9 +587,6 @@ export function Routing(_p: Props): VNode { onOrderCreated={(id: string) => { route(InstancePaths.order_details.replace(":oid", id)); }} - // onUnauthorized={LoginPageAccessDenied} - // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} - // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.templates_list); }} @@ -648,9 +594,6 @@ export function Routing(_p: Props): VNode { { route(InstancePaths.templates_list); }} diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts b/packages/merchant-backoffice-ui/src/hooks/bank.ts index 513314f17..6555ec085 100644 --- a/packages/merchant-backoffice-ui/src/hooks/bank.ts +++ b/packages/merchant-backoffice-ui/src/hooks/bank.ts @@ -22,6 +22,7 @@ import { useState } from "preact/hooks"; import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; import _useSWR, { SWRHook, mutate } from "swr"; import { useSessionContext } from "../context/session.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; import { buildPaginatedResult } from "./webhooks.js"; const useSWR = _useSWR as unknown as SWRHook; @@ -43,7 +44,7 @@ export function useInstanceBankAccounts() { async function fetcher([token, bid]: [AccessToken, string]) { return await instance.listBankAccounts(token, { - limit: 5, + limit: PAGINATED_LIST_REQUEST, offset: bid, order: "dec", }); diff --git a/packages/merchant-backoffice-ui/src/hooks/order.test.ts b/packages/merchant-backoffice-ui/src/hooks/order.test.ts index 1aa2fcf0a..9c1eaccbb 100644 --- a/packages/merchant-backoffice-ui/src/hooks/order.test.ts +++ b/packages/merchant-backoffice-ui/src/hooks/order.test.ts @@ -464,7 +464,7 @@ describe("order listing pagination", () => { expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" }); }); - it("should load more if result brings more that PAGE_SIZE", async () => { + it("should load more if result brings more that PAGINATED_LIST_REQUEST", async () => { const env = new ApiMockEnvironment(); const ordersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ diff --git a/packages/merchant-backoffice-ui/src/hooks/order.ts b/packages/merchant-backoffice-ui/src/hooks/order.ts index b1805f6e3..79f970ec2 100644 --- a/packages/merchant-backoffice-ui/src/hooks/order.ts +++ b/packages/merchant-backoffice-ui/src/hooks/order.ts @@ -16,7 +16,7 @@ import { useMerchantApiContext } from "@gnu-taler/web-util/browser"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { AbsoluteTime, AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; @@ -60,6 +60,13 @@ export interface InstanceOrderFilter { position?: string; } +export function revalidateInstanceOrders() { + return mutate( + (key) => Array.isArray(key) && key[key.length - 1] === "listOrders", + undefined, + { revalidate: true }, + ); +} export function useInstanceOrders( args?: InstanceOrderFilter, updatePosition: (d: string | undefined) => void = () => { }, @@ -71,7 +78,7 @@ export function useInstanceOrders( async function fetcher([token, o, p, r, w, d]: [AccessToken, string, boolean, boolean, boolean, AbsoluteTime]) { return await instance.listOrders(token, { - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: o, order: "dec", paid: p, diff --git a/packages/merchant-backoffice-ui/src/hooks/otp.ts b/packages/merchant-backoffice-ui/src/hooks/otp.ts index 898a27a69..9820c5f11 100644 --- a/packages/merchant-backoffice-ui/src/hooks/otp.ts +++ b/packages/merchant-backoffice-ui/src/hooks/otp.ts @@ -17,7 +17,7 @@ import { useMerchantApiContext } from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; @@ -41,7 +41,7 @@ export function useInstanceOtpDevices() { async function fetcher([token, bid]: [AccessToken, string]) { return await instance.listOtpDevices(token, { - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: bid, order: "dec", }); diff --git a/packages/merchant-backoffice-ui/src/hooks/product.ts b/packages/merchant-backoffice-ui/src/hooks/product.ts index cfbd4a653..7f3504c64 100644 --- a/packages/merchant-backoffice-ui/src/hooks/product.ts +++ b/packages/merchant-backoffice-ui/src/hooks/product.ts @@ -22,7 +22,7 @@ import { AccessToken, OperationOk, TalerHttpError, TalerMerchantApi, TalerMercha import { useState } from "preact/hooks"; import _useSWR, { SWRHook, mutate } from "swr"; import { useSessionContext } from "../context/session.js"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; import { buildPaginatedResult } from "./webhooks.js"; const useSWR = _useSWR as unknown as SWRHook; @@ -46,7 +46,7 @@ export function useInstanceProducts() { async function fetcher([token, bid]: [AccessToken, number]) { const list = await instance.listProducts(token, { - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: bid === undefined ? undefined: String(bid), order: "dec", }); diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts b/packages/merchant-backoffice-ui/src/hooks/templates.ts index dbea93fdf..e0065e284 100644 --- a/packages/merchant-backoffice-ui/src/hooks/templates.ts +++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts @@ -17,7 +17,7 @@ import { useMerchantApiContext } from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; @@ -45,7 +45,7 @@ export function useInstanceTemplates() { async function fetcher([token, bid]: [AccessToken, string]) { return await instance.listTemplates(token, { - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: bid, order: "dec", }); @@ -64,7 +64,7 @@ export function useInstanceTemplates() { } -export function revalidateProductDetails() { +export function revalidateTemplateDetails() { return mutate( (key) => Array.isArray(key) && key[key.length - 1] === "getTemplateDetails", undefined, diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts b/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts index d0865d236..7daaf5049 100644 --- a/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts +++ b/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts @@ -163,7 +163,7 @@ describe("transfer listing pagination", () => { expect(hookBehavior).deep.eq({ result: "ok" }); }); - it("should load more if result brings more that PAGE_SIZE", async () => { + it("should load more if result brings more that PAGINATED_LIST_REQUEST", async () => { const env = new ApiMockEnvironment(); const transfersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ @@ -172,7 +172,7 @@ describe("transfer listing pagination", () => { const transfersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({ wtid: String(i + 20), })); - const transfersFrom20to0 = [...transfersFrom0to20].reverse(); + // const transfersFrom20to0 = [...transfersFrom0to20].reverse(); env.addRequestExpectation(API_LIST_TRANSFERS, { qparam: { limit: 20, payto_uri: "payto://", offset: "1" }, diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.ts b/packages/merchant-backoffice-ui/src/hooks/transfer.ts index 44068f52d..6c2fc1d75 100644 --- a/packages/merchant-backoffice-ui/src/hooks/transfer.ts +++ b/packages/merchant-backoffice-ui/src/hooks/transfer.ts @@ -16,7 +16,7 @@ import { useMerchantApiContext } from "@gnu-taler/web-util/browser"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; @@ -51,7 +51,7 @@ export function useInstanceTransfers( return await instance.listWireTransfers(token, { paytoURI: p, verified: v, - limit: PAGE_SIZE, + limit: PAGINATED_LIST_REQUEST, offset: o, order: "dec", }); diff --git a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts index c69db6e80..c5d0382e2 100644 --- a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts +++ b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts @@ -17,7 +17,7 @@ import { useMerchantApiContext } from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; -import { PAGE_SIZE } from "../utils/constants.js"; +import { PAGINATED_LIST_REQUEST } from "../utils/constants.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { AccessToken, OperationOk, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; @@ -43,7 +43,7 @@ export function useInstanceWebhooks() { async function fetcher([token, bid]: [AccessToken, string]) { return await instance.listWebhooks(token, { - limit: 5, + limit: PAGINATED_LIST_REQUEST, offset: bid, order: "dec", }); @@ -71,11 +71,11 @@ type PaginatedResult = OperationOk & { //TODO: consider sending this to web-util export function buildPaginatedResult(data: R[], offset: OffId | undefined, setOffset: (o: OffId | undefined) => void, getId: (r: R) => OffId): PaginatedResult { - const isLastPage = data.length <= PAGE_SIZE; + const isLastPage = data.length < PAGINATED_LIST_REQUEST; const isFirstPage = offset === undefined; const result = structuredClone(data); - if (result.length == PAGE_SIZE + 1) { + if (result.length == PAGINATED_LIST_REQUEST) { result.pop(); } return { diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx index c3df81b87..919b608c3 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -146,7 +146,7 @@ function Table({
{hasMoreBefore && ( )} @@ -219,8 +219,10 @@ function Table({
{hasMoreAfter && ( - )}
@@ -301,7 +303,7 @@ export function RefundModal({ : undefined, }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined, + (k) => (errors as Record)[k] !== undefined, ); const validateAndConfirm = () => { @@ -380,7 +382,7 @@ export function RefundModal({ errors={errors} object={form} - valueHandler={(d) => setValue(d as any)} + valueHandler={(d) => setValue(d)} > name="refund" diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx index 9022cc35b..8ca0a9c58 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx @@ -52,9 +52,7 @@ export function ListPage({ onDelete={onDelete} onSelect={onSelect} onLoadMoreBefore={onLoadMoreBefore} - hasMoreBefore={!onLoadMoreBefore} onLoadMoreAfter={onLoadMoreAfter} - hasMoreAfter={!onLoadMoreAfter} /> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx index 7b1ccd4fc..afe3c98e2 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx @@ -32,8 +32,6 @@ interface Props { onSelect: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -44,8 +42,6 @@ export function CardTable({ onSelect, onLoadMoreAfter, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState([]); @@ -85,8 +81,6 @@ export function CardTable({ rowSelectionHandler={rowSelectionHandler} onLoadMoreAfter={onLoadMoreAfter} onLoadMoreBefore={onLoadMoreBefore} - hasMoreAfter={hasMoreAfter} - hasMoreBefore={hasMoreBefore} /> ) : ( @@ -104,8 +98,6 @@ interface TableProps { onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -115,19 +107,17 @@ function Table({ onDelete, onSelect, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: TableProps): VNode { const { i18n } = useTranslationContext(); return (
- {hasMoreBefore && ( + {onLoadMoreBefore && ( )} @@ -174,13 +164,13 @@ function Table({ })}
- {hasMoreAfter && ( + {onLoadMoreAfter && ( )}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx index 265146c01..292974e89 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -341,12 +341,6 @@ function FastProductWithInfiniteStockUpdateForm({
-
- - -
)} @@ -198,13 +188,13 @@ function Table({ })}
- {hasMoreAfter && ( + {onLoadMoreAfter && ( )}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx index e1493a870..b99549825 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -62,7 +62,7 @@ type Entity = { interface Props { onUpdate: (d: TalerMerchantApi.TemplatePatchDetails) => Promise; onBack?: () => void; - template: TalerMerchantApi.TemplateDetails; + template: TalerMerchantApi.TemplateDetails & WithId; } export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { @@ -187,7 +187,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
- {new URL(`templates/${template.otp_id}`,backendUrl.href).href} + {new URL(`templates/${template.id}`,backendUrl.href).href}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx index 2c0c358e2..9e5099947 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx @@ -79,7 +79,7 @@ export default function UpdateTemplate({ { return lib.instance.updateTemplate(state.token, tid, data) diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx index 7b54dc5ed..ff2bc6c23 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx @@ -125,9 +125,7 @@ export function ListPage({ onCreate={onCreate} onDelete={onDelete} onLoadMoreBefore={onLoadMoreBefore} - hasMoreBefore={!onLoadMoreBefore} onLoadMoreAfter={onLoadMoreAfter} - hasMoreAfter={!onLoadMoreAfter} /> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx index cf7ebe922..b9235c669 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx @@ -34,8 +34,6 @@ interface Props { onCreate: () => void; accounts: string[]; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -45,8 +43,6 @@ export function CardTable({ onDelete, onLoadMoreAfter, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState([]); @@ -85,8 +81,6 @@ export function CardTable({ rowSelectionHandler={rowSelectionHandler} onLoadMoreAfter={onLoadMoreAfter} onLoadMoreBefore={onLoadMoreBefore} - hasMoreAfter={hasMoreAfter} - hasMoreBefore={hasMoreBefore} /> ) : ( @@ -103,8 +97,6 @@ interface TableProps { onDelete: (id: Entity) => void; rowSelectionHandler: StateUpdater; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -113,20 +105,18 @@ function Table({ onLoadMoreAfter, onDelete, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: TableProps): VNode { const { i18n } = useTranslationContext(); const [settings] = usePreference(); return (
- {hasMoreBefore && ( + {onLoadMoreBefore && ( )} @@ -192,13 +182,13 @@ function Table({ })}
- {hasMoreAfter && ( + {onLoadMoreAfter && ( )}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx index 98bd61d8f..3f1feb8e9 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx @@ -55,9 +55,7 @@ export function ListPage({ onDelete={onDelete} onSelect={onSelect} onLoadMoreBefore={onLoadMoreBefore} - hasMoreBefore={!onLoadMoreBefore} onLoadMoreAfter={onLoadMoreAfter} - hasMoreAfter={!onLoadMoreAfter} /> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx index 2cafc7f9a..919285e78 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx @@ -32,8 +32,6 @@ interface Props { onSelect: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -44,8 +42,6 @@ export function CardTable({ onSelect, onLoadMoreAfter, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState([]); @@ -85,8 +81,6 @@ export function CardTable({ rowSelectionHandler={rowSelectionHandler} onLoadMoreAfter={onLoadMoreAfter} onLoadMoreBefore={onLoadMoreBefore} - hasMoreAfter={hasMoreAfter} - hasMoreBefore={hasMoreBefore} /> ) : ( @@ -104,8 +98,6 @@ interface TableProps { onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater; onLoadMoreBefore?: () => void; - hasMoreBefore?: boolean; - hasMoreAfter?: boolean; onLoadMoreAfter?: () => void; } @@ -115,19 +107,17 @@ function Table({ onDelete, onSelect, onLoadMoreBefore, - hasMoreAfter, - hasMoreBefore, }: TableProps): VNode { const { i18n } = useTranslationContext(); return (
- {hasMoreBefore && ( + {onLoadMoreBefore && ( )} @@ -181,13 +171,13 @@ function Table({ })}
- {hasMoreAfter && ( + {onLoadMoreAfter && ( )}
diff --git a/packages/merchant-backoffice-ui/src/utils/constants.ts b/packages/merchant-backoffice-ui/src/utils/constants.ts index 9e7a69ed0..6b4d8eade 100644 --- a/packages/merchant-backoffice-ui/src/utils/constants.ts +++ b/packages/merchant-backoffice-ui/src/utils/constants.ts @@ -35,11 +35,10 @@ export const CROCKFORD_BASE32_REGEX = export const URL_REGEX = /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/; -// how much rows we add every time user hit load more -export const PAGE_SIZE = 20; -// how bigger can be the result set -// after this threshold, load more with move the cursor -export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; +export const PAGINATED_LIST_SIZE = 5; +// when doing paginated request, ask for one more +// and use it to know if there are more to request +export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1; // how much we will wait for all request, in seconds export const DEFAULT_REQUEST_TIMEOUT = 10; diff --git a/packages/taler-harness/bin/create_merchantAndBankAccount_pdf.sh b/packages/taler-harness/bin/create_merchantAndBankAccount_pdf.sh index cd87c18c9..008791eff 100755 --- a/packages/taler-harness/bin/create_merchantAndBankAccount_pdf.sh +++ b/packages/taler-harness/bin/create_merchantAndBankAccount_pdf.sh @@ -1,7 +1,20 @@ #!/bin/bash +# This file is in the public domain. + THIS_FILE=$(realpath "$0") DIR=$(dirname "$THIS_FILE") +# this script requires from debian packages +# * jq +# * sponge (from moreutils) +# * wkhtmltopdf +# * qrencode +# * sponge: +# +# and from python pip packages +# +# * chevron + DATA=$(mktemp) set -e diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts index cfe3155d1..23d7c76df 100644 --- a/packages/taler-util/src/http-client/merchant.ts +++ b/packages/taler-util/src/http-client/merchant.ts @@ -81,9 +81,36 @@ export type TalerMerchantInstanceErrorsByMethod< export enum TalerMerchantInstanceCacheEviction { CREATE_ORDER, + UPDATE_ORDER, + DELETE_ORDER, + UPDATE_CURRENT_INSTANCE, + DELETE_CURRENT_INSTANCE, + CREATE_BANK_ACCOUNT, + UPDATE_BANK_ACCOUNT, + DELETE_BANK_ACCOUNT, + CREATE_PRODUCT, + UPDATE_PRODUCT, + DELETE_PRODUCT, + CREATE_TRANSFER, + DELETE_TRANSFER, + CREATE_DEVICE, + UPDATE_DEVICE, + DELETE_DEVICE, + CREATE_TEMPLATE, + UPDATE_TEMPLATE, + DELETE_TEMPLATE, + CREATE_WEBHOOK, + UPDATE_WEBHOOK, + DELETE_WEBHOOK, + CREATE_TOKENFAMILY, + UPDATE_TOKENFAMILY, + DELETE_TOKENFAMILY, + LAST, } export enum TalerMerchantManagementCacheEviction { - CREATE_INSTANCE, + CREATE_INSTANCE = TalerMerchantInstanceCacheEviction.LAST + 1, + UPDATE_INSTANCE, + DELETE_INSTANCE, } /** * Protocol version spoken with the core bank. @@ -150,8 +177,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForClaimResponse()); + } case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -173,8 +202,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForPaymentResponse()); + } case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.PaymentRequired: @@ -274,8 +305,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForPaidRefundStatusResponse()); + } case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Forbidden: @@ -302,8 +335,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForAbortResponse()); + } case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Forbidden: @@ -330,8 +365,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForWalletRefundResponse()); + } case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Forbidden: @@ -399,8 +436,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_CURRENT_INSTANCE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -461,8 +500,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_CURRENT_INSTANCE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -550,8 +591,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_BANK_ACCOUNT); return opSuccessFromHttp(resp, codecForAccountAddResponse()); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -583,8 +626,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_BANK_ACCOUNT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -669,8 +714,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_BANK_ACCOUNT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -704,8 +751,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_PRODUCT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: // FIXME: missing in docs @@ -738,8 +787,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -831,8 +882,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -860,8 +913,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_PRODUCT); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1049,8 +1104,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opEmptySuccess(resp); + } case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.Unauthorized: // FIXME: missing in docs @@ -1082,8 +1139,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_ORDER); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1120,8 +1179,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER); return opSuccessFromHttp(resp, codecForMerchantRefundResponse()); + } case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: // FIXME: missing in docs @@ -1161,8 +1222,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TRANSFER); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1234,8 +1297,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TRANSFER); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1271,8 +1336,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_DEVICE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1302,8 +1369,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_DEVICE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1396,8 +1465,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_DEVICE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1430,8 +1501,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TEMPLATE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1461,8 +1534,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_TEMPLATE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1544,8 +1619,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TEMPLATE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1615,8 +1692,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_WEBHOOK); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1647,8 +1726,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_WEBHOOK); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1731,8 +1812,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_WEBHOOK); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1766,8 +1849,10 @@ export class TalerMerchantInstanceHttpClient { }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TOKENFAMILY); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1797,8 +1882,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.Ok: + case HttpStatusCode.Ok: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY); return opSuccessFromHttp(resp, codecForTokenFamilyDetails()); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1883,8 +1970,10 @@ export class TalerMerchantInstanceHttpClient { headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TOKENFAMILY); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -1913,15 +2002,15 @@ export type TalerMerchantManagementErrorsByMethod< > = FailCasesByMethod; export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttpClient { - readonly cacheManagementEvictor: CacheEvictor; + readonly cacheManagementEvictor: CacheEvictor; constructor( readonly baseUrl: string, httpClient?: HttpRequestLibrary, - cacheManagementEvictor?: CacheEvictor, - cacheEvictor?: CacheEvictor, + // cacheManagementEvictor?: CacheEvictor, + cacheEvictor?: CacheEvictor, ) { super(baseUrl, httpClient, cacheEvictor); - this.cacheManagementEvictor = cacheManagementEvictor ?? nullEvictor; + this.cacheManagementEvictor = cacheEvictor ?? nullEvictor; } getSubInstanceAPI(instanceId: string) { @@ -1953,9 +2042,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp switch (resp.status) { case HttpStatusCode.NoContent: { - this.cacheManagementEvictor.notifySuccess( - TalerMerchantManagementCacheEviction.CREATE_INSTANCE, - ); + this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.CREATE_INSTANCE); return opEmptySuccess(resp); } case HttpStatusCode.Unauthorized: // FIXME: missing in docs @@ -2022,8 +2109,10 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.UPDATE_INSTANCE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -2112,8 +2201,10 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp headers, }); switch (resp.status) { - case HttpStatusCode.NoContent: + case HttpStatusCode.NoContent: { + this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.DELETE_INSTANCE); return opEmptySuccess(resp); + } case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index dd2161deb..ea7ba341b 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -777,7 +777,7 @@ export const codecForTransferDetails = .property("payto_uri", codecForPaytoString()) .property("exchange_url", codecForURL()) .property("transfer_serial_id", codecForNumber()) - .property("execution_time", codecForTimestamp) + .property("execution_time", codecOptional(codecForTimestamp)) .property("verified", codecOptional(codecForBoolean())) .property("confirmed", codecOptional(codecForBoolean())) .build("TalerMerchantApi.TransferDetails"); @@ -3781,6 +3781,8 @@ export namespace TalerMerchantApi { // List of accounts that are known for the instance. accounts: BankAccountSummaryEntry[]; } + + // TODO: missing in docs export interface BankAccountSummaryEntry { // payto:// URI of the account. payto_uri: PaytoString; diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts index 2e24856ee..95b4911a0 100644 --- a/packages/taler-util/src/time.ts +++ b/packages/taler-util/src/time.ts @@ -604,6 +604,9 @@ export function durationAdd(d1: Duration, d2: Duration): Duration { export const codecForAbsoluteTime: Codec = { decode(x: any, c?: Context): AbsoluteTime { + if (x === undefined) { + throw Error(`got undefined and expected absolute time at ${renderContext(c)}`); + } const t_ms = x.t_ms; if (typeof t_ms === "string") { if (t_ms === "never") { @@ -619,6 +622,9 @@ export const codecForAbsoluteTime: Codec = { export const codecForTimestamp: Codec = { decode(x: any, c?: Context): TalerProtocolTimestamp { // Compatibility, should be removed soon. + if (x === undefined) { + throw Error(`got undefined and expected timestamp at ${renderContext(c)}`); + } const t_ms = x.t_ms; if (typeof t_ms === "string") { if (t_ms === "never") { diff --git a/packages/web-util/src/context/merchant-api.ts b/packages/web-util/src/context/merchant-api.ts index 26d9c9e85..604119e84 100644 --- a/packages/web-util/src/context/merchant-api.ts +++ b/packages/web-util/src/context/merchant-api.ts @@ -69,10 +69,7 @@ enum VersionHint { } type Evictors = { - management?: CacheEvictor; - instance?: ( - instanceId: string, - ) => CacheEvictor; + management?: CacheEvictor; }; type ConfigResult = -- cgit v1.2.3