libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit ff823ec830c93a6616181e0ee52ed0c81f6c9326
parent 3de9d180d8212718a7a150f2a42854d9b3eed16d
Author: Antoine A <>
Date:   Fri, 18 Jul 2025 14:23:26 +0200

common: refactor CLI options

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/cli/BenchPwh.kt | 11++++-------
Mbank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt | 12+++++-------
Mbank/src/main/kotlin/tech/libeufin/bank/cli/CreateAccount.kt | 7+++----
Mbank/src/main/kotlin/tech/libeufin/bank/cli/CreateToken.kt | 10+++++-----
Mbank/src/main/kotlin/tech/libeufin/bank/cli/DbInit.kt | 10++++------
Mbank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt | 7+++----
Mbank/src/main/kotlin/tech/libeufin/bank/cli/Gc.kt | 13+++++--------
Mbank/src/main/kotlin/tech/libeufin/bank/cli/Serve.kt | 13+++++--------
Mcommon/src/main/kotlin/Cli.kt | 58+++++++++++++++++++++++++++-------------------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/DbInit.kt | 12+++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt | 9+++------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt | 7+++----
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt | 10+++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/InitiatePayment.kt | 9++++-----
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt | 8++++++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/Manual.kt | 23++++++++++-------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/Serve.kt | 12+++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt | 36+++++++++++++++---------------------
18 files changed, 115 insertions(+), 152 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/BenchPwh.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/BenchPwh.kt @@ -24,17 +24,14 @@ import com.github.ajalt.clikt.core.Context import com.github.ajalt.clikt.parameters.groups.provideDelegate import tech.libeufin.bank.bankConfig import tech.libeufin.bank.logger -import tech.libeufin.common.CommonOption -import tech.libeufin.common.cliCmd +import tech.libeufin.common.TalerCmd import tech.libeufin.common.crypto.PwCrypto -class BenchPwh : CliktCommand("bench-pwh") { +class BenchPwh : TalerCmd("bench-pwh") { override fun help(context: Context) = "Benchmark password hashing algorithm and configuration" - private val common by CommonOption() - - override fun run() = cliCmd(logger, common.log) { - val pwCrypto = bankConfig(common.config).pwCrypto + override fun run() = cliCmd(logger) { + val pwCrypto = bankConfig(config).pwCrypto when (pwCrypto) { is PwCrypto.Bcrypt -> println("Benching bcrypt with cost=${pwCrypto.cost} for 10s") diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -28,15 +28,13 @@ import tech.libeufin.bank.bankConfig import tech.libeufin.bank.db.AccountDAO.AccountPatchAuthResult import tech.libeufin.bank.logger import tech.libeufin.bank.withDb -import tech.libeufin.common.CommonOption -import tech.libeufin.common.cliCmd +import tech.libeufin.common.TalerCmd import tech.libeufin.common.crypto.checkPw import com.github.ajalt.mordant.terminal.* -class ChangePw : CliktCommand("passwd") { +class ChangePw : TalerCmd("passwd") { override fun help(context: Context) = "Change account password" - private val common by CommonOption() private val username by argument("username", help = "Account username") private val password by argument( "password", @@ -60,8 +58,8 @@ class ChangePw : CliktCommand("passwd") { } ).ask()!! } - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> val password = password.checkPw(cfg.pwdCheckQuality) val res = db.account.reconfigPassword(username, password, null, true, cfg.pwCrypto) when (res) { diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/CreateAccount.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/CreateAccount.kt @@ -70,15 +70,14 @@ class CreateAccountOption: OptionGroup() { ).convert { TanChannel.valueOf(it) } } -class CreateAccount : CliktCommand("create-account") { +class CreateAccount : TalerCmd("create-account") { override fun help(context: Context) = "Create an account, returning the payto://-URI associated with it" - private val common by CommonOption() private val json by argument().convert { Json.decodeFromString<RegisterAccountRequest>(it) }.optional() private val options by CreateAccountOption().cooccurring() - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> val req = json ?: options?.run { RegisterAccountRequest( username = username, diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/CreateToken.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/CreateToken.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -32,9 +32,9 @@ import java.time.* import java.time.temporal.ChronoUnit import java.util.concurrent.TimeUnit -class CreateToken : CliktCommand("create-token") { +class CreateToken : TalerCmd("create-token") { override fun help(context: Context) = "Create authentication token for a user" - private val common by CommonOption() + private val username by option( "-u", "--user", "--username", metavar = "<username>", @@ -59,8 +59,8 @@ class CreateToken : CliktCommand("create-token") { Base32Crockford.decode(it.removePrefix(TOKEN_PREFIX)) } - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> val now = Instant.now() val token = currentToken?.let { db.token.access(it, now) } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/DbInit.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/DbInit.kt @@ -29,22 +29,20 @@ import tech.libeufin.bank.createAdminAccount import tech.libeufin.bank.db.AccountDAO.AccountCreationResult import tech.libeufin.bank.logger import tech.libeufin.bank.withDb -import tech.libeufin.common.CommonOption -import tech.libeufin.common.cliCmd +import tech.libeufin.common.TalerCmd import tech.libeufin.common.db.dbInit import tech.libeufin.common.db.pgDataSource -class DbInit : CliktCommand("dbinit") { +class DbInit : TalerCmd("dbinit") { override fun help(context: Context) = "Initialize the libeufin-bank database" - private val common by CommonOption() private val reset by option( "--reset", "-r", help = "Reset database (DANGEROUS: All existing data is lost)" ).flag() - override fun run() = cliCmd(logger, common.log) { - val cfg = bankConfig(common.config) + override fun run() = cliCmd(logger) { + val cfg = bankConfig(config) val dbCfg = cfg.dbCfg pgDataSource(dbCfg.dbConnStr).dbInit(dbCfg, "libeufin-bank", reset) cfg.withDb { db, cfg -> diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt @@ -32,10 +32,9 @@ import tech.libeufin.bank.db.AccountDAO.AccountPatchResult import tech.libeufin.common.* -class EditAccount : CliktCommand("edit-account") { +class EditAccount : TalerCmd("edit-account") { override fun help(context: Context) = "Edit an existing account" - private val common by CommonOption() private val username: String by argument( "username", help = "Account username" @@ -68,8 +67,8 @@ class EditAccount : CliktCommand("edit-account") { } } - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> val req = AccountReconfiguration( name = name, is_taler_exchange = exchange, diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/Gc.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/Gc.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -25,17 +25,14 @@ import com.github.ajalt.clikt.parameters.groups.provideDelegate import tech.libeufin.bank.bankConfig import tech.libeufin.bank.logger import tech.libeufin.bank.withDb -import tech.libeufin.common.CommonOption -import tech.libeufin.common.cliCmd +import tech.libeufin.common.TalerCmd import java.time.Instant -class GC : CliktCommand("gc") { +class GC : TalerCmd("gc") { override fun help(context: Context) = "Run garbage collection: abort expired operations and clean expired data" - - private val common by CommonOption() - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> logger.info("Run garbage collection") db.gc.collect(Instant.now(), cfg.gcAbortAfter, cfg.gcCleanAfter, cfg.gcDeleteAfter) } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/Serve.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/Serve.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -26,20 +26,17 @@ import tech.libeufin.bank.bankConfig import tech.libeufin.bank.corebankWebApp import tech.libeufin.bank.logger import tech.libeufin.bank.withDb -import tech.libeufin.common.CommonOption +import tech.libeufin.common.TalerCmd import tech.libeufin.common.api.serve -import tech.libeufin.common.cliCmd import kotlin.io.path.Path import kotlin.io.path.exists import kotlin.io.path.readText -class Serve: CliktCommand("serve") { +class Serve: TalerCmd("serve") { override fun help(context: Context) = "Run libeufin-bank HTTP server" - private val common by CommonOption() - - override fun run() = cliCmd(logger, common.log) { - bankConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + bankConfig(config).withDb { db, cfg -> if (cfg.allowConversion) { logger.info("Ensure exchange account exists") val info = db.account.bankInfo("exchange") diff --git a/common/src/main/kotlin/Cli.kt b/common/src/main/kotlin/Cli.kt @@ -38,23 +38,7 @@ import org.slf4j.event.Level private val logger: Logger = LoggerFactory.getLogger("libeufin-config") -fun cliCmd(logger: Logger, level: Level, lambda: suspend () -> Unit) { - // Set log level - TalerServiceProvider.currentLevel = level - // Run cli command catching all errors - try { - runBlocking { - lambda() - } - } catch (e: ProgramResult) { - throw e - } catch (e: Throwable) { - e.fmtLog(logger) - throw ProgramResult(1) - } -} - -class CommonOption: OptionGroup() { +abstract class TalerCmd(name: String? = null): CliktCommand(name) { val config by option( "--config", "-c", help = "Specifies the configuration file", @@ -64,9 +48,25 @@ class CommonOption: OptionGroup() { "--log", "-L", help = "Configure logging to use LOGLEVEL" ).enum<Level>().default(Level.INFO) + + fun cliCmd(logger: Logger, lambda: suspend () -> Unit) { + // Set log level + TalerServiceProvider.currentLevel = log + // Run cli command catching all errors + try { + runBlocking { + lambda() + } + } catch (e: ProgramResult) { + throw e + } catch (e: Throwable) { + e.fmtLog(logger) + throw ProgramResult(1) + } + } } -class CliConfigCmd(configSource: ConfigSource) : CliktCommand("config") { +class CliConfigCmd(configSource: ConfigSource) : TalerCmd("config") { init { subcommands(CliConfigGet(configSource), CliConfigDump(configSource), CliConfigPathsub(configSource)) } @@ -76,10 +76,9 @@ class CliConfigCmd(configSource: ConfigSource) : CliktCommand("config") { override fun run() = Unit } -private class CliConfigGet(private val configSource: ConfigSource) : CliktCommand("get") { +private class CliConfigGet(private val configSource: ConfigSource) : TalerCmd("get") { override fun help(context: Context) = "Lookup config value" - private val common by CommonOption() private val isPath by option( "--filename", "-f", help = "Interpret value as path with dollar-expansion" @@ -87,8 +86,8 @@ private class CliConfigGet(private val configSource: ConfigSource) : CliktComman private val section by argument() private val option by argument() - override fun run() = cliCmd(logger, common.log) { - val config = configSource.fromFile(common.config) + override fun run() = cliCmd(logger) { + val config = configSource.fromFile(config) val sect = config.section(section) if (isPath) { println(sect.path(option).require()) @@ -100,25 +99,22 @@ private class CliConfigGet(private val configSource: ConfigSource) : CliktComman -private class CliConfigPathsub(private val configSource: ConfigSource) : CliktCommand("pathsub") { +private class CliConfigPathsub(private val configSource: ConfigSource) : TalerCmd("pathsub") { override fun help(context: Context) = "Substitute variables in a path" - private val common by CommonOption() private val pathExpr by argument() - override fun run() = cliCmd(logger, common.log) { - val config = configSource.fromFile(common.config) + override fun run() = cliCmd(logger) { + val config = configSource.fromFile(config) println(config.pathsub(pathExpr)) } } -private class CliConfigDump(private val configSource: ConfigSource) : CliktCommand("dump") { +private class CliConfigDump(private val configSource: ConfigSource) : TalerCmd("dump") { override fun help(context: Context) = "Dump the configuration" - private val common by CommonOption() - - override fun run() = cliCmd(logger, common.log) { - val config = configSource.fromFile(common.config) + override fun run() = cliCmd(logger) { + val config = configSource.fromFile(config) println("# install path: ${configSource.installPath()}") println(config.stringify()) } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/DbInit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/DbInit.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,24 +23,22 @@ import com.github.ajalt.clikt.core.Context import com.github.ajalt.clikt.parameters.groups.provideDelegate import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option -import tech.libeufin.common.CommonOption -import tech.libeufin.common.cliCmd +import tech.libeufin.common.TalerCmd import tech.libeufin.common.db.dbInit import tech.libeufin.common.db.pgDataSource import tech.libeufin.nexus.dbConfig import tech.libeufin.nexus.logger -class DbInit : CliktCommand("dbinit") { +class DbInit : TalerCmd("dbinit") { override fun help(context: Context) = "Initialize the libeufin-nexus database" - private val common by CommonOption() private val reset by option( "--reset", "-r", help = "Reset database (DANGEROUS: All existing data is lost)" ).flag() - override fun run() = cliCmd(logger, common.log) { - val cfg = dbConfig(common.config) + override fun run() = cliCmd(logger) { + val cfg = dbConfig(config) pgDataSource(cfg.dbConnStr).dbInit(cfg, "libeufin-nexus", reset) } } \ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt @@ -418,11 +418,9 @@ data class Checkpoint( val last_trial: Instant? = null ) -class EbicsFetch: CliktCommand() { +class EbicsFetch: EbicsCmd() { override fun help(context: Context) = "Downloads and parse EBICS files from the bank and register them into the database" - private val common by CommonOption() - private val transient by transientOption() private val documents: Set<OrderDoc> by argument( help = "Which documents should be fetched? If none are specified, all supported documents will be fetched", helpTags = OrderDoc.entries.associate { Pair(it.name, it.shortDescription()) }, @@ -437,10 +435,9 @@ class EbicsFetch: CliktCommand() { private val transientCheckpoint by option("--checkpoint", help = "Only supported in --transient mode, run a checkpoint" ).flag() - private val ebicsLog by ebicsLogOption() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val client = EbicsClient( cfg, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt @@ -153,10 +153,9 @@ private fun makePdf(privs: ClientPrivateKeysFile, cfg: NexusEbicsConfig) { /** * CLI class implementing the "ebics-setup" subcommand. */ -class EbicsSetup: CliktCommand() { +class EbicsSetup: TalerCmd() { override fun help(context: Context) = "Set up the EBICS subscriber" - private val common by CommonOption() private val forceKeysResubmission by option( help = "Resubmits all the keys to the bank" ).flag(default = false) @@ -170,8 +169,8 @@ class EbicsSetup: CliktCommand() { /** * This function collects the main steps of setting up an EBICS access. */ - override fun run() = cliCmd(logger, common.log) { - val cfg = nexusConfig(common.config) + override fun run() = cliCmd(logger) { + val cfg = nexusConfig(config) val setupCfg = cfg.setup val client = httpClient() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt @@ -113,15 +113,11 @@ private suspend fun submitAll(client: EbicsClient) { } } -class EbicsSubmit : CliktCommand() { +class EbicsSubmit : EbicsCmd() { override fun help(context: Context) = "Submits pending initiated payments found in the database" - private val common by CommonOption() - private val transient by transientOption() - private val ebicsLog by ebicsLogOption() - - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val client = EbicsClient( cfg, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/InitiatePayment.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/InitiatePayment.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -33,10 +33,9 @@ import tech.libeufin.nexus.nexusConfig import tech.libeufin.nexus.withDb import java.time.Instant -class InitiatePayment: CliktCommand() { +class InitiatePayment: TalerCmd() { override fun help(context: Context) = "Initiate an outgoing payment" - private val common by CommonOption() private val amount by option( "--amount", help = "The amount to transfer, payto 'amount' parameter takes the precedence" @@ -54,8 +53,8 @@ class InitiatePayment: CliktCommand() { help = "The credited account IBAN payto URI" ).convert { Payto.parse(it).expectIban() } - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val subject = requireNotNull(payto.message ?: subject) { "Missing subject" } val amount = requireNotNull(payto.amount ?: amount) { "Missing amount" } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt @@ -25,8 +25,7 @@ import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.versionOption import com.github.ajalt.clikt.parameters.types.path -import tech.libeufin.common.CliConfigCmd -import tech.libeufin.common.VERSION +import tech.libeufin.common.* import tech.libeufin.nexus.NEXUS_CONFIG_SOURCE fun CliktCommand.ebicsLogOption() = option( @@ -40,6 +39,11 @@ fun CliktCommand.transientOption() = option( help = "Execute once and return, ignoring the 'FREQUENCY' configuration value" ).flag(default = false) +abstract class EbicsCmd(name: String? = null): TalerCmd(name) { + val ebicsLog by ebicsLogOption() + val transient by transientOption() +} + class LibeufinNexus : CliktCommand() { init { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Manual.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Manual.kt @@ -39,14 +39,13 @@ import java.util.zip.* import java.time.Instant import java.io.* -class ExportCmt: CliktCommand("export") { +class ExportCmt: TalerCmd("export") { override fun help(context: Context) = "Export pending batches as pain001 messages" - private val common by CommonOption() private val out by argument().outputStream() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> // Create and get pending batches db.initiated.batch(Instant.now(), randEbicsId()) val batches = db.initiated.submittable() @@ -84,14 +83,13 @@ class ExportCmt: CliktCommand("export") { } } -class ImportCmt: CliktCommand("import") { +class ImportCmt: TalerCmd("import") { override fun help(context: Context) = "Import EBICS camt files" - private val common by CommonOption() private val sources by argument().inputStream().multiple(required = true) - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> for (source in sources) { var nbTx: Int = 0 source.use { xml -> @@ -103,7 +101,7 @@ class ImportCmt: CliktCommand("import") { } } -class StatusCmd: CliktCommand("status") { +class StatusCmd: TalerCmd("status") { override fun help(context: Context) = "Change batches or transactions status" enum class Kind { @@ -111,14 +109,13 @@ class StatusCmd: CliktCommand("status") { tx } - private val common by CommonOption() private val kind by argument().enum<Kind>() private val id by argument() private val status by argument().enum<StatusUpdate>() private val msg by argument().optional() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> when (kind) { Kind.batch -> if (db.initiated.batchStatusUpdate(id, status, msg)) { logger.info("Updated batch '${id}' to ${status}") @@ -135,7 +132,7 @@ class StatusCmd: CliktCommand("status") { } } -class ManualCmd : CliktCommand("manual") { +class ManualCmd : TalerCmd("manual") { init { subcommands(ExportCmt(), ImportCmt(), StatusCmd()) } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Serve.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Serve.kt @@ -1,6 +1,6 @@ /* * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. + * Copyright (C) 2024-2025 Taler Systems S.A. * LibEuFin is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -25,25 +25,23 @@ import com.github.ajalt.clikt.core.ProgramResult import com.github.ajalt.clikt.parameters.groups.provideDelegate import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option -import tech.libeufin.common.CommonOption +import tech.libeufin.common.TalerCmd import tech.libeufin.common.api.serve -import tech.libeufin.common.cliCmd import tech.libeufin.nexus.logger import tech.libeufin.nexus.nexusApi import tech.libeufin.nexus.nexusConfig import tech.libeufin.nexus.withDb -class Serve : CliktCommand("serve") { +class Serve : TalerCmd("serve") { override fun help(context: Context) = "Run libeufin-nexus HTTP server" - private val common by CommonOption() private val check by option( help = "Check whether an API is in use (if it's useful to start the HTTP server). Exit with 0 if at least one API is enabled, otherwise 1" ).flag() - override fun run() = cliCmd(logger, common.log) { - val cfg = nexusConfig(common.config) + override fun run() = cliCmd(logger) { + val cfg = nexusConfig(config) if (check) { // Check if the server is to be started diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt @@ -38,14 +38,13 @@ import java.util.zip.* import java.time.Instant import java.io.* -class Wss: CliktCommand() { +class Wss: TalerCmd() { override fun help(context: Context) = "Listen to EBICS instant notification over websocket" - private val common by CommonOption() private val ebicsLog by ebicsLogOption() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val client = EbicsClient( cfg, @@ -66,10 +65,9 @@ class Wss: CliktCommand() { } } -class FakeIncoming: CliktCommand() { +class FakeIncoming: TalerCmd() { override fun help(context: Context) = "Genere a fake incoming payment" - private val common by CommonOption() private val amount by option( "--amount", help = "The payment amount, payto 'amount' parameter takes the precedence" @@ -86,8 +84,8 @@ class FakeIncoming: CliktCommand() { help = "The debited account IBAN payto URI" ).convert { Payto.parse(it).expectIban() } - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val subject = requireNotNull(payto.message ?: subject) { "Missing subject" } val amount = requireNotNull(payto.amount ?: amount) { "Missing amount" } @@ -109,13 +107,11 @@ class FakeIncoming: CliktCommand() { } } -class TxCheck: CliktCommand() { +class TxCheck: TalerCmd() { override fun help(context: Context) = "Check transaction semantic" - private val common by CommonOption() - - override fun run() = cliCmd(logger, common.log) { - val nexusCgf = nexusConfig(common.config) + override fun run() = cliCmd(logger) { + val nexusCgf = nexusConfig(config) val cfg = nexusCgf.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val order = cfg.dialect.downloadDoc(OrderDoc.acknowledgement, false) @@ -137,10 +133,9 @@ enum class ListKind { } } -class EbicsDownload: CliktCommand("ebics-btd") { +class EbicsDownload: TalerCmd("ebics-btd") { override fun help(context: Context) = "Perform EBICS requests" - private val common by CommonOption() private val type by option().default("BTD") private val name by option() private val scope by option() @@ -161,8 +156,8 @@ class EbicsDownload: CliktCommand("ebics-btd") { class DryRun: Exception() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val pinnedStartVal = pinnedStart val pinnedStartArg = if (pinnedStartVal != null) { @@ -201,17 +196,16 @@ class EbicsDownload: CliktCommand("ebics-btd") { } } -class ListCmd: CliktCommand("list") { +class ListCmd: TalerCmd("list") { override fun help(context: Context) = "List nexus transactions" - private val common by CommonOption() private val kind: ListKind by argument( help = "Which list to print", helpTags = ListKind.entries.associate { Pair(it.name, it.description()) } ).enum<ListKind>() - override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + override fun run() = cliCmd(logger) { + nexusConfig(config).withDb { db, cfg -> fun fmtPayto(payto: String): String { if (payto == null) return "" try {