aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-cli/src/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-cli/src/index.ts')
-rw-r--r--packages/taler-wallet-cli/src/index.ts163
1 files changed, 20 insertions, 143 deletions
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 67d0e3784..cce982dfb 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -60,13 +60,15 @@ import {
WalletCoreApiClient,
walletCoreDebugFlags,
} from "@gnu-taler/taler-wallet-core";
+
+import {
+ createRemoteWallet,
+ getClientFromRemoteWallet,
+ makeNotificationWaiter,
+} from "@gnu-taler/taler-wallet-core/remote";
import fs from "fs";
import os from "os";
-import {
- connectRpc,
- JsonMessage,
- runRpcServer,
-} from "@gnu-taler/taler-util/twrpc";
+import { JsonMessage, runRpcServer } from "@gnu-taler/taler-util/twrpc";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
@@ -280,162 +282,33 @@ async function createLocalWallet(
}
}
-export interface RemoteWallet {
- /**
- * Low-level interface for making API requests to wallet-core.
- */
- makeCoreApiRequest(
- operation: string,
- payload: unknown,
- ): Promise<CoreApiResponse>;
-
- /**
- * Close the connection to the remote wallet.
- */
- close(): void;
-}
-
-async function createRemoteWallet(
- notificationHandler?: (n: WalletNotification) => void,
-): Promise<RemoteWallet> {
- let nextRequestId = 1;
- let requestMap: Map<
- string,
- {
- promiseCapability: OpenedPromise<CoreApiResponse>;
- }
- > = new Map();
-
- const ctx = await connectRpc<RemoteWallet>({
- socketFilename: "wallet-core.sock",
- onEstablished(connection) {
- const ctx: RemoteWallet = {
- makeCoreApiRequest(operation, payload) {
- const id = `req-${nextRequestId}`;
- const req: CoreApiRequestEnvelope = {
- operation,
- id,
- args: payload,
- };
- const promiseCap = openPromise<CoreApiResponse>();
- requestMap.set(id, {
- promiseCapability: promiseCap,
- });
- connection.sendMessage(req as unknown as JsonMessage);
- return promiseCap.promise;
- },
- close() {
- connection.close();
- },
- };
- return {
- result: ctx,
- onDisconnect() {
- logger.info("remote wallet disconnected");
- },
- onMessage(m) {
- // FIXME: use a codec for parsing the response envelope!
-
- logger.info(`got message from remote wallet: ${j2s(m)}`);
- if (typeof m !== "object" || m == null) {
- logger.warn("message from wallet not understood (wrong type)");
- return;
- }
- const type = (m as any).type;
- if (type === "response" || type === "error") {
- const id = (m as any).id;
- if (typeof id !== "string") {
- logger.warn(
- "message from wallet not understood (no id in response)",
- );
- return;
- }
- const h = requestMap.get(id);
- if (!h) {
- logger.warn(`no handler registered for response id ${id}`);
- return;
- }
- h.promiseCapability.resolve(m as any);
- } else if (type === "notification") {
- logger.info("got notification");
- if (notificationHandler) {
- notificationHandler((m as any).payload);
- }
- } else {
- logger.warn("message from wallet not understood");
- }
- },
- };
- },
- });
- return ctx;
-}
-
-/**
- * Get a high-level API client from a remove wallet.
- */
-function getClientFromRemoteWallet(w: RemoteWallet): WalletCoreApiClient {
- const client: WalletCoreApiClient = {
- async call(op, payload): Promise<any> {
- const res = await w.makeCoreApiRequest(op, payload);
- switch (res.type) {
- case "error":
- throw TalerError.fromUncheckedDetail(res.error);
- case "response":
- return res.result;
- }
- },
- };
- return client;
-}
-
async function withWallet<T>(
walletCliArgs: WalletCliArgsType,
f: (ctx: WalletContext) => Promise<T>,
): Promise<T> {
- // Bookkeeping for waiting on notification conditions
- let nextCondIndex = 1;
- const condMap: Map<
- number,
- {
- condition: (n: WalletNotification) => boolean;
- promiseCapability: OpenedPromise<void>;
- }
- > = new Map();
- function onNotification(n: WalletNotification) {
- condMap.forEach((cond, condKey) => {
- if (cond.condition(n)) {
- cond.promiseCapability.resolve();
- }
- });
- }
- function waitForNotificationCond(cond: (n: WalletNotification) => boolean) {
- const promCap = openPromise<void>();
- condMap.set(nextCondIndex++, {
- condition: cond,
- promiseCapability: promCap,
- });
- return promCap.promise;
- }
+ const waiter = makeNotificationWaiter();
if (walletCliArgs.wallet.walletConnection) {
logger.info("creating remote wallet");
- const w = await createRemoteWallet(onNotification);
+ const w = await createRemoteWallet({
+ notificationHandler: waiter.notify,
+ socketFilename: walletCliArgs.wallet.walletConnection,
+ });
const ctx: WalletContext = {
makeCoreApiRequest(operation, payload) {
return w.makeCoreApiRequest(operation, payload);
},
client: getClientFromRemoteWallet(w),
- waitForNotificationCond,
+ waitForNotificationCond: waiter.waitForNotificationCond,
};
const res = await f(ctx);
w.close();
return res;
} else {
- const w = await createLocalWallet(walletCliArgs, onNotification);
+ const w = await createLocalWallet(walletCliArgs, waiter.notify);
const ctx: WalletContext = {
client: w.client,
- waitForNotificationCond,
+ waitForNotificationCond: waiter.waitForNotificationCond,
makeCoreApiRequest(operation, payload) {
return w.handleCoreApiRequest(operation, "my-req", payload);
},
@@ -1053,7 +926,11 @@ advancedCli
.subcommand("serve", "serve", {
help: "Serve the wallet API via a unix domain socket.",
})
+ .requiredOption("unixPath", ["--unix-path"], clk.STRING, {
+ default: "wallet-core.sock",
+ })
.action(async (args) => {
+ logger.info(`serving at ${args.serve.unixPath}`);
const w = await createLocalWallet(args);
w.runTaskLoop()
.then((res) => {
@@ -1070,7 +947,7 @@ advancedCli
});
});
await runRpcServer({
- socketFilename: "wallet-core.sock",
+ socketFilename: args.serve.unixPath,
onConnect(client) {
logger.info("connected");
const clientId = nextClientId++;