libeufin

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

commit 0f45089ada0c1eac60120c6b4b0914ce10d788bc
parent c7471702ab241ab16b0ad6c13a22201c4077c1e0
Author: Antoine A <>
Date:   Wed, 28 Aug 2024 19:13:11 +0200

common: improve and fix CLI

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt | 10++++++++++
Mbank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt | 6++++++
Mbank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt | 35+++++++++++++++++++++++------------
Mbank/src/main/kotlin/tech/libeufin/bank/cli/LibeufinBank.kt | 2+-
Mbank/src/test/kotlin/CoreBankApiTest.kt | 7+++++++
Mcommon/src/main/kotlin/Cli.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt | 2+-
7 files changed, 49 insertions(+), 15 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt @@ -124,6 +124,16 @@ sealed class Option<out T> { return Some(valueSerializer.deserialize(decoder)) } } + + companion object { + fun <T> invert(optional: Option<T>?): Option<T?> { + return when (optional) { + null -> Option.None + is Option.None -> Option.Some(null) + is Option.Some -> Option.Some(optional.value) + } + } + } } @Serializable diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt @@ -273,6 +273,12 @@ suspend fun patchAccount( "'admin' account cannot be public", TalerErrorCode.END ) + + if (login == "exchange" && req.is_taler_exchange == false) + throw conflict( + "'exchange' account must be a taler exchange account", + TalerErrorCode.END + ) if (req.tan_channel is Option.Some && req.tan_channel.value != null && !cfg.tanChannels.contains(req.tan_channel.value)) { throw unsupportedTanChannel(req.tan_channel.value) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/EditAccount.kt @@ -43,25 +43,39 @@ class EditAccount : CliktCommand( private val name: String? by option( help = "Legal name of the account owner" ) - private val exchange: Boolean? by option( - hidden = true - ).boolean() private val is_public: Boolean? by option( "--public", help = "Make this account visible to anyone" ).boolean() + private val exchange: Boolean? by option( + help = "Make this account a taler exchange" + ).boolean() private val email: String? by option(help = "E-Mail address used for TAN transmission") private val phone: String? by option(help = "Phone number used for TAN transmission") - private val tan_channel: String? by option(help = "which channel TAN challenges should be sent to") - private val cashout_payto_uri: IbanPayto? by option(help = "Payto URI of a fiant account who receive cashout amount").convert { Payto.parse(it).expectIban() } - private val debit_threshold: TalerAmount? by option(help = "Max debit allowed for this account").convert { TalerAmount(it) } - private val min_cashout: Option<TalerAmount>? by option(help = "Custom minimum cashout amount for this account").convert { + private val cashout_payto_uri: IbanPayto? by option( + help = "Payto URI of a fiant account who receive cashout amount" + ).convert { Payto.parse(it).expectIban() } + private val debit_threshold: TalerAmount? by option( + help = "Max debit allowed for this account" + ).convert { TalerAmount(it) } + private val min_cashout: Option<TalerAmount>? by option( + help = "Custom minimum cashout amount for this account" + ).convert { if (it == "") { Option.None } else { Option.Some(TalerAmount(it)) } } + private val tan_channel: Option<TanChannel>? by option( + help = "Enables 2FA and set the TAN channel used for challenges" + ).convert { + if (it == "") { + Option.None + } else { + Option.Some(TanChannel.valueOf(it)) + } + } override fun run() = cliCmd(logger, common.log) { bankConfig(common.config).withDb { db, cfg -> @@ -76,11 +90,8 @@ class EditAccount : CliktCommand( ), cashout_payto_uri = Option.Some(cashout_payto_uri), debit_threshold = debit_threshold, - min_cashout = when (val tmp = min_cashout) { - null -> Option.None - is Option.None -> Option.Some(null) - is Option.Some -> Option.Some(tmp.value) - } + min_cashout = Option.invert(min_cashout), + tan_channel = Option.invert(tan_channel) ) when (patchAccount(db, cfg, req, username, true, true)) { AccountPatchResult.Success -> diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/LibeufinBank.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/LibeufinBank.kt @@ -29,7 +29,7 @@ import tech.libeufin.common.getVersion class LibeufinBank : CliktCommand() { init { versionOption(getVersion()) - subcommands(Serve(), DbInit(), CreateAccount(), EditAccount(), ChangePw(), BenchPwh(), GC(), CliConfigCmd(BANK_CONFIG_SOURCE)) + subcommands(DbInit(), ChangePw(), Serve(), CreateAccount(), EditAccount(), GC(), BenchPwh(), CliConfigCmd(BANK_CONFIG_SOURCE)) } override fun run() = Unit diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -769,6 +769,13 @@ class CoreBankAccountsApiTest { } }.assertConflict(TalerErrorCode.END) + // Exchange must be exchange + client.patchA("/accounts/exchange") { + json { + "is_taler_exchange" to false + } + }.assertConflict(TalerErrorCode.END) + // Check cashout payto receiver name logic client.post("/accounts") { json { diff --git a/common/src/main/kotlin/Cli.kt b/common/src/main/kotlin/Cli.kt @@ -77,7 +77,7 @@ class CommonOption: OptionGroup() { class CliConfigCmd(configSource: ConfigSource) : CliktCommand("Inspect or change the configuration", name = "config") { init { - subcommands(CliConfigDump(configSource), CliConfigPathsub(configSource), CliConfigGet(configSource)) + subcommands(CliConfigGet(configSource), CliConfigDump(configSource), CliConfigPathsub(configSource)) } override fun run() = Unit diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/LibeufinNexus.kt @@ -44,7 +44,7 @@ fun CliktCommand.transientOption() = option( class LibeufinNexus : CliktCommand() { init { versionOption(getVersion()) - subcommands(EbicsSetup(), DbInit(), Serve(), EbicsSubmit(), EbicsFetch(), InitiatePayment(), CliConfigCmd(NEXUS_CONFIG_SOURCE), TestingCmd()) + subcommands(DbInit(), EbicsSetup(), EbicsSubmit(), EbicsFetch(), Serve(), InitiatePayment(), CliConfigCmd(NEXUS_CONFIG_SOURCE), TestingCmd()) } override fun run() = Unit } \ No newline at end of file