diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wxApi.ts')
-rw-r--r-- | packages/taler-wallet-webextension/src/wxApi.ts | 510 |
1 files changed, 191 insertions, 319 deletions
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 92597cbd2..195efecd4 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -1,17 +1,17 @@ /* - This file is part of TALER - (C) 2016 GNUnet e.V. + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. - TALER is free software; you can redistribute it and/or modify it under the + 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. - TALER is distributed in the hope that it will be useful, but WITHOUT ANY + 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** @@ -22,343 +22,215 @@ * Imports. */ import { + AbsoluteTime, CoreApiResponse, - ConfirmPayResult, - BalancesResponse, - TransactionsResponse, - ApplyRefundResponse, - PreparePayResult, - AcceptWithdrawalResponse, - WalletDiagnostics, - GetWithdrawalDetailsForUriRequest, - WithdrawUriInfoResponse, - PrepareTipRequest, - PrepareTipResult, - AcceptTipRequest, - DeleteTransactionRequest, - RetryTransactionRequest, - SetWalletDeviceIdRequest, - GetExchangeWithdrawalInfo, - AcceptExchangeTosRequest, - AcceptManualWithdrawalResult, - AcceptManualWithdrawalRequest, - AmountJson, - ExchangesListRespose, - AddExchangeRequest, - GetExchangeTosResult, + DetailsMap, + Logger, + LogLevel, + NotificationType, + TalerError, + TalerErrorCode, + TalerErrorDetail, + WalletNotification } from "@gnu-taler/taler-util"; -import { AddBackupProviderRequest, BackupProviderState, OperationFailedError, RemoveBackupProviderRequest } from "@gnu-taler/taler-wallet-core"; -import { BackupInfo } from "@gnu-taler/taler-wallet-core"; -import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw"; - -export interface ExtendedPermissionsResponse { - newValue: boolean; -} - -/** - * Response with information about available version upgrades. - */ -export interface UpgradeResponse { - /** - * Is a reset required because of a new DB version - * that can't be automatically upgraded? - */ - dbResetRequired: boolean; - - /** - * Current database version. - */ - currentDbVersion: string; - - /** - * Old db version (if applicable). - */ - oldDbVersion: string; -} - -async function callBackend(operation: string, payload: any): Promise<any> { - return new Promise<any>((resolve, reject) => { - chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => { - if (chrome.runtime.lastError) { - console.log("Error calling backend"); - reject( - new Error( - `Error contacting backend: chrome.runtime.lastError.message`, - ), - ); - } - console.log("got response", resp); - const r = resp as CoreApiResponse; - if (r.type === "error") { - reject(new OperationFailedError(r.error)); - return; - } - resolve(r.result); - }); - }); -} - -/** - * Start refreshing a coin. - */ -export function refresh(coinPub: string): Promise<void> { - return callBackend("refresh-coin", { coinPub }); -} - -/** - * Pay for a proposal. - */ -export function confirmPay( - proposalId: string, - sessionId: string | undefined, -): Promise<ConfirmPayResult> { - return callBackend("confirmPay", { proposalId, sessionId }); -} - -/** - * Check upgrade information - */ -export function checkUpgrade(): Promise<UpgradeResponse> { - return callBackend("check-upgrade", {}); -} - -/** - * Reset database - */ -export function resetDb(): Promise<void> { - return callBackend("reset-db", {}); -} - -/** - * Get balances for all currencies/exchanges. - */ -export function getBalance(): Promise<BalancesResponse> { - return callBackend("getBalances", {}); -} - -/** - * Retrieve the full event history for this wallet. - */ -export function getTransactions(): Promise<TransactionsResponse> { - return callBackend("getTransactions", {}); -} - -interface CurrencyInfo { - name: string; - baseUrl: string; - pub: string; -} -interface ListOfKnownCurrencies { - auditors: CurrencyInfo[], - exchanges: CurrencyInfo[], -} - -/** - * Get a list of currencies from known auditors and exchanges - */ -export function listKnownCurrencies(): Promise<ListOfKnownCurrencies> { - return callBackend("listCurrencies", {}).then(result => { - console.log("result list", result) - const auditors = result.trustedAuditors.map((a: Record<string, string>) => ({ - name: a.currency, - baseUrl: a.auditorBaseUrl, - pub: a.auditorPub, - })) - const exchanges = result.trustedExchanges.map((a: Record<string, string>) => ({ - name: a.currency, - baseUrl: a.exchangeBaseUrl, - pub: a.exchangeMasterPub, - })) - return { auditors, exchanges } - }); -} - -export function listExchanges(): Promise<ExchangesListRespose> { - return callBackend("listExchanges", {}) -} - -/** - * Get information about the current state of wallet backups. - */ -export function getBackupInfo(): Promise<BackupInfo> { - return callBackend("getBackupInfo", {}) -} - -/** - * Add a backup provider and activate it - */ -export function addBackupProvider(backupProviderBaseUrl: string, name: string): Promise<void> { - return callBackend("addBackupProvider", { - backupProviderBaseUrl, activate: true, name - } as AddBackupProviderRequest) -} - -export function setWalletDeviceId(walletDeviceId: string): Promise<void> { - return callBackend("setWalletDeviceId", { - walletDeviceId - } as SetWalletDeviceIdRequest) -} - -export function syncAllProviders(): Promise<void> { - return callBackend("runBackupCycle", {}) -} - -export function syncOneProvider(url: string): Promise<void> { - return callBackend("runBackupCycle", { providers: [url] }) -} -export function removeProvider(url: string): Promise<void> { - return callBackend("removeBackupProvider", { provider: url } as RemoveBackupProviderRequest) -} -export function extendedProvider(url: string): Promise<void> { - return callBackend("extendBackupProvider", { provider: url }) -} - -/** - * Retry a transaction - * @param transactionId - * @returns - */ -export function retryTransaction(transactionId: string): Promise<void> { - return callBackend("retryTransaction", { - transactionId - } as RetryTransactionRequest); -} - -/** - * Permanently delete a transaction from the transaction list - */ -export function deleteTransaction(transactionId: string): Promise<void> { - return callBackend("deleteTransaction", { - transactionId - } as DeleteTransactionRequest); -} +import { + WalletCoreApiClient, + WalletCoreOpKeys, + WalletCoreRequestType, + WalletCoreResponseType, +} from "@gnu-taler/taler-wallet-core"; +import { + ExtensionNotification, + MessageFromBackend, + MessageFromFrontendBackground, + MessageFromFrontendWallet, +} from "./platform/api.js"; +import { platform } from "./platform/foreground.js"; /** - * Download a refund and accept it. + * + * @author sebasjm */ -export function applyRefund( - talerRefundUri: string, -): Promise<ApplyRefundResponse> { - return callBackend("applyRefund", { talerRefundUri }); -} -/** - * Get details about a pay operation. - */ -export function preparePay(talerPayUri: string): Promise<PreparePayResult> { - return callBackend("preparePay", { talerPayUri }); -} +const logger = new Logger("wxApi"); -/** - * Get details about a withdraw operation. - */ -export function acceptWithdrawal( - talerWithdrawUri: string, - selectedExchange: string, -): Promise<AcceptWithdrawalResponse> { - return callBackend("acceptBankIntegratedWithdrawal", { - talerWithdrawUri, - exchangeBaseUrl: selectedExchange, - }); -} +export const WALLET_CORE_SUPPORTED_VERSION = "4:0:0" -/** - * Create a reserve into the exchange that expect the amount indicated - * @param exchangeBaseUrl - * @param amount - * @returns - */ -export function acceptManualWithdrawal( - exchangeBaseUrl: string, - amount: string, -): Promise<AcceptManualWithdrawalResult> { - return callBackend("acceptManualWithdrawal", { - amount, exchangeBaseUrl - }); +export interface ExtendedPermissionsResponse { + newValue: boolean; } -export function setExchangeTosAccepted( - exchangeBaseUrl: string, - etag: string | undefined -): Promise<void> { - return callBackend("setExchangeTosAccepted", { - exchangeBaseUrl, etag - } as AcceptExchangeTosRequest) +export interface BackgroundOperations { + resetDb: { + request: void; + response: void; + }; + runGarbageCollector: { + request: void; + response: void; + }; + reinitWallet: { + request: void; + response: void; + }; + getNotifications: { + request: void; + response: WalletEvent[]; + }; + clearNotifications: { + request: void; + response: void; + }; + setLoggingLevel: { + request: { + tag?: string; + level: LogLevel; + }; + response: void; + }; } +export type WalletEvent = { notification: WalletNotification, when: AbsoluteTime } -/** - * Get diagnostics information - */ -export function getDiagnostics(): Promise<WalletDiagnostics> { - return callBackend("wxGetDiagnostics", {}); +export interface BackgroundApiClient { + call<Op extends keyof BackgroundOperations>( + operation: Op, + payload: BackgroundOperations[Op]["request"], + ): Promise<BackgroundOperations[Op]["response"]>; } -/** - * Get diagnostics information - */ -export function setExtendedPermissions( - value: boolean, -): Promise<ExtendedPermissionsResponse> { - return callBackend("wxSetExtendedPermissions", { value }); -} +export class BackgroundError<T = any> extends Error { + public readonly errorDetail: TalerErrorDetail & T; + public readonly cause: Error; -/** - * Get diagnostics information - */ -export function getExtendedPermissions(): Promise<ExtendedPermissionsResponse> { - return callBackend("wxGetExtendedPermissions", {}); -} + constructor(title: string, e: TalerErrorDetail & T, cause: Error) { + super(title); + this.errorDetail = e; + this.cause = cause; + } -/** - * Get diagnostics information - */ -export function getWithdrawalDetailsForUri( - req: GetWithdrawalDetailsForUriRequest, -): Promise<WithdrawUriInfoResponse> { - return callBackend("getWithdrawalDetailsForUri", req); + hasErrorCode<C extends keyof DetailsMap>( + code: C, + ): this is BackgroundError<DetailsMap[C]> { + return this.errorDetail.code === code; + } } - /** - * Get diagnostics information + * BackgroundApiClient integration with browser platform */ -export function getExchangeWithdrawalInfo( - req: GetExchangeWithdrawalInfo, -): Promise<ExchangeWithdrawDetails> { - return callBackend("getExchangeWithdrawalInfo", req); -} -export function getExchangeTos( - exchangeBaseUrl: string, - acceptedFormat: string[], -): Promise<GetExchangeTosResult> { - return callBackend("getExchangeTos", { - exchangeBaseUrl, acceptedFormat - }); -} - -export function addExchange( - req: AddExchangeRequest, -): Promise<void> { - return callBackend("addExchange", req); -} +class BackgroundApiClientImpl implements BackgroundApiClient { + async call<Op extends keyof BackgroundOperations>( + operation: Op, + payload: BackgroundOperations[Op]["request"], + ): Promise<BackgroundOperations[Op]["response"]> { + let response: CoreApiResponse; + const message: MessageFromFrontendBackground<Op> = { + channel: "background", + operation, + payload, + }; -export function prepareTip(req: PrepareTipRequest): Promise<PrepareTipResult> { - return callBackend("prepareTip", req); -} - -export function acceptTip(req: AcceptTipRequest): Promise<void> { - return callBackend("acceptTip", req); + try { + response = await platform.sendMessageToBackground(message); + } catch (error) { + if (error instanceof Error) { + throw new BackgroundError(operation, { + code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, + when: AbsoluteTime.now(), + }, error); + } + throw error; + } + if (response.type === "error") { + throw new BackgroundError( + `Background operation "${operation}" failed`, + response.error, + TalerError.fromUncheckedDetail(response.error), + ); + } + logger.trace("response", response); + return response.result as any; + } +} + +/** + * WalletCoreApiClient integration with browser platform + */ +class WalletApiClientImpl implements WalletCoreApiClient { + async call<Op extends WalletCoreOpKeys>( + operation: Op, + payload: WalletCoreRequestType<Op>, + ): Promise<WalletCoreResponseType<Op>> { + let response: CoreApiResponse; + try { + const message: MessageFromFrontendWallet<Op> = { + channel: "wallet", + operation, + payload, + }; + response = await platform.sendMessageToBackground(message); + } catch (error) { + if (error instanceof Error) { + throw new BackgroundError(operation, { + code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, + when: AbsoluteTime.now(), + }, error); + } + throw error; + } + if (response.type === "error") { + throw new BackgroundError( + `Wallet operation "${operation}" failed`, + response.error, + TalerError.fromUncheckedDetail(response.error) + ); + } + logger.trace("got response", response); + return response.result as any; + } +} + +function onUpdateNotification( + messageTypes: Array<NotificationType>, + doCallback: undefined | ((n: WalletNotification) => void), +): () => void { + //if no callback, then ignore + if (!doCallback) + return () => { + return; + }; + const onNewMessage = (message: MessageFromBackend): void => { + const shouldNotify = message.type === "wallet" && messageTypes.includes(message.notification.type); + if (shouldNotify) { + doCallback(message.notification); + } + }; + return platform.listenToWalletBackground(onNewMessage); } -export function onUpdateNotification(f: () => void): () => void { - const port = chrome.runtime.connect({ name: "notifications" }); - const listener = (): void => { - f(); - }; - port.onMessage.addListener(listener); - return () => { - port.onMessage.removeListener(listener); +export type WxApiType = { + wallet: WalletCoreApiClient; + background: BackgroundApiClient; + listener: { + trigger: (d: ExtensionNotification) => void; + onUpdateNotification: typeof onUpdateNotification; }; -} +}; + +function trigger(w: ExtensionNotification) { + platform.triggerWalletEvent({ + type: "web-extension", + notification: w, + }) +} + +export const wxApi = { + wallet: new WalletApiClientImpl(), + background: new BackgroundApiClientImpl(), + listener: { + trigger, + onUpdateNotification, + }, +}; |