aboutsummaryrefslogtreecommitdiff
path: root/src/headless
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-11-20 19:48:43 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-11-20 19:48:43 +0100
commit553da649902f71d5ca34c9a6289ab6b1ef0ba7cb (patch)
tree857c4eb2c39e4a92e71c8a623d3188e6dbbbd1e9 /src/headless
parentfaedf697626dd37f3ac74ad4cac1ec378598bbf3 (diff)
downloadwallet-core-553da649902f71d5ca34c9a6289ab6b1ef0ba7cb.tar.xz
WIP: simplify DB queries and error handling
Diffstat (limited to 'src/headless')
-rw-r--r--src/headless/clk.ts64
-rw-r--r--src/headless/taler-wallet-cli.ts108
2 files changed, 128 insertions, 44 deletions
diff --git a/src/headless/clk.ts b/src/headless/clk.ts
index 642a1bef3..f66d609e8 100644
--- a/src/headless/clk.ts
+++ b/src/headless/clk.ts
@@ -20,7 +20,6 @@
import process = require("process");
import path = require("path");
import readline = require("readline");
-import { symlinkSync } from "fs";
class Converter<T> {}
@@ -54,6 +53,7 @@ interface ArgumentDef {
name: string;
conv: Converter<any>;
args: ArgumentArgs<any>;
+ required: boolean;
}
interface SubcommandDef {
@@ -181,7 +181,7 @@ export class CommandGroup<GN extends keyof any, TG> {
return this as any;
}
- argument<N extends keyof any, V>(
+ requiredArgument<N extends keyof any, V>(
name: N,
conv: Converter<V>,
args: ArgumentArgs<V> = {},
@@ -190,6 +190,22 @@ export class CommandGroup<GN extends keyof any, TG> {
args: args,
conv: conv,
name: name as string,
+ required: true,
+ };
+ this.arguments.push(argDef);
+ return this as any;
+ }
+
+ maybeArgument<N extends keyof any, V>(
+ name: N,
+ conv: Converter<V>,
+ args: ArgumentArgs<V> = {},
+ ): CommandGroup<GN, TG & SubRecord<GN, N, V | undefined>> {
+ const argDef: ArgumentDef = {
+ args: args,
+ conv: conv,
+ name: name as string,
+ required: false,
};
this.arguments.push(argDef);
return this as any;
@@ -401,10 +417,25 @@ export class CommandGroup<GN extends keyof any, TG> {
process.exit(-1);
throw Error("not reached");
}
+ myArgs[d.name] = unparsedArgs[i];
posArgIndex++;
}
}
+ for (let i = posArgIndex; i < this.arguments.length; i++) {
+ const d = this.arguments[i];
+ const n = this.name ?? progname;
+ if (d.required) {
+ if (d.args.default !== undefined) {
+ myArgs[d.name] = d.args.default;
+ } else {
+ console.error(`error: missing positional argument '${d.name}' for ${n}`);
+ process.exit(-1);
+ throw Error("not reached");
+ }
+ }
+ }
+
for (let option of this.options) {
if (option.isFlag == false && option.required == true) {
if (!foundOptions[option.name]) {
@@ -433,9 +464,7 @@ export class CommandGroup<GN extends keyof any, TG> {
unparsedArgs.slice(i + 1),
parsedArgs,
);
- }
-
- if (this.myAction) {
+ } else if (this.myAction) {
this.myAction(parsedArgs);
} else {
this.printHelp(progname, parents);
@@ -513,18 +542,35 @@ export class Program<PN extends keyof any, T> {
}
/**
- * Add a positional argument to the program.
+ * Add a required positional argument to the program.
*/
- argument<N extends keyof any, V>(
+ requiredArgument<N extends keyof any, V>(
name: N,
conv: Converter<V>,
args: ArgumentArgs<V> = {},
): Program<N, T & SubRecord<PN, N, V>> {
- this.mainCommand.argument(name, conv, args);
+ this.mainCommand.requiredArgument(name, conv, args);
+ return this as any;
+ }
+
+ /**
+ * Add an optional argument to the program.
+ */
+ maybeArgument<N extends keyof any, V>(
+ name: N,
+ conv: Converter<V>,
+ args: ArgumentArgs<V> = {},
+ ): Program<N, T & SubRecord<PN, N, V | undefined>> {
+ this.mainCommand.maybeArgument(name, conv, args);
return this as any;
}
}
+export type GetArgType<T> =
+ T extends Program<any, infer AT> ? AT :
+ T extends CommandGroup<any, infer AT> ? AT :
+ any;
+
export function program<PN extends keyof any>(
argKey: PN,
args: ProgramArgs = {},
@@ -532,6 +578,8 @@ export function program<PN extends keyof any>(
return new Program(argKey as string, args);
}
+
+
export function prompt(question: string): Promise<string> {
const stdinReadline = readline.createInterface({
input: process.stdin,
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 41f68319a..06235d0b4 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -97,6 +97,28 @@ const walletCli = clk
help: "Enable verbose output.",
});
+type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
+
+async function withWallet<T>(
+ walletCliArgs: WalletCliArgsType,
+ f: (w: Wallet) => Promise<T>,
+): Promise<T> {
+ applyVerbose(walletCliArgs.wallet.verbose);
+ const wallet = await getDefaultNodeWallet({
+ persistentStoragePath: walletDbPath,
+ });
+ try {
+ await wallet.fillDefaults();
+ const ret = await f(wallet);
+ return ret;
+ } catch (e) {
+ console.error("caught exception:", e);
+ process.exit(1);
+ } finally {
+ wallet.stop();
+ }
+}
+
walletCli
.subcommand("testPayCmd", "test-pay", { help: "create contract and pay" })
.requiredOption("amount", ["-a", "--amount"], clk.STRING)
@@ -135,15 +157,11 @@ walletCli
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,
+ withWallet(args, async (wallet) => {
+ const balance = await wallet.getBalances();
+ console.log(JSON.stringify(balance, undefined, 2));
});
- console.log("got wallet");
- const balance = await wallet.getBalances();
- console.log(JSON.stringify(balance, undefined, 2));
- process.exit(0);
});
walletCli
@@ -153,29 +171,19 @@ walletCli
.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,
+ withWallet(args, async (wallet) => {
+ const history = await wallet.getHistory();
+ console.log(JSON.stringify(history, undefined, 2));
});
- console.log("got wallet");
- const history = await wallet.getHistory();
- console.log(JSON.stringify(history, undefined, 2));
- 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,
+ withWallet(args, async (wallet) => {
+ const pending = await wallet.getPendingOperations();
+ console.log(JSON.stringify(pending, undefined, 2));
});
- 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> {
@@ -185,6 +193,16 @@ async function asyncSleep(milliSeconds: number): Promise<void> {
}
walletCli
+ .subcommand("runPendingOpt", "run-pending", {
+ help: "Run pending operations."
+ })
+ .action(async (args) => {
+ withWallet(args, async (wallet) => {
+ await wallet.processPending();
+ });
+ });
+
+walletCli
.subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode")
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
default: "TESTKUDOS:1",
@@ -279,7 +297,7 @@ walletCli
walletCli
.subcommand("withdrawUriCmd", "withdraw-uri")
- .argument("withdrawUri", clk.STRING)
+ .requiredArgument("withdrawUri", clk.STRING)
.action(async args => {
applyVerbose(args.wallet.verbose);
const cmdArgs = args.withdrawUriCmd;
@@ -318,7 +336,7 @@ walletCli
walletCli
.subcommand("tipUriCmd", "tip-uri")
- .argument("uri", clk.STRING)
+ .requiredArgument("uri", clk.STRING)
.action(async args => {
applyVerbose(args.wallet.verbose);
const tipUri = args.tipUriCmd.uri;
@@ -334,7 +352,7 @@ walletCli
walletCli
.subcommand("refundUriCmd", "refund-uri")
- .argument("uri", clk.STRING)
+ .requiredArgument("uri", clk.STRING)
.action(async args => {
applyVerbose(args.wallet.verbose);
const refundUri = args.refundUriCmd.uri;
@@ -346,20 +364,38 @@ walletCli
wallet.stop();
});
-const exchangesCli = walletCli
- .subcommand("exchangesCmd", "exchanges", {
- help: "Manage exchanges."
- });
-
-exchangesCli.subcommand("exchangesListCmd", "list", {
- help: "List known exchanges."
+const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
+ help: "Manage exchanges.",
});
-exchangesCli.subcommand("exchangesListCmd", "update");
+exchangesCli
+ .subcommand("exchangesListCmd", "list", {
+ help: "List known exchanges.",
+ })
+ .action(async args => {
+ console.log("Listing exchanges ...");
+ withWallet(args, async (wallet) => {
+ const exchanges = await wallet.getExchanges();
+ console.log("exchanges", exchanges);
+ });
+ });
+
+exchangesCli
+ .subcommand("exchangesUpdateCmd", "update", {
+ help: "Update or add an exchange by base URL.",
+ })
+ .requiredArgument("url", clk.STRING, {
+ help: "Base URL of the exchange.",
+ })
+ .action(async args => {
+ withWallet(args, async (wallet) => {
+ const res = await wallet.updateExchangeFromUrl(args.exchangesUpdateCmd.url);
+ });
+ });
walletCli
.subcommand("payUriCmd", "pay-uri")
- .argument("url", clk.STRING)
+ .requiredArgument("url", clk.STRING)
.flag("autoYes", ["-y", "--yes"])
.action(async args => {
applyVerbose(args.wallet.verbose);
@@ -374,7 +410,7 @@ walletCli
});
const testCli = walletCli.subcommand("testingArgs", "testing", {
- help: "Subcommands for testing GNU Taler deployments."
+ help: "Subcommands for testing GNU Taler deployments.",
});
testCli