commit 16becb0e5daa1b87bf5f0fdf7c87156a61022427
parent 8665bf97a5f673e9201c5fdd44850838fb60f090
Author: Florian Dold <dold@taler.net>
Date: Mon, 10 Aug 2026 00:45:39 +0200
wallet-cli: pretty-print bank accounts
Diffstat:
3 files changed, 151 insertions(+), 10 deletions(-)
diff --git a/packages/taler-wallet-cli/src/bank-accounts-pretty.test.ts b/packages/taler-wallet-cli/src/bank-accounts-pretty.test.ts
@@ -0,0 +1,61 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ 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.
+
+ 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
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { WalletBankAccountInfo } from "@gnu-taler/taler-util";
+import assert from "node:assert";
+import { test } from "node:test";
+import { formatPrettyBankAccount } from "./bank-accounts-pretty.js";
+
+function makeAccount(
+ extra: Record<string, unknown> = {},
+): WalletBankAccountInfo {
+ return {
+ bankAccountId: "account-1",
+ label: "Savings",
+ paytoUri: "payto://iban/DE123",
+ currencies: ["KUDOS", "EUR"],
+ kycCompleted: true,
+ ...extra,
+ } as WalletBankAccountInfo;
+}
+
+test("pretty bank account output is compact and useful", () => {
+ assert.deepStrictEqual(formatPrettyBankAccount(makeAccount()), [
+ "Savings payto://iban/DE123 [KUDOS, EUR]",
+ ]);
+});
+
+test("pretty bank account output has a fallback label", () => {
+ assert.deepStrictEqual(
+ formatPrettyBankAccount(
+ makeAccount({ label: undefined, currencies: undefined }),
+ ),
+ ["(unlabelled) payto://iban/DE123 [unknown currencies]"],
+ );
+});
+
+test("verbose bank account output includes diagnostic details", () => {
+ const lines = formatPrettyBankAccount(makeAccount(), true);
+
+ assert.ok(lines.includes(" ID: account-1"));
+ assert.ok(lines.includes(" KYC completed: yes"));
+});
+
+test("the one-line view omits verbose details", () => {
+ assert.deepStrictEqual(formatPrettyBankAccount(makeAccount(), true, true), [
+ "Savings payto://iban/DE123 [KUDOS, EUR]",
+ ]);
+});
diff --git a/packages/taler-wallet-cli/src/bank-accounts-pretty.ts b/packages/taler-wallet-cli/src/bank-accounts-pretty.ts
@@ -0,0 +1,39 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ 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.
+
+ 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
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file
+ * Compact, useful rendering of known bank accounts.
+ */
+
+import { WalletBankAccountInfo } from "@gnu-taler/taler-util";
+
+/** Render one known bank account for a person reading the CLI output. */
+export function formatPrettyBankAccount(
+ account: WalletBankAccountInfo,
+ verbose = false,
+ oneline = false,
+): string[] {
+ const label = account.label ?? "(unlabelled)";
+ const currencies = account.currencies?.join(", ") ?? "unknown currencies";
+ const lines = [`${label} ${account.paytoUri} [${currencies}]`];
+
+ if (oneline || !verbose) return lines;
+
+ lines.push(` ID: ${account.bankAccountId}`);
+ lines.push(` KYC completed: ${account.kycCompleted ? "yes" : "no"}`);
+ return lines;
+}
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
@@ -105,6 +105,7 @@ import {
} from "./waitspec.js";
import { formatPrettyTransaction } from "./transactions-pretty.js";
import { formatPrettyBalance } from "./balance-pretty.js";
+import { formatPrettyBankAccount } from "./bank-accounts-pretty.js";
import * as fs from "node:fs";
@@ -2891,23 +2892,63 @@ backupCli.subcommand("importDb", "import-db").action(async (args) => {
});
});
-const bankAccountsCli = walletCli.subcommand(
- "bankAccountArgs",
- "bank-accounts",
- {
- help: "Subcommands for managing known bank accounts.",
- },
-);
+const bankAccountsCli = walletCli
+ .subcommand("bankAccountArgs", "bank-accounts", {
+ help: [
+ "Manage known bank accounts; subcommands:",
+ "add, delete, list",
+ ].join("\n"),
+ })
+ .flag("json", ["--json"], {
+ help: "Print JSON, even when stdout is a terminal.",
+ })
+ .flag("pretty", ["--pretty"], {
+ help: "Print a human-readable account list, even when stdout is not a terminal.",
+ })
+ .flag("verbose", ["-v", "--verbose"], {
+ help: "Include account IDs and diagnostic details in pretty output.",
+ })
+ .flag("oneline", ["--oneline"], {
+ help: "Print one compact line per account in pretty output.",
+ });
-bankAccountsCli.subcommand("listArgs", "list").action(async (args) => {
+async function listKnownBankAccounts(args: any): Promise<void> {
await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.ListBankAccounts,
{},
);
- console.log(`Bank accounts: ${j2s(resp)}`);
+ // A terminal gets a useful overview, while pipes retain the stable
+ // machine-readable form. Either flag explicitly selects its format;
+ // --json wins if a caller accidentally passes both.
+ const pretty =
+ !args.bankAccountArgs.json &&
+ (args.bankAccountArgs.pretty || process.stdout.isTTY === true);
+ if (!pretty) {
+ console.log(JSON.stringify(resp, undefined, 2));
+ return;
+ }
+ if (resp.accounts.length === 0) {
+ console.log("No bank accounts.");
+ return;
+ }
+ const verbose = args.bankAccountArgs.verbose || args.wallet.verbose;
+ const rendered = resp.accounts.map((account) =>
+ formatPrettyBankAccount(account, verbose, args.bankAccountArgs.oneline),
+ );
+ console.log(rendered.map((lines) => lines.join("\n")).join("\n\n"));
});
-});
+}
+
+// Listing is the default action, matching the transaction command. Keeping
+// the explicit subcommand makes existing invocations continue to work.
+bankAccountsCli.action(listKnownBankAccounts);
+
+bankAccountsCli
+ .subcommand("listArgs", "list", {
+ help: "List known bank accounts.",
+ })
+ .action(listKnownBankAccounts);
bankAccountsCli
.subcommand("addBankAccountArgs", "add", {