taler-typescript-core

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

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:
Apackages/taler-wallet-cli/src/bank-accounts-pretty.test.ts | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackages/taler-wallet-cli/src/bank-accounts-pretty.ts | 39+++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-cli/src/index.ts | 61+++++++++++++++++++++++++++++++++++++++++++++++++++----------
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", {