commit b2dff673b9a0d31457ed7648bcd24aa50dca730d
parent fcfd5497b88399fa7c6f74e61c868b2e9b947ecf
Author: MS <ms@taler.net>
Date: Wed, 8 Nov 2023 10:24:40 +0100
nexus fetch: drafting the CLI
Diffstat:
3 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -0,0 +1,91 @@
+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 io.ktor.client.*
+import kotlin.concurrent.fixedRateTimer
+import kotlin.system.exitProcess
+
+/**
+ * Fetches the banking records via EBICS, calling the CAMT
+ * parsing logic and finally updating the database accordingly.
+ *
+ * @param cfg config handle.
+ * @param db database connection
+ * @param httpClient HTTP client handle to reach the bank
+ * @param clientKeys EBICS subscriber private keys.
+ * @param bankKeys bank public keys.
+ */
+fun fetchHistory(
+ cfg: EbicsSetupConfig,
+ db: Database,
+ httpClient: HttpClient,
+ clientKeys: ClientPrivateKeysFile,
+ bankKeys: BankPublicKeysFile
+) {
+ throw NotImplementedError()
+}
+
+class EbicsFetch: CliktCommand("Fetches bank records") {
+ private val configFile by option(
+ "--config", "-c",
+ help = "set the configuration file"
+ )
+ private val transient by option(
+ "--transient",
+ help = "This flag fetches only once from the bank and returns, " +
+ "ignoring the 'frequency' configuration value"
+ ).flag(default = false)
+
+ /**
+ * This function collects the main steps of fetching banking records.
+ * In this current version, it does not implement long polling, instead
+ * it runs transient if FREQUENCY is zero. Transient is also the default
+ * mode when no flags are passed to the invocation.
+ * FIXME: reduce code duplication with the submit subcommand.
+ */
+ override fun run() {
+ val cfg: EbicsSetupConfig = doOrFail {
+ extractEbicsConfig(configFile)
+ }
+ // Fail now if keying is incomplete.
+ if (!isKeyingComplete(cfg)) exitProcess(1)
+ val dbCfg = cfg.config.extractDbConfigOrFail()
+ 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 fetch any records.")
+ exitProcess(1)
+ }
+ val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
+ if (clientKeys == null) {
+ logger.error("Client private keys not found at: ${cfg.clientPrivateKeysFilename}")
+ exitProcess(1)
+ }
+ val httpClient = HttpClient()
+ if (transient) {
+ logger.info("Transient mode: fetching once and returning.")
+ fetchHistory(cfg, db, httpClient, clientKeys, bankKeys)
+ return
+ }
+ val frequency: NexusFrequency = doOrFail {
+ val configValue = cfg.config.requireString("nexus-fetch", "frequency")
+ val frequencySeconds = checkFrequency(configValue)
+ return@doOrFail 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")
+ fetchHistory(cfg, db, httpClient, clientKeys, bankKeys)
+ return
+ }
+ fixedRateTimer(
+ name = "ebics submit period",
+ period = (frequency.inSeconds * 1000).toLong(),
+ action = {
+ fetchHistory(cfg, db, httpClient, clientKeys, bankKeys)
+ }
+ )
+ }
+}
+\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -251,6 +251,7 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat
* Submits any initiated payment that was not submitted
* so far and -- according to the configuration -- returns
* or long-polls (currently not implemented) for new payments.
+ * FIXME: reduce code duplication with the fetch subcommand.
*/
override fun run() {
val cfg: EbicsSetupConfig = doOrFail {
@@ -260,7 +261,6 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat
if (!isKeyingComplete(cfg)) exitProcess(1)
val dbCfg = cfg.config.extractDbConfigOrFail()
val db = Database(dbCfg.dbConnStr)
- val httpClient = HttpClient()
val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename) ?: exitProcess(1)
if (!bankKeys.accepted) {
logger.error("Bank keys are not accepted, yet. Won't submit any payment.")
@@ -271,6 +271,7 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat
logger.error("Client private keys not found at: ${cfg.clientPrivateKeysFilename}")
exitProcess(1)
}
+ val httpClient = HttpClient()
if (transient) {
logger.info("Transient mode: submitting what found and returning.")
submitBatch(cfg, db, httpClient, clientKeys, bankKeys)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -303,7 +303,7 @@ fun TalerConfig.extractDbConfigOrFail(): DatabaseConfig =
class LibeufinNexusCommand : CliktCommand() {
init {
versionOption(getVersion())
- subcommands(EbicsSetup(), DbInit(), EbicsSubmit())
+ subcommands(EbicsSetup(), DbInit(), EbicsSubmit(), EbicsFetch())
}
override fun run() = Unit
}