diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-11-19 16:16:12 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-11-19 16:16:12 +0100 |
commit | d9297f3dfddd5c7b072b46dee984251e3202ad75 (patch) | |
tree | 09e6f9e9e169e554d966b8a805f9fdf6a77ca2f3 /src/headless/taler-wallet-cli.ts | |
parent | 87aa0f65c358773392d0c33c82fa040d466845a9 (diff) | |
download | wallet-core-d9297f3dfddd5c7b072b46dee984251e3202ad75.tar.xz |
work on CLI
Diffstat (limited to 'src/headless/taler-wallet-cli.ts')
-rw-r--r-- | src/headless/taler-wallet-cli.ts | 430 |
1 files changed, 244 insertions, 186 deletions
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts index 8c31e67d8..41f68319a 100644 --- a/src/headless/taler-wallet-cli.ts +++ b/src/headless/taler-wallet-cli.ts @@ -14,32 +14,68 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import commander = require("commander"); import os = require("os"); import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; import { MerchantBackendConnection } from "./merchant"; import { runIntegrationTest } from "./integrationtest"; import { Wallet } from "../wallet"; -import querystring = require("querystring"); import qrcodeGenerator = require("qrcode-generator"); -import readline = require("readline"); - -const program = new commander.Command(); -program.version("0.0.1").option("--verbose", "enable verbose output", false); +import * as clk from "./clk"; const walletDbPath = os.homedir + "/" + ".talerwalletdb.json"; -function prompt(question: string): Promise<string> { - const stdinReadline = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise<string>((resolve, reject) => { - stdinReadline.question(question, res => { - resolve(res); - stdinReadline.close(); - }); - }); +async function doPay( + wallet: Wallet, + payUrl: string, + options: { alwaysYes: boolean } = { alwaysYes: true }, +) { + const result = await wallet.preparePay(payUrl); + if (result.status === "error") { + console.error("Could not pay:", result.error); + process.exit(1); + return; + } + if (result.status === "insufficient-balance") { + console.log("contract", result.contractTerms!); + console.error("insufficient balance"); + process.exit(1); + return; + } + if (result.status === "paid") { + console.log("already paid!"); + process.exit(0); + return; + } + if (result.status === "payment-possible") { + console.log("paying ..."); + } else { + throw Error("not reached"); + } + console.log("contract", result.contractTerms!); + let pay; + if (options.alwaysYes) { + pay = true; + } else { + while (true) { + const yesNoResp = (await clk.prompt("Pay? [Y/n]")).toLowerCase(); + if (yesNoResp === "" || yesNoResp === "y" || yesNoResp === "yes") { + pay = true; + break; + } else if (yesNoResp === "n" || yesNoResp === "no") { + pay = false; + break; + } else { + console.log("please answer y/n"); + } + } + } + + if (pay) { + const payRes = await wallet.confirmPay(result.proposalId!, undefined); + console.log("paid!"); + } else { + console.log("not paying"); + } } function applyVerbose(verbose: boolean) { @@ -49,31 +85,57 @@ function applyVerbose(verbose: boolean) { } } -program - .command("test-withdraw") - .option( - "-e, --exchange <exchange-url>", - "exchange base URL", - "https://exchange.test.taler.net/", - ) - .option("-a, --amount <withdraw-amt>", "amount to withdraw", "TESTKUDOS:10") - .option("-b, --bank <bank-url>", "bank base URL", "https://bank.test.taler.net/") - .description("withdraw test currency from the test bank") - .action(async cmdObj => { - applyVerbose(program.verbose); - console.log("test-withdraw command called"); +const walletCli = clk + .program("wallet", { + help: "Command line interface for the GNU Taler wallet.", + }) + .maybeOption("inhibit", ["--inhibit"], clk.STRING, { + help: + "Inhibit running certain operations, useful for debugging and testing.", + }) + .flag("verbose", ["-V", "--verbose"], { + help: "Enable verbose output.", + }); + +walletCli + .subcommand("testPayCmd", "test-pay", { help: "create contract and pay" }) + .requiredOption("amount", ["-a", "--amount"], clk.STRING) + .requiredOption("summary", ["-s", "--summary"], clk.STRING, { + default: "Test Payment", + }) + .action(async args => { + const cmdArgs = args.testPayCmd; + console.log("creating order"); + const merchantBackend = new MerchantBackendConnection( + "https://backend.test.taler.net/", + "sandbox", + ); + const orderResp = await merchantBackend.createOrder( + cmdArgs.amount, + cmdArgs.summary, + "", + ); + console.log("created new order with order ID", orderResp.orderId); + const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); + const talerPayUri = checkPayResp.taler_pay_uri; + if (!talerPayUri) { + console.error("fatal: no taler pay URI received from backend"); + process.exit(1); + return; + } + console.log("taler pay URI:", talerPayUri); + const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, }); - await withdrawTestBalance(wallet, cmdObj.amount, cmdObj.bank, cmdObj.exchange); - process.exit(0); + + await doPay(wallet, talerPayUri, { alwaysYes: true }); }); -program - .command("balance") - .description("show wallet balance") - .action(async () => { - applyVerbose(program.verbose); +walletCli + .subcommand("", "balance", { help: "Show wallet balance." }) + .action(async args => { + applyVerbose(args.wallet.verbose); console.log("balance command called"); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, @@ -84,12 +146,14 @@ program process.exit(0); }); - -program - .command("history") - .description("show wallet history") - .action(async () => { - applyVerbose(program.verbose); +walletCli + .subcommand("", "history", { help: "Show wallet event history." }) + .requiredOption("from", ["--from"], clk.STRING) + .requiredOption("to", ["--to"], clk.STRING) + .requiredOption("limit", ["--limit"], clk.STRING) + .requiredOption("contEvt", ["--continue-with"], clk.STRING) + .action(async args => { + applyVerbose(args.wallet.verbose); console.log("history command called"); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, @@ -100,26 +164,45 @@ program process.exit(0); }); +walletCli + .subcommand("", "pending", { help: "Show pending operations." }) + .action(async args => { + applyVerbose(args.wallet.verbose); + console.log("history command called"); + const wallet = await getDefaultNodeWallet({ + persistentStoragePath: walletDbPath, + }); + console.log("got wallet"); + const pending = await wallet.getPendingOperations(); + console.log(JSON.stringify(pending, undefined, 2)); + process.exit(0); + }); + async function asyncSleep(milliSeconds: number): Promise<void> { return new Promise<void>((resolve, reject) => { setTimeout(() => resolve(), milliSeconds); }); } -program - .command("test-merchant-qrcode") - .option("-a, --amount <spend-amt>", "amount to spend", "TESTKUDOS:1") - .option("-s, --summary <summary>", "contract summary", "Test Payment") - .action(async cmdObj => { - applyVerbose(program.verbose); +walletCli + .subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode") + .requiredOption("amount", ["-a", "--amount"], clk.STRING, { + default: "TESTKUDOS:1", + }) + .requiredOption("summary", ["-s", "--summary"], clk.STRING, { + default: "Test Payment", + }) + .action(async args => { + const cmdArgs = args.testMerchantQrcodeCmd; + applyVerbose(args.wallet.verbose); console.log("creating order"); const merchantBackend = new MerchantBackendConnection( "https://backend.test.taler.net/", "sandbox", ); const orderResp = await merchantBackend.createOrder( - cmdObj.amount, - cmdObj.summary, + cmdArgs.amount, + cmdArgs.summary, "", ); console.log("created new order with order ID", orderResp.orderId); @@ -148,10 +231,59 @@ program } }); -program - .command("withdraw-uri <withdraw-uri>") - .action(async (withdrawUrl, cmdObj) => { - applyVerbose(program.verbose); +walletCli + .subcommand("integrationtestCmd", "integrationtest", { + help: "Run integration test with bank, exchange and merchant.", + }) + .requiredOption("exchange", ["-e", "--exchange"], clk.STRING, { + default: "https://exchange.test.taler.net/", + }) + .requiredOption("merchant", ["-m", "--merchant"], clk.STRING, { + default: "https://backend.test.taler.net/", + }) + .requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, { + default: "sandbox", + }) + .requiredOption("bank", ["-b", "--bank"], clk.STRING, { + default: "https://bank.test.taler.net/", + }) + .requiredOption("withdrawAmount", ["-b", "--bank"], clk.STRING, { + default: "TESTKUDOS:10", + }) + .requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, { + default: "TESTKUDOS:4", + }) + .action(async args => { + applyVerbose(args.wallet.verbose); + let cmdObj = args.integrationtestCmd; + + try { + await runIntegrationTest({ + amountToSpend: cmdObj.spendAmount, + amountToWithdraw: cmdObj.withdrawAmount, + bankBaseUrl: cmdObj.bank, + exchangeBaseUrl: cmdObj.exchange, + merchantApiKey: cmdObj.merchantApiKey, + merchantBaseUrl: cmdObj.merchant, + }).catch(err => { + console.error("Failed with exception:"); + console.error(err); + }); + + process.exit(0); + } catch (e) { + console.error(e); + process.exit(1); + } + }); + +walletCli + .subcommand("withdrawUriCmd", "withdraw-uri") + .argument("withdrawUri", clk.STRING) + .action(async args => { + applyVerbose(args.wallet.verbose); + const cmdArgs = args.withdrawUriCmd; + const withdrawUrl = cmdArgs.withdrawUri; console.log("withdrawing", withdrawUrl); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, @@ -168,10 +300,7 @@ program return; } - const { - reservePub, - confirmTransferUrl, - } = await wallet.acceptWithdrawal( + const { reservePub, confirmTransferUrl } = await wallet.acceptWithdrawal( withdrawUrl, selectedExchange, ); @@ -187,10 +316,12 @@ program wallet.stop(); }); -program - .command("tip-uri <tip-uri>") - .action(async (tipUri, cmdObj) => { - applyVerbose(program.verbose); +walletCli + .subcommand("tipUriCmd", "tip-uri") + .argument("uri", clk.STRING) + .action(async args => { + applyVerbose(args.wallet.verbose); + const tipUri = args.tipUriCmd.uri; console.log("getting tip", tipUri); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, @@ -201,12 +332,12 @@ program wallet.stop(); }); - - - program - .command("refund-uri <refund-uri>") - .action(async (refundUri, cmdObj) => { - applyVerbose(program.verbose); +walletCli + .subcommand("refundUriCmd", "refund-uri") + .argument("uri", clk.STRING) + .action(async args => { + applyVerbose(args.wallet.verbose); + const refundUri = args.refundUriCmd.uri; console.log("getting refund", refundUri); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, @@ -215,131 +346,58 @@ program wallet.stop(); }); -program - .command("pay-uri <pay-uri") - .option("-y, --yes", "automatically answer yes to prompts") - .action(async (payUrl, cmdObj) => { - applyVerbose(program.verbose); +const exchangesCli = walletCli + .subcommand("exchangesCmd", "exchanges", { + help: "Manage exchanges." + }); + +exchangesCli.subcommand("exchangesListCmd", "list", { + help: "List known exchanges." +}); + +exchangesCli.subcommand("exchangesListCmd", "update"); + +walletCli + .subcommand("payUriCmd", "pay-uri") + .argument("url", clk.STRING) + .flag("autoYes", ["-y", "--yes"]) + .action(async args => { + applyVerbose(args.wallet.verbose); + const payUrl = args.payUriCmd.url; console.log("paying for", payUrl); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, }); - const result = await wallet.preparePay(payUrl); - if (result.status === "error") { - console.error("Could not pay:", result.error); - process.exit(1); - return; - } - if (result.status === "insufficient-balance") { - console.log("contract", result.contractTerms!); - console.error("insufficient balance"); - process.exit(1); - return; - } - if (result.status === "paid") { - console.log("already paid!"); - process.exit(0); - return; - } - if (result.status === "payment-possible") { - console.log("paying ..."); - } else { - throw Error("not reached"); - } - console.log("contract", result.contractTerms!); - let pay; - if (cmdObj.yes) { - pay = true; - } else { - while (true) { - const yesNoResp = (await prompt("Pay? [Y/n]")).toLowerCase(); - if (yesNoResp === "" || yesNoResp === "y" || yesNoResp === "yes") { - pay = true; - break; - } else if (yesNoResp === "n" || yesNoResp === "no") { - pay = false; - break; - } else { - console.log("please answer y/n"); - } - } - } - - if (pay) { - const payRes = await wallet.confirmPay(result.proposalId!, undefined); - console.log("paid!"); - } else { - console.log("not paying"); - } + await doPay(wallet, payUrl, { alwaysYes: args.payUriCmd.autoYes }); wallet.stop(); }); -program - .command("integrationtest") - .option( - "-e, --exchange <exchange-url>", - "exchange base URL", - "https://exchange.test.taler.net/", - ) - .option( - "-m, --merchant <merchant-url>", - "merchant base URL", - "https://backend.test.taler.net/", - ) - .option( - "-k, --merchant-api-key <merchant-api-key>", - "merchant API key", - "sandbox", - ) - .option( - "-b, --bank <bank-url>", - "bank base URL", - "https://bank.test.taler.net/", - ) - .option( - "-w, --withdraw-amount <withdraw-amt>", - "amount to withdraw", - "TESTKUDOS:10", - ) - .option("-s, --spend-amount <spend-amt>", "amount to spend", "TESTKUDOS:4") - .description("Run integration test with bank, exchange and merchant.") - .action(async cmdObj => { - applyVerbose(program.verbose); - - try { - await runIntegrationTest({ - amountToSpend: cmdObj.spendAmount, - amountToWithdraw: cmdObj.withdrawAmount, - bankBaseUrl: cmdObj.bank, - exchangeBaseUrl: cmdObj.exchange, - merchantApiKey: cmdObj.merchantApiKey, - merchantBaseUrl: cmdObj.merchant, - }).catch(err => { - console.error("Failed with exception:"); - console.error(err); - }); - - process.exit(0); - } catch (e) { - console.error(e); - process.exit(1); - } - - }); - -// error on unknown commands -program.on("command:*", function() { - console.error( - "Invalid command: %s\nSee --help for a list of available commands.", - program.args.join(" "), - ); - process.exit(1); +const testCli = walletCli.subcommand("testingArgs", "testing", { + help: "Subcommands for testing GNU Taler deployments." }); -program.parse(process.argv); +testCli + .subcommand("withdrawArgs", "withdraw", { + help: "Withdraw from a test bank (must support test registrations).", + }) + .requiredOption("exchange", ["-e", "--exchange"], clk.STRING, { + default: "https://exchange.test.taler.net/", + help: "Exchange base URL.", + }) + .requiredOption("bank", ["-b", "--bank"], clk.STRING, { + default: "https://bank.test.taler.net/", + help: "Bank base URL", + }) + .action(async args => { + applyVerbose(args.wallet.verbose); + console.log("balance command called"); + const wallet = await getDefaultNodeWallet({ + persistentStoragePath: walletDbPath, + }); + console.log("got wallet"); + const balance = await wallet.getBalances(); + console.log(JSON.stringify(balance, undefined, 2)); + }); -if (process.argv.length <= 2) { - console.error("Error: No command given."); - program.help(); -} +walletCli.run(); |