commit f24be2dae4b9d081825d9409f9dd00afca7181bc
parent 6182bc939d9e0c4d15e4e1a6289c99898a2821dc
Author: Antoine A <>
Date: Mon, 8 Jan 2024 16:53:32 +0000
Simplify cli error handling
Diffstat:
7 files changed, 73 insertions(+), 144 deletions(-)
diff --git a/integration/src/main/kotlin/Main.kt b/integration/src/main/kotlin/Main.kt
@@ -50,7 +50,8 @@ fun step(name: String) {
}
fun ask(question: String): String? {
- println("\u001b[;1m$question\u001b[0m")
+ print("\u001b[;1m$question\u001b[0m")
+ System.out.flush()
return readlnOrNull()
}
@@ -86,16 +87,14 @@ class PostFinanceCli : CliktCommand("Run tests on postfinance", name="postfinanc
if (!hasClientKeys) {
step("Test INI order")
- println("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Reset EBICS user'.\nPress Enter when done>")
- readlnOrNull()
+ ask("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Reset EBICS user'.\nPress Enter when done>")
nexusCmd.test("ebics-setup -c $conf")
.assertErr("ebics-setup should failed the first time")
}
if (!hasBankKeys) {
step("Test HIA order")
- println("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Activate EBICS user'.\nPress Enter when done>")
- readlnOrNull()
+ ask("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Activate EBICS user'.\nPress Enter when done>")
nexusCmd.test("ebics-setup --auto-accept-keys -c $conf")
.assertOk("ebics-setup should succeed the second time")
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt
@@ -18,14 +18,12 @@ class DbInit : CliktCommand("Initialize the libeufin-nexus database", name = "db
).flag()
override fun run() {
- val cfg = loadConfigOrFail(common.config).extractDbConfigOrFail()
- doOrFail {
- pgDataSource(cfg.dbConnStr).pgConnection().use { conn ->
- if (requestReset) {
- resetDatabaseTables(conn, cfg, sqlFilePrefix = "libeufin-nexus")
- }
- initializeDatabaseTables(conn, cfg, sqlFilePrefix = "libeufin-nexus")
+ val cfg = loadConfig(common.config).dbConfig()
+ pgDataSource(cfg.dbConnStr).pgConnection().use { conn ->
+ if (requestReset) {
+ resetDatabaseTables(conn, cfg, sqlFilePrefix = "libeufin-nexus")
}
+ initializeDatabaseTables(conn, cfg, sqlFilePrefix = "libeufin-nexus")
}
}
}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -402,14 +402,11 @@ private fun ingestNotification(
try {
runBlocking {
incomingPayments.forEach {
- println(it)
- ingestIncomingPayment(
- db,
- it
- )
+ logger.debug("incoming tx: $it")
+ ingestIncomingPayment(db, it)
}
outgoingPayments.forEach {
- println(it)
+ logger.debug("outgoing tx: $it")
ingestOutgoingPayment(db, it)
}
}
@@ -532,7 +529,7 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti
override fun run() = cliCmd(logger) {
val cfg: EbicsSetupConfig = extractEbicsConfig(common.config)
- val dbCfg = cfg.config.extractDbConfigOrFail()
+ val dbCfg = cfg.config.dbConfig()
val db = Database(dbCfg.dbConnStr)
// Deciding what to download.
@@ -568,17 +565,7 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti
return@cliCmd
}
- // Fail now if keying is incomplete.
- if (!isKeyingComplete(cfg)) throw Error()
- val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename) ?: throw Error()
- if (!bankKeys.accepted && !import && !parse) {
- throw Error("Bank keys are not accepted, yet. Won't fetch any records.")
- }
- val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
- if (clientKeys == null) {
- throw Error("Client private keys not found at: ${cfg.clientPrivateKeysFilename}")
- }
-
+ val (clientKeys, bankKeys) = expectFullKeys(cfg)
val ctx = FetchContext(
cfg,
HttpClient(),
@@ -593,10 +580,8 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti
val pinnedStartVal = pinnedStart
val pinnedStartArg = if (pinnedStartVal != null) {
logger.debug("Pinning start date to: $pinnedStartVal")
- doOrFail {
- // Converting YYYY-MM-DD to Instant.
- LocalDate.parse(pinnedStartVal).atStartOfDay(ZoneId.of("UTC")).toInstant()
- }
+ // Converting YYYY-MM-DD to Instant.
+ LocalDate.parse(pinnedStartVal).atStartOfDay(ZoneId.of("UTC")).toInstant()
} else null
ctx.pinnedStart = pinnedStartArg
if (whichDoc == SupportedDocument.PAIN_002_LOGS)
@@ -606,11 +591,9 @@ class EbicsFetch: CliktCommand("Fetches bank records. Defaults to camt.054 noti
}
return@cliCmd
}
- val frequency: NexusFrequency = doOrFail {
- val configValue = cfg.config.requireString("nexus-fetch", "frequency")
- val frequencySeconds = checkFrequency(configValue)
- NexusFrequency(frequencySeconds, configValue)
- }
+ val configValue = cfg.config.requireString("nexus-fetch", "frequency")
+ val frequencySeconds = checkFrequency(configValue)
+ val frequency: NexusFrequency = NexusFrequency(frequencySeconds, configValue)
logger.debug("Running with a frequency of ${frequency.fromConfig}")
if (frequency.inSeconds == 0) {
logger.warn("Long-polling not implemented, running therefore in transient mode")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -35,32 +35,6 @@ import java.time.Instant
import kotlin.reflect.typeOf
/**
- * Checks the configuration to secure that the key exchange between
- * the bank and the subscriber took place. Helps to fail before starting
- * to talk EBICS to the bank.
- *
- * @param cfg configuration handle.
- * @return true if the keying was made before, false otherwise.
- */
-fun isKeyingComplete(cfg: EbicsSetupConfig): Boolean {
- val maybeClientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
- if (maybeClientKeys == null ||
- (!maybeClientKeys.submitted_ini) ||
- (!maybeClientKeys.submitted_hia)) {
- logger.error("Cannot operate without or with unsubmitted subscriber keys." +
- " Run 'libeufin-nexus ebics-setup' first.")
- return false
- }
- val maybeBankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
- if (maybeBankKeys == null || (!maybeBankKeys.accepted)) {
- logger.error("Cannot operate without or with unaccepted bank keys." +
- " Run 'libeufin-nexus ebics-setup' until accepting the bank keys.")
- return false
- }
- return true
-}
-
-/**
* Writes the JSON content to disk. Used when we create or update
* keys and other metadata JSON content to disk. WARNING: this overrides
* silently what's found under the given location!
@@ -290,14 +264,8 @@ suspend fun doKeysRequestAndUpdateState(
* @return internal representation of the configuration.
*/
fun extractEbicsConfig(configFile: String?): EbicsSetupConfig {
- val config = loadConfigOrFail(configFile)
- // Checking the config.
- val cfg = try {
- EbicsSetupConfig(config)
- } catch (e: TalerConfigError) {
- throw Error(e.message)
- }
- return cfg
+ val config = loadConfig(configFile)
+ return EbicsSetupConfig(config)
}
/**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -37,7 +37,6 @@ import java.time.ZoneId
import java.util.*
import kotlin.concurrent.fixedRateTimer
import kotlin.io.path.createDirectories
-import kotlin.system.exitProcess
/**
* Possible stages when an error may occur. These stages
@@ -117,17 +116,16 @@ private fun maybeLog(
val asUtcDate = LocalDate.ofInstant(now, ZoneId.of("UTC"))
val subDir = "${asUtcDate.year}-${asUtcDate.monthValue}-${asUtcDate.dayOfMonth}"
val dirs = Path.of(maybeLogDir, subDir)
- doOrFail { dirs.createDirectories() }
+ dirs.createDirectories()
val f = File(
dirs.toString(),
"${now.toDbMicros()}_requestUid_${requestUid}_pain.001.xml"
)
// Very rare: same pain.001 should not be submitted twice in the same microsecond.
if (f.exists()) {
- logger.error("pain.001 log file exists already at: $f")
- exitProcess(1)
+ throw Error("pain.001 log file exists already at: $f")
}
- doOrFail { f.writeText(xml) }
+ f.writeText(xml)
}
/**
@@ -270,24 +268,11 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat
* or long-polls (currently not implemented) for new payments.
* FIXME: reduce code duplication with the fetch subcommand.
*/
- override fun run() {
- val cfg: EbicsSetupConfig = doOrFail {
- extractEbicsConfig(common.config)
- }
- // Fail now if keying is incomplete.
- if (!isKeyingComplete(cfg)) exitProcess(1)
- val dbCfg = cfg.config.extractDbConfigOrFail()
+ override fun run() = cliCmd(logger) {
+ val cfg: EbicsSetupConfig = extractEbicsConfig(common.config)
+ val dbCfg = cfg.config.dbConfig()
val db = Database(dbCfg.dbConnStr)
- val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename) ?: exitProcess(1)
- if (!bankKeys.accepted) {
- logger.error("Bank keys are not accepted, yet. Won't submit any payment.")
- exitProcess(1)
- }
- val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
- if (clientKeys == null) {
- logger.error("Client private keys not found at: ${cfg.clientPrivateKeysFilename}")
- exitProcess(1)
- }
+ val (clientKeys, bankKeys) = expectFullKeys(cfg)
val ctx = SubmissionContext(
cfg = cfg,
bankPublicKeysFile = bankKeys,
@@ -298,35 +283,31 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat
if (debug) {
logger.info("Running in debug mode, submitting STDIN to the bank")
val maybeStdin = generateSequence(::readLine).joinToString("\n")
- doOrFail {
- runBlocking {
- submitPain001(
- maybeStdin,
- ctx.cfg,
- ctx.clientPrivateKeysFile,
- ctx.bankPublicKeysFile,
- ctx.httpClient,
- ctx.ebicsExtraLog
- )
- }
+ runBlocking {
+ submitPain001(
+ maybeStdin,
+ ctx.cfg,
+ ctx.clientPrivateKeysFile,
+ ctx.bankPublicKeysFile,
+ ctx.httpClient,
+ ctx.ebicsExtraLog
+ )
}
- return
+ return@cliCmd
}
if (transient) {
logger.info("Transient mode: submitting what found and returning.")
submitBatch(ctx, db)
- return
- }
- val frequency: NexusFrequency = doOrFail {
- val configValue = cfg.config.requireString("nexus-submit", "frequency")
- val frequencySeconds = checkFrequency(configValue)
- return@doOrFail NexusFrequency(frequencySeconds, configValue)
+ return@cliCmd
}
+ val configValue = cfg.config.requireString("nexus-submit", "frequency")
+ val frequencySeconds = checkFrequency(configValue)
+ val frequency: NexusFrequency = NexusFrequency(frequencySeconds, configValue)
logger.debug("Running with a frequency of ${frequency.fromConfig}")
if (frequency.inSeconds == 0) {
logger.warn("Long-polling not implemented, running therefore in transient mode")
submitBatch(ctx, db)
- return
+ return@cliCmd
}
fixedRateTimer(
name = "ebics submit period",
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -35,7 +35,6 @@ import kotlinx.serialization.KSerializer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
-import kotlin.system.exitProcess
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
@@ -269,19 +268,31 @@ data class BankPublicKeysFile(
)
/**
- * Runs the argument and fails the process, if that throws
- * an exception.
+ * Load client and bank keys from disk.
+ * Checks that the keying process has been fully completed.
+ *
+ * Helps to fail before starting to talk EBICS to the bank.
*
- * @param getLambda function that might return a value.
- * @return the value from getLambda.
+ * @param cfg configuration handle.
+ * @return both client and bank keys
*/
-fun <T>doOrFail(getLambda: () -> T): T =
- try {
- getLambda()
- } catch (e: Exception) {
- logger.error(e.message)
- exitProcess(1)
+fun expectFullKeys(
+ cfg: EbicsSetupConfig
+): Pair<ClientPrivateKeysFile, BankPublicKeysFile> {
+ val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
+ if (clientKeys == null ||
+ !clientKeys.submitted_ini ||
+ !clientKeys.submitted_hia) {
+ throw Error("Cannot operate without or with unsubmitted subscriber keys." +
+ " Run 'libeufin-nexus ebics-setup' first.")
+ }
+ val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
+ if (bankKeys == null || !bankKeys.accepted) {
+ throw Error("Cannot operate without or with unaccepted bank keys." +
+ " Run 'libeufin-nexus ebics-setup' until accepting the bank keys.")
}
+ return Pair(clientKeys, bankKeys)
+}
/**
* Load the bank keys file from disk.
@@ -342,35 +353,25 @@ fun loadPrivateKeysFromDisk(location: String): ClientPrivateKeysFile? {
}
/**
- * Abstracts the config loading and exception handling.
+ * Abstracts the config loading
*
* @param configFile potentially NULL configuration file location.
* @return the configuration handle.
*/
-fun loadConfigOrFail(configFile: String?): TalerConfig {
+fun loadConfig(configFile: String?): TalerConfig {
val config = TalerConfig(NEXUS_CONFIG_SOURCE)
- try {
- config.load(configFile)
- } catch (e: Exception) {
- logger.error("Could not load configuration from ${configFile}, detail: ${e.message}")
- exitProcess(1)
- }
+ config.load(configFile)
return config
}
/**
* Abstracts fetching the DB config values to set up Nexus.
*/
-fun TalerConfig.extractDbConfigOrFail(): DatabaseConfig =
- try {
- DatabaseConfig(
- dbConnStr = requireString("nexus-postgres", "config"),
- sqlDir = requirePath("libeufin-nexusdb-postgres", "sql_dir")
- )
- } catch (e: Exception) {
- logger.error("Could not load config options for Nexus DB, detail: ${e.message}.")
- exitProcess(1)
- }
+fun TalerConfig.dbConfig(): DatabaseConfig =
+ DatabaseConfig(
+ dbConnStr = requireString("nexus-postgres", "config"),
+ sqlDir = requirePath("libeufin-nexusdb-postgres", "sql_dir")
+ )
/**
* Main CLI class that collects all the subcommands.
diff --git a/util/src/main/kotlin/Cli.kt b/util/src/main/kotlin/Cli.kt
@@ -29,7 +29,6 @@ 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")