From 20ebc44420c76207fa197b7c53201429ffd89bde Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 17 Dec 2019 18:42:14 +0100 Subject: android: propagate errors --- src/android/index.ts | 209 +++++++++++++++++++++++++++------------------------ 1 file changed, 112 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/android/index.ts b/src/android/index.ts index 003f7f4ea..a62299936 100644 --- a/src/android/index.ts +++ b/src/android/index.ts @@ -32,6 +32,7 @@ import { Headers, } from "../util/http"; import { NodeHttpLib } from "../headless/NodeHttpLib"; +import { OperationFailedAndReportedError } from "../operations/errors"; // @ts-ignore: special built-in module //import akono = require("akono"); @@ -121,136 +122,109 @@ export class AndroidHttpLib implements HttpRequestLibrary { } } -export function installAndroidWalletListener() { +function sendAkonoMessage(m: string) { // @ts-ignore - const sendMessage: (m: string) => void = globalThis.__akono_sendMessage; - if (typeof sendMessage !== "function") { - const errMsg = - "FATAL: cannot install android wallet listener: akono functions missing"; - console.error(errMsg); - throw new Error(errMsg); - } - let maybeWallet: Wallet | undefined; - let wp = openPromise(); - let httpLib = new AndroidHttpLib(sendMessage); - let walletArgs: DefaultNodeWalletArgs | undefined; - const onMessage = async (msgStr: any) => { - if (typeof msgStr !== "string") { - console.error("expected string as message"); - return; - } - const msg = JSON.parse(msgStr); - const operation = msg.operation; - if (typeof operation !== "string") { - console.error( - "message to android wallet helper must contain operation of type string", - ); - return; - } - const id = msg.id; - console.log(`android listener: got request for ${operation} (${id})`); - let result; + globalThis.__akono_sendMessage(m); +} + +class AndroidWalletMessageHandler { + walletArgs: DefaultNodeWalletArgs | undefined; + maybeWallet: Wallet | undefined; + wp = openPromise(); + httpLib = new NodeHttpLib(); + + /** + * Handle a request from the Android wallet. + */ + async handleMessage(operation: string, id: string, args: any): Promise { switch (operation) { case "init": { - walletArgs = { + this.walletArgs = { notifyHandler: async () => { - sendMessage(JSON.stringify({ type: "notification" })); + sendAkonoMessage(JSON.stringify({ type: "notification" })); }, - persistentStoragePath: msg.args.persistentStoragePath, - httpLib: httpLib, + persistentStoragePath: args.persistentStoragePath, + httpLib: this.httpLib, }; - const w = await getDefaultNodeWallet(walletArgs); - maybeWallet = w; + const w = await getDefaultNodeWallet(this.walletArgs); + this.maybeWallet = w; w.runRetryLoop().catch(e => { console.error("Error during wallet retry loop", e); }); - wp.resolve(w); - result = true; - break; + this.wp.resolve(w); + return {}; } case "getBalances": { - const wallet = await wp.promise; - result = await wallet.getBalances(); - break; + const wallet = await this.wp.promise; + return await wallet.getBalances(); } case "getPendingOperations": { - const wallet = await wp.promise; - result = await wallet.getPendingOperations(); - break; + const wallet = await this.wp.promise; + return await wallet.getPendingOperations(); } case "withdrawTestkudos": { - const wallet = await wp.promise; + const wallet = await this.wp.promise; try { await withdrawTestBalance(wallet); } catch (e) { console.log("error during withdrawTestBalance", e); } - result = {}; - break; + return {}; } case "getHistory": { - const wallet = await wp.promise; - result = await wallet.getHistory(); - break; + const wallet = await this.wp.promise; + return await wallet.getHistory(); } case "retryPendingNow": { - const wallet = await wp.promise; + const wallet = await this.wp.promise; await wallet.runPending(true); - result = {}; - break; + return {}; } case "preparePay": { - const wallet = await wp.promise; - result = await wallet.preparePay(msg.args.url); + const wallet = await this.wp.promise; + return await wallet.preparePay(args.url); break; } case "confirmPay": { - const wallet = await wp.promise; - result = await wallet.confirmPay( - msg.args.proposalId, - msg.args.sessionId, - ); - break; + const wallet = await this.wp.promise; + return await wallet.confirmPay(args.proposalId, args.sessionId); } case "startTunnel": { - httpLib.useNfcTunnel = true; - break; + // this.httpLib.useNfcTunnel = true; + throw Error("not implemented"); } case "stopTunnel": { - httpLib.useNfcTunnel = false; - break; + // this.httpLib.useNfcTunnel = false; + throw Error("not implemented"); } case "tunnelResponse": { - httpLib.handleTunnelResponse(msg.args); - break; + // httpLib.handleTunnelResponse(msg.args); + throw Error("not implemented"); } case "getWithdrawDetailsForUri": { - const wallet = await wp.promise; - result = await wallet.getWithdrawDetailsForUri( - msg.args.talerWithdrawUri, - msg.args.selectedExchange, + const wallet = await this.wp.promise; + return await wallet.getWithdrawDetailsForUri( + args.talerWithdrawUri, + args.selectedExchange, ); - break; } case "acceptExchangeTermsOfService": { - const wallet = await wp.promise; - result = await wallet.acceptExchangeTermsOfService( - msg.args.exchangeBaseUrl, - msg.args.etag, + const wallet = await this.wp.promise; + return await wallet.acceptExchangeTermsOfService( + args.exchangeBaseUrl, + args.etag, ); - break; } case "acceptWithdrawal": { - const wallet = await wp.promise; - result = await wallet.acceptWithdrawal( - msg.args.talerWithdrawUri, - msg.args.selectedExchange, + const wallet = await this.wp.promise; + return await wallet.acceptWithdrawal( + args.talerWithdrawUri, + args.selectedExchange, ); - break; } case "reset": { - const oldArgs = walletArgs; - walletArgs = { ...oldArgs }; + const oldArgs = this.walletArgs; + this.walletArgs = { ...oldArgs }; if (oldArgs && oldArgs.persistentStoragePath) { try { fs.unlinkSync(oldArgs.persistentStoragePath); @@ -258,31 +232,72 @@ export function installAndroidWalletListener() { console.error("Error while deleting the wallet db:", e); } // Prevent further storage! - walletArgs.persistentStoragePath = undefined; + this.walletArgs.persistentStoragePath = undefined; } - const wallet = await wp.promise; + const wallet = await this.wp.promise; wallet.stop(); - wp = openPromise(); - maybeWallet = undefined; - const w = await getDefaultNodeWallet(walletArgs); - maybeWallet = w; + this.wp = openPromise(); + this.maybeWallet = undefined; + const w = await getDefaultNodeWallet(this.walletArgs); + this.maybeWallet = w; w.runRetryLoop().catch(e => { console.error("Error during wallet retry loop", e); }); - wp.resolve(w); - result = {}; - break; + this.wp.resolve(w); + return {}; } default: - console.error(`operation "${operation}" not understood`); - return; + throw Error(`operation "${operation}" not understood`); } + } +} - console.log(`android listener: sending response for ${operation} (${id})`); +export function installAndroidWalletListener() { + // @ts-ignore + const sendMessage: (m: string) => void = globalThis.__akono_sendMessage; + if (typeof sendMessage !== "function") { + const errMsg = + "FATAL: cannot install android wallet listener: akono functions missing"; + console.error(errMsg); + throw new Error(errMsg); + } + const handler = new AndroidWalletMessageHandler(); + const onMessage = async (msgStr: any) => { + if (typeof msgStr !== "string") { + console.error("expected string as message"); + return; + } + const msg = JSON.parse(msgStr); + const operation = msg.operation; + if (typeof operation !== "string") { + console.error( + "message to android wallet helper must contain operation of type string", + ); + return; + } + const id = msg.id; + console.log(`android listener: got request for ${operation} (${id})`); - const respMsg = { result, id, operation, type: "response" }; - sendMessage(JSON.stringify(respMsg)); + try { + const result = await handler.handleMessage(operation, id, msg.args); + console.log( + `android listener: sending success response for ${operation} (${id})`, + ); + const respMsg = { type: "response", id, operation, isError: false, result }; + sendMessage(JSON.stringify(respMsg)); + } catch (e) { + const respMsg = { + type: "response", + id, + operation, + isError: true, + result: { message: e.toString() }, + }; + sendMessage(JSON.stringify(respMsg)); + return; + } }; + // @ts-ignore globalThis.__akono_onMessage = onMessage; -- cgit v1.2.3