taler-typescript-core

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

commit 1ec9a91ccc5713a0966cb50fc35d1560d1906f17
parent 4748841815f035fbfccfc89b599abde8b48cc789
Author: Florian Dold <florian@dold.me>
Date:   Fri, 14 Mar 2025 15:03:10 +0100

wallet-core: allow passing taler URI when adding exchange

Diffstat:
Mpackages/taler-util/src/types-taler-wallet.ts | 31+++++++++++++++++++++++++++++--
Mpackages/taler-wallet-cli/src/index.ts | 2+-
Mpackages/taler-wallet-core/src/wallet-api-types.ts | 6++++--
Mpackages/taler-wallet-core/src/wallet.ts | 40+++++++++++++++++++++++++++++++++++-----
4 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -1840,6 +1840,16 @@ export interface GetExchangeEntryByUrlRequest { exchangeBaseUrl: string; } +export const codecForGetExchangeDetailedInfoRequest = + (): Codec<GetExchangeDetailedInfoRequest> => + buildCodecForObject<GetExchangeDetailedInfoRequest>() + .property("exchangeBaseUrl", codecForCanonBaseUrl()) + .build("GetExchangeDetailedInfoRequest"); + +export interface GetExchangeDetailedInfoRequest { + exchangeBaseUrl: string; +} + export const codecForGetExchangeEntryByUrlRequest = (): Codec<GetExchangeEntryByUrlRequest> => buildCodecForObject<GetExchangeEntryByUrlRequest>() @@ -1849,7 +1859,16 @@ export const codecForGetExchangeEntryByUrlRequest = export type GetExchangeEntryByUrlResponse = ExchangeListItem; export interface AddExchangeRequest { - exchangeBaseUrl: string; + /** + * @deprecated Use {@link uri} instead + */ + exchangeBaseUrl?: string; + + /** + * Either an http(s) exchange base URL or + * a taler://add-exchange/ URI. + */ + uri?: string; ephemeral?: boolean; @@ -1859,9 +1878,17 @@ export interface AddExchangeRequest { forceUpdate?: boolean; } +export interface AddExchangeResponse { + /** + * Base URL of the exchange that was added to the wallet. + */ + exchangeBaseUrl: string; +} + export const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> => buildCodecForObject<AddExchangeRequest>() - .property("exchangeBaseUrl", codecForCanonBaseUrl()) + .property("exchangeBaseUrl", codecOptional(codecForCanonBaseUrl())) + .property("uri", codecOptional(codecForString())) .property("forceUpdate", codecOptional(codecForBoolean())) .property("ephemeral", codecOptional(codecForBoolean())) .build("AddExchangeRequest"); diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts @@ -899,7 +899,7 @@ exchangesCli .action(async (args) => { await withWallet(args, { lazyTaskLoop: true }, async (wallet) => { await wallet.client.call(WalletApiOperation.AddExchange, { - exchangeBaseUrl: args.exchangesAddCmd.url, + uri: args.exchangesAddCmd.url, }); }); }); diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -35,6 +35,7 @@ import { AddBankAccountRequest, AddBankAccountResponse, AddExchangeRequest, + AddExchangeResponse, AddGlobalCurrencyAuditorRequest, AddGlobalCurrencyExchangeRequest, AmountResponse, @@ -86,6 +87,7 @@ import { GetDepositWireTypesForCurrencyResponse, GetDepositWireTypesRequest, GetDepositWireTypesResponse, + GetExchangeDetailedInfoRequest, GetExchangeEntryByUrlRequest, GetExchangeEntryByUrlResponse, GetExchangeResourcesRequest, @@ -705,7 +707,7 @@ export type PrepareWithdrawExchangeOp = { export type AddExchangeOp = { op: WalletApiOperation.AddExchange; request: AddExchangeRequest; - response: EmptyObject; + response: AddExchangeResponse; }; /** @@ -791,7 +793,7 @@ export type GetDepositWireTypesForCurrencyOp = { */ export type GetExchangeDetailedInfoOp = { op: WalletApiOperation.GetExchangeDetailedInfo; - request: AddExchangeRequest; + request: GetExchangeDetailedInfoRequest; response: ExchangeDetailedResponse; }; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -38,6 +38,7 @@ import { AddBankAccountRequest, AddBankAccountResponse, AddExchangeRequest, + AddExchangeResponse, AddGlobalCurrencyAuditorRequest, AddGlobalCurrencyExchangeRequest, AmountJson, @@ -929,16 +930,45 @@ async function handleValidateIban( async function handleAddExchange( wex: WalletExecutionContext, req: AddExchangeRequest, -): Promise<EmptyObject> { - await fetchFreshExchange(wex, req.exchangeBaseUrl, {}); +): Promise<AddExchangeResponse> { + let exchangeBaseUrl: string; + if (req.exchangeBaseUrl) { + logger.warn( + "Deprecated request property: AddExchangeRequest.exchangeBaseUrl", + ); + exchangeBaseUrl = req.exchangeBaseUrl; + } else if (req.uri) { + if (req.uri.startsWith("http")) { + const canonUrl = canonicalizeBaseUrl(req.uri); + if (req.uri != canonUrl) { + throw Error("exchange base URL must be canonicalized"); + } + exchangeBaseUrl = req.uri; + } else if (req.uri.startsWith("taler")) { + const p = parseTalerUri(req.uri); + if (p?.type !== TalerUriAction.AddExchange) { + throw Error("invalid taler://add-exchange/ URI"); + } + exchangeBaseUrl = p.exchangeBaseUrl; + } else { + throw Error("AddExchangeRequest.uri must be http(s) or taler URI"); + } + } else { + throw Error( + "AddExchangeRequest must either specify uri or exchangeBaseUrl", + ); + } + await fetchFreshExchange(wex, exchangeBaseUrl, {}); // Exchange has been explicitly added upon user request. // Thus, we mark it as "used". if (!req.ephemeral) { await wex.db.runAllStoresReadWriteTx({}, async (tx) => { - await markExchangeUsed(wex, tx, req.exchangeBaseUrl); + await markExchangeUsed(wex, tx, exchangeBaseUrl); }); } - return {}; + return { + exchangeBaseUrl, + }; } async function handleUpdateExchangeEntry( @@ -1793,7 +1823,7 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { handler: lookupExchangeByUri, }, [WalletApiOperation.GetExchangeDetailedInfo]: { - codec: codecForAddExchangeRequest(), + codec: codecForGetExchangeEntryByUrlRequest(), handler: (wex, req) => getExchangeDetailedInfo(wex, req.exchangeBaseUrl), }, [WalletApiOperation.ListBankAccounts]: {