libeufin

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

commit a5068c3974d3fb39936ed783fddfa8e881277fe3
parent 5f2313d68e65e6c6d39347c0ed83d32c8e166e40
Author: Antoine A <>
Date:   Tue, 12 Dec 2023 17:31:22 +0000

Improve CLI help message format

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt | 13+++++--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt | 13+++++--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt | 19++++++++-----------
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt | 14+++++---------
Autil/src/main/kotlin/Cli.kt | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dutil/src/main/kotlin/ConfigCli.kt | 119-------------------------------------------------------------------------------
6 files changed, 144 insertions(+), 155 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt @@ -1,8 +1,8 @@ package tech.libeufin.nexus import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.groups.* import tech.libeufin.util.* import kotlin.system.exitProcess @@ -12,17 +12,14 @@ import kotlin.system.exitProcess * the data first. */ class DbInit : CliktCommand("Initialize the libeufin-nexus database", name = "dbinit") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) + private val common by CommonOption() private val requestReset by option( "--reset", "-r", - help = "reset database (DANGEROUS: All existing data is lost)" + help = "Reset database (DANGEROUS: All existing data is lost)" ).flag() override fun run() { - val cfg = loadConfigOrFail(configFile).extractDbConfigOrFail() + val cfg = loadConfigOrFail(common.config).extractDbConfigOrFail() doOrFail { pgDataSource(cfg.dbConnStr).pgConnection().use { conn -> if (requestReset) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt @@ -1,8 +1,8 @@ package tech.libeufin.nexus import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.groups.* import io.ktor.client.* import kotlinx.coroutines.runBlocking import net.taler.wallet.crypto.Base32Crockford @@ -524,10 +524,7 @@ private suspend fun fetchDocuments( } class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 notifications") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) + private val common by CommonOption() private val transient by option( "--transient", help = "This flag fetches only once from the bank and returns, " + @@ -553,7 +550,7 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti ).flag(default = false) private val pinnedStart by option( - help = "constant YYYY-MM-DD date for the earliest document" + + help = "Constant YYYY-MM-DD date for the earliest document" + " to download (only consumed in --transient mode). The" + " latest document is always until the current time." ) @@ -580,7 +577,7 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti */ override fun run() { val cfg: EbicsSetupConfig = doOrFail { - extractEbicsConfig(configFile) + extractEbicsConfig(common.config) } val dbCfg = cfg.config.extractDbConfigOrFail() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt @@ -20,8 +20,8 @@ package tech.libeufin.nexus import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.groups.* import io.ktor.client.* import kotlinx.coroutines.runBlocking import tech.libeufin.util.ebics_h004.EbicsTypes @@ -330,27 +330,24 @@ private fun makePdf(privs: ClientPrivateKeysFile, cfg: EbicsSetupConfig) { * CLI class implementing the "ebics-setup" subcommand. */ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) + private val common by CommonOption() private val checkFullConfig by option( - help = "checks config values of ALL the subcommands" + help = "Checks config values of ALL the subcommands" ).flag(default = false) private val forceKeysResubmission by option( - help = "resubmits all the keys to the bank" + help = "Resubmits all the keys to the bank" ).flag(default = false) private val autoAcceptKeys by option( - help = "accepts the bank keys without the user confirmation" + help = "Accepts the bank keys without the user confirmation" ).flag(default = false) private val generateRegistrationPdf by option( - help = "generates the PDF with the client public keys to send to the bank" + help = "Generates the PDF with the client public keys to send to the bank" ).flag(default = false) /** * This function collects the main steps of setting up an EBICS access. */ override fun run() { - val cfg = doOrFail { extractEbicsConfig(this.configFile) } + val cfg = doOrFail { extractEbicsConfig(common.config) } if (checkFullConfig) { doOrFail { cfg.config.requireString("nexus-submit", "frequency").apply { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt @@ -20,16 +20,15 @@ package tech.libeufin.nexus import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.groups.* import io.ktor.client.* import kotlinx.coroutines.runBlocking import tech.libeufin.nexus.ebics.EbicsSideError import tech.libeufin.nexus.ebics.EbicsSideException import tech.libeufin.nexus.ebics.EbicsUploadException import tech.libeufin.nexus.ebics.submitPain001 -import tech.libeufin.util.parsePayto -import tech.libeufin.util.toDbMicros +import tech.libeufin.util.* import java.io.File import java.nio.file.Path import java.time.Instant @@ -251,10 +250,7 @@ private fun submitBatch( } class EbicsSubmit : CliktCommand("Submits any initiated payment found in the database") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) + private val common by CommonOption() private val transient by option( "--transient", help = "This flag submits what is found in the database and returns, " + @@ -276,7 +272,7 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat */ override fun run() { val cfg: EbicsSetupConfig = doOrFail { - extractEbicsConfig(configFile) + extractEbicsConfig(common.config) } // Fail now if keying is incomplete. if (!isKeyingComplete(cfg)) exitProcess(1) diff --git a/util/src/main/kotlin/Cli.kt b/util/src/main/kotlin/Cli.kt @@ -0,0 +1,121 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2023 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 + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin 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 Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.util + +import ConfigSource +import TalerConfig +import TalerConfigError +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.types.* +import com.github.ajalt.clikt.parameters.arguments.* +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.groups.* +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.system.exitProcess + +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.ConfigCli") + +fun cliCmd(logger: Logger, lambda: () -> Unit) { + try { + lambda() + } catch (e: Exception) { + logger.error(e.message) + exitProcess(1) + } +} + +private fun talerConfig(configSource: ConfigSource, configPath: String?): TalerConfig { + val config = TalerConfig(configSource) + config.load(configPath) + return config +} + +class CommonOption: OptionGroup() { + val config by option( + "--config", "-c", + help = "Specifies the configuration file" + ).path( + mustExist = true, + canBeDir = false, + mustBeReadable = true, + ).convert { it.toString() } // TODO take path to load config +} + +class CliConfigCmd(configSource: ConfigSource) : CliktCommand("Inspect or change the configuration", name = "config") { + init { + subcommands(CliConfigDump(configSource), CliConfigPathsub(configSource), CliConfigGet(configSource)) + } + + override fun run() = Unit +} + +private class CliConfigGet(private val configSource: ConfigSource) : CliktCommand("Lookup config value", name = "get") { + private val common by CommonOption() + private val isPath by option( + "--filename", "-f", + help = "Interpret value as path with dollar-expansion" + ).flag() + private val sectionName by argument() + private val optionName by argument() + + + override fun run() = cliCmd(logger) { + val config = talerConfig(configSource, common.config) + if (isPath) { + val res = config.lookupPath(sectionName, optionName) + if (res == null) { + logger.error("value not found in config") + exitProcess(2) + } + println(res) + } else { + val res = config.lookupString(sectionName, optionName) + if (res == null) { + logger.error("value not found in config") + exitProcess(2) + } + println(res) + } + } +} + + + +private class CliConfigPathsub(private val configSource: ConfigSource) : CliktCommand("Substitute variables in a path", name = "pathsub") { + private val common by CommonOption() + private val pathExpr by argument() + + override fun run() = cliCmd(logger) { + val config = talerConfig(configSource, common.config) + println(config.pathsub(pathExpr)) + } +} + +private class CliConfigDump(private val configSource: ConfigSource) : CliktCommand("Dump the configuration", name = "dump") { + private val common by CommonOption() + + override fun run() = cliCmd(logger) { + val config = talerConfig(configSource, common.config) + println("# install path: ${config.getInstallPath()}") + println(config.stringify()) + } +} diff --git a/util/src/main/kotlin/ConfigCli.kt b/util/src/main/kotlin/ConfigCli.kt @@ -1,119 +0,0 @@ -/* - * This file is part of LibEuFin. - * Copyright (C) 2023 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 - * published by the Free Software Foundation; either version 3, or - * (at your option) any later version. - - * LibEuFin 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 Affero General - * Public License for more details. - - * You should have received a copy of the GNU Affero General Public - * License along with LibEuFin; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/> - */ - -package tech.libeufin.util - -import ConfigSource -import TalerConfig -import TalerConfigError -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.arguments.argument -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import kotlin.system.exitProcess - -private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.ConfigCli") - -fun cliCmd(logger: Logger, lambda: () -> Unit) { - try { - lambda() - } catch (e: Exception) { - logger.error(e.message) - exitProcess(1) - } -} - -private fun talerConfig(configSource: ConfigSource, configPath: String?): TalerConfig { - val config = TalerConfig(configSource) - config.load(configPath) - return config -} - -class CliConfigCmd(configSource: ConfigSource) : CliktCommand("Inspect or change the configuration", name = "config") { - init { - subcommands(CliConfigDump(configSource), CliConfigPathsub(configSource), CliConfigGet(configSource)) - } - - override fun run() = Unit -} - -private class CliConfigGet(private val configSource: ConfigSource) : CliktCommand("Lookup config value", name = "get") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - private val isPath by option( - "--filename", "-f", - help = "interpret value as path with dollar-expansion" - ).flag() - private val sectionName by argument() - private val optionName by argument() - - - override fun run() = cliCmd(logger) { - val config = talerConfig(configSource, configFile) - if (isPath) { - val res = config.lookupPath(sectionName, optionName) - if (res == null) { - logger.error("value not found in config") - exitProcess(2) - } - println(res) - } else { - val res = config.lookupString(sectionName, optionName) - if (res == null) { - logger.error("value not found in config") - exitProcess(2) - } - println(res) - } - } -} - - - -private class CliConfigPathsub(private val configSource: ConfigSource) : CliktCommand("Substitute variables in a path", name = "pathsub") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - private val pathExpr by argument() - - override fun run() = cliCmd(logger) { - val config = talerConfig(configSource, configFile) - println(config.pathsub(pathExpr)) - } -} - -private class CliConfigDump(private val configSource: ConfigSource) : CliktCommand("Dump the configuration", name = "dump") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - - override fun run() = cliCmd(logger) { - val config = talerConfig(configSource, configFile) - println("# install path: ${config.getInstallPath()}") - config.load(this.configFile) - println(config.stringify()) - } -}