libeufin

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

commit b59efeeb55c6fe7af0d1adf77e01bae0d933c32e
parent d1d70485cb8f84f6b32f9a6894c494e11c4b99ca
Author: Antoine A <>
Date:   Tue,  1 Oct 2024 17:01:04 +0200

nexus: lazier config parsing

Diffstat:
Mcommon/src/main/kotlin/TalerConfig.kt | 1+
Mnexus/src/main/kotlin/tech/libeufin/nexus/Config.kt | 23++++++++++-------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt | 13++++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt | 37++++++++++++++++++-------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt | 12++++++------
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt | 12+++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt | 8++++----
Mnexus/src/test/kotlin/DatabaseTest.kt | 18+++++++++---------
Mnexus/src/test/kotlin/RegistrationTest.kt | 10+++++-----
Mtestbench/src/main/kotlin/Main.kt | 9++++-----
Mtestbench/src/test/kotlin/Iso20022Test.kt | 5++---
11 files changed, 70 insertions(+), 78 deletions(-)

diff --git a/common/src/main/kotlin/TalerConfig.kt b/common/src/main/kotlin/TalerConfig.kt @@ -191,6 +191,7 @@ private class ConfigLoader( if (recursionDepth > 128) { throw genericError(file, lineNum, "Recursion limit in config inlining") } + logger.trace("load file at $file") return try { file.useLines { loadFromMem(it, file, recursionDepth+1) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Config.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Config.kt @@ -53,11 +53,8 @@ class NexusSubmitConfig(config: TalerConfig) { val frequencyRaw = section.string("frequency").require() } -class NexusEbicsConfig( - config: TalerConfig, +class NexusEbicsConfig( sect: TalerConfigSection, - val currency: String, - val accountType: AccountType ) { /** The bank base URL */ val hostBaseUrl = sect.string("host_base_url").require() @@ -85,14 +82,6 @@ class NexusEbicsConfig( val bankPublicKeysPath = sect.path("bank_public_keys_file").require() /** Path where we store our private keys */ val clientPrivateKeysPath = sect.path("client_private_keys_file").require() - - val fetch = NexusFetchConfig(config) - val submit = NexusSubmitConfig(config) - val ingest get() = NexusIngestConfig( - accountType, - fetch.ignoreTransactionsBefore, - fetch.ignoreBouncesBefore - ) } class ApiConfig(section: TalerConfigSection) { @@ -116,7 +105,15 @@ class NexusConfig internal constructor (private val cfg: TalerConfig) { "exchange" to AccountType.exchange )).require() - val ebics by lazy { NexusEbicsConfig(cfg, sect, currency, accountType) } + val fetch by lazy { NexusFetchConfig(cfg) } + val submit by lazy { NexusSubmitConfig(cfg) } + val ebics by lazy { NexusEbicsConfig(sect) } + + val ingest get() = NexusIngestConfig( + accountType, + fetch.ignoreTransactionsBefore, + fetch.ignoreBouncesBefore + ) val wireGatewayApiCfg = cfg.section("nexus-httpd-wire-gateway-api").apiConf() val revenueApiCfg = cfg.section("nexus-httpd-revenue-api").apiConf() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt @@ -158,14 +158,14 @@ suspend fun registerTransaction( /** Register a single EBICS [xml] [document] into [db] */ suspend fun registerFile( db: Database, - cfg: NexusEbicsConfig, + cfg: NexusConfig, xml: InputStream, doc: OrderDoc ) { when (doc) { OrderDoc.report, OrderDoc.statement, OrderDoc.notification -> { try { - parseTx(xml, cfg.currency, cfg.dialect).forEach { tx -> + parseTx(xml, cfg.currency, cfg.ebics.dialect).forEach { tx -> registerTransaction(db, cfg.ingest, tx) } } catch (e: Exception) { @@ -247,7 +247,7 @@ suspend fun registerFile( /** Register an EBICS [payload] of [doc] into [db] */ private suspend fun registerPayload( db: Database, - cfg: NexusEbicsConfig, + cfg: NexusConfig, payload: InputStream, doc: OrderDoc ) { @@ -326,9 +326,8 @@ class EbicsFetch: CliktCommand() { private val ebicsLog by ebicsLogOption() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, nexusCgf -> - val cfg = nexusCgf.ebics - val (clientKeys, bankKeys) = expectFullKeys(cfg) + nexusConfig(common.config).withDb { db, cfg -> + val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val client = EbicsClient( cfg, httpClient(), @@ -338,7 +337,7 @@ class EbicsFetch: CliktCommand() { bankKeys ) val docs = if (documents.isEmpty()) OrderDoc.entries else documents.toList() - val requestedOrders = docs.map { cfg.dialect.downloadDoc(it, false) }.toSet() + val requestedOrders = docs.map { cfg.ebics.dialect.downloadDoc(it, false) }.toSet() /** Fetch requested documents only if they have new content */ suspend fun fetchAvailableDocuments(pinnedStartArg: Instant?): Boolean { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt @@ -163,17 +163,16 @@ class EbicsSetup: CliktCommand() { * This function collects the main steps of setting up an EBICS access. */ override fun run() = cliCmd(logger, common.log) { - val nexusCfg = nexusConfig(common.config) - val cfg = nexusCfg.ebics + val cfg = nexusConfig(common.config) val client = httpClient() val ebicsLogger = EbicsLogger(ebicsLog) - val clientKeys = loadOrGenerateClientKeys(cfg.clientPrivateKeysPath) - var bankKeys = loadBankKeys(cfg.bankPublicKeysPath) + val clientKeys = loadOrGenerateClientKeys(cfg.ebics.clientPrivateKeysPath) + var bankKeys = loadBankKeys(cfg.ebics.bankPublicKeysPath) // Check EBICS 3 support - val versions = HEV(client, cfg, ebicsLogger) + val versions = HEV(client, cfg.ebics, ebicsLogger) logger.debug("HEV: {}", versions) if (!versions.contains(VersionNumber(3.0f, "H005")) && !versions.contains(VersionNumber(3.02f, "H005"))) { throw Exception("EBICS 3 is not supported by your bank") @@ -182,17 +181,17 @@ class EbicsSetup: CliktCommand() { // Privs exist. Upload their pubs val keysNotSub = !clientKeys.submitted_ini if ((!clientKeys.submitted_ini) || forceKeysResubmission) - doKeysRequestAndUpdateState(cfg, clientKeys, client, ebicsLogger, INI) + doKeysRequestAndUpdateState(cfg.ebics, clientKeys, client, ebicsLogger, INI) // Eject PDF if the keys were submitted for the first time, or the user asked. - if (keysNotSub || generateRegistrationPdf) makePdf(clientKeys, cfg) + if (keysNotSub || generateRegistrationPdf) makePdf(clientKeys, cfg.ebics) if ((!clientKeys.submitted_hia) || forceKeysResubmission) - doKeysRequestAndUpdateState(cfg, clientKeys, client, ebicsLogger, HIA) + doKeysRequestAndUpdateState(cfg.ebics, clientKeys, client, ebicsLogger, HIA) // Checking if the bank keys exist on disk if (bankKeys == null) { - doKeysRequestAndUpdateState(cfg, clientKeys, client, ebicsLogger, HPB) - logger.info("Bank keys stored at ${cfg.bankPublicKeysPath}") - bankKeys = loadBankKeys(cfg.bankPublicKeysPath)!! + doKeysRequestAndUpdateState(cfg.ebics, clientKeys, client, ebicsLogger, HPB) + logger.info("Bank keys stored at ${cfg.ebics.bankPublicKeysPath}") + bankKeys = loadBankKeys(cfg.ebics.bankPublicKeysPath)!! } if (!bankKeys.accepted) { @@ -204,7 +203,7 @@ class EbicsSetup: CliktCommand() { throw Exception("Cannot successfully finish the setup without accepting the bank keys") } try { - persistBankKeys(bankKeys, cfg.bankPublicKeysPath) + persistBankKeys(bankKeys, cfg.ebics.bankPublicKeysPath) } catch (e: Exception) { throw Exception("Could not set bank keys as accepted on disk", e) } @@ -213,7 +212,7 @@ class EbicsSetup: CliktCommand() { // Check account information logger.info("Doing administrative request HKD") try { - nexusCfg.withDb { db, _ -> + cfg.withDb { db, _ -> EbicsClient( cfg, client, @@ -223,7 +222,7 @@ class EbicsSetup: CliktCommand() { bankKeys ).download(EbicsOrder.V3.HKD, null, null) { stream -> val (partner, users) = EbicsAdministrative.parseHKD(stream) - val user = users.find { it -> it.id == cfg.ebicsUserId } + val user = users.find { it -> it.id == cfg.ebics.ebicsUserId } // Debug logging logger.debug { buildString { @@ -261,18 +260,18 @@ class EbicsSetup: CliktCommand() { } // Check partner info match config - if (partner.name != null && partner.name != cfg.account.name) - logger.warn("Expected NAME '${cfg.account.name}' from config got '${partner.name}' from bank") - val account = partner.accounts.find { it.iban == cfg.account.iban } + if (partner.name != null && partner.name != cfg.ebics.account.name) + logger.warn("Expected NAME '${cfg.ebics.account.name}' from config got '${partner.name}' from bank") + val account = partner.accounts.find { it.iban == cfg.ebics.account.iban } if (account != null) { if (account.currency != null && account.currency != cfg.currency) logger.error("Expected CURRENCY '${cfg.currency}' from config got '${account.currency}' from bank") } else if (partner.accounts.isNotEmpty()) { val ibans = partner.accounts.map { it.iban }.joinToString(", ") - logger.error("Expected IBAN ${cfg.account.iban} from config got $ibans from bank") + logger.error("Expected IBAN ${cfg.ebics.account.iban} from config got $ibans from bank") } - val requireOrders = cfg.dialect.orders() + val requireOrders = cfg.ebics.dialect.orders() // Check partner support required orders val unsupportedOrder = requireOrders subtract partner.orders.map { it.order } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt @@ -64,13 +64,14 @@ private suspend fun submitBatch( client: EbicsClient, batch: PaymentBatch ): String { - val msg = batchToPain001Msg(client.cfg.account, batch) + val ebicsCfg = client.cfg.ebics + val msg = batchToPain001Msg(ebicsCfg.account, batch) val xml = createPain001( msg = msg, - dialect = client.cfg.dialect + dialect = ebicsCfg.dialect ) return client.upload( - client.cfg.dialect.directDebit(), + ebicsCfg.dialect.directDebit(), xml ) } @@ -105,9 +106,8 @@ class EbicsSubmit : CliktCommand() { private val ebicsLog by ebicsLogOption() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, nexusCfg -> - val cfg = nexusCfg.ebics - val (clientKeys, bankKeys) = expectFullKeys(cfg) + nexusConfig(common.config).withDb { db, cfg -> + val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val client = EbicsClient( cfg, httpClient(), diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt @@ -43,9 +43,8 @@ class Wss: CliktCommand() { private val ebicsLog by ebicsLogOption() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, nexusCgf -> - val cfg = nexusCgf.ebics - val (clientKeys, bankKeys) = expectFullKeys(cfg) + nexusConfig(common.config).withDb { db, cfg -> + val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val httpClient = httpClient() val client = EbicsClient( cfg, @@ -91,7 +90,7 @@ class FakeIncoming: CliktCommand() { "Wrong currency: expected ${cfg.currency} got ${amount.currency}" } - registerIncomingPayment(db, cfg.ebics.ingest, + registerIncomingPayment(db, cfg.ingest, IncomingPayment( amount = amount, debtorPayto = payto, @@ -154,9 +153,8 @@ class EbicsDownload: CliktCommand("ebics-btd") { class DryRun: Exception() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, nexusCgf -> - val cfg = nexusCgf.ebics - val (clientKeys, bankKeys) = expectFullKeys(cfg) + nexusConfig(common.config).withDb { db, cfg -> + val (clientKeys, bankKeys) = expectFullKeys(cfg.ebics) val pinnedStartVal = pinnedStart val pinnedStartArg = if (pinnedStartVal != null) { logger.debug("Pinning start date to: $pinnedStartVal") diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -127,7 +127,7 @@ suspend fun EbicsBTS.postBTS( /** High level EBICS client */ class EbicsClient( - val cfg: NexusEbicsConfig, + val cfg: NexusConfig, val client: HttpClient, val db: Database, val ebicsLogger: EbicsLogger, @@ -151,7 +151,7 @@ class EbicsClient( val description = order.description() logger.debug { "Download order $description" } val txLog = ebicsLogger.tx(order) - val impl = EbicsBTS(cfg, bankKeys, clientKeys, order) + val impl = EbicsBTS(cfg.ebics, bankKeys, clientKeys, order) // Close pending while (true) { @@ -248,8 +248,8 @@ class EbicsClient( val description = order.description(); logger.debug { "Upload order $description" } val txLog = ebicsLogger.tx(order) - val impl = EbicsBTS(cfg, bankKeys, clientKeys, order) - val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload) + val impl = EbicsBTS(cfg.ebics, bankKeys, clientKeys, order) + val preparedPayload = prepareUploadPayload(cfg.ebics, clientKeys, bankKeys, payload) // Init phase val initXml = impl.uploadInitialization(preparedPayload) diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt @@ -319,29 +319,29 @@ class PaymentInitiationsTest { genInPay("test at $executionTime", executionTime = executionTime), genOutPay("test at $executionTime", executionTime = executionTime) )) { - registerTransaction(db, cfg.ebics.ingest, tx) + registerTransaction(db, cfg.ingest, tx) } } - assertEquals(cfg.ebics.fetch.ignoreTransactionsBefore, dateToInstant("2024-04-04")) - assertEquals(cfg.ebics.fetch.ignoreBouncesBefore, dateToInstant("2024-06-12")) + assertEquals(cfg.fetch.ignoreTransactionsBefore, dateToInstant("2024-04-04")) + assertEquals(cfg.fetch.ignoreBouncesBefore, dateToInstant("2024-06-12")) // No transaction at the beginning checkCount(0, 0) // Skipped transactions - ingest(cfg.ebics.fetch.ignoreTransactionsBefore.minusMillis(10)) + ingest(cfg.fetch.ignoreTransactionsBefore.minusMillis(10)) checkCount(0, 0) // Skipped bounces - ingest(cfg.ebics.fetch.ignoreTransactionsBefore) - ingest(cfg.ebics.fetch.ignoreTransactionsBefore.plusMillis(10)) - ingest(cfg.ebics.fetch.ignoreBouncesBefore.minusMillis(10)) + ingest(cfg.fetch.ignoreTransactionsBefore) + ingest(cfg.fetch.ignoreTransactionsBefore.plusMillis(10)) + ingest(cfg.fetch.ignoreBouncesBefore.minusMillis(10)) checkCount(6, 0) // Bounces - ingest(cfg.ebics.fetch.ignoreBouncesBefore) - ingest(cfg.ebics.fetch.ignoreBouncesBefore.plusMillis(10)) + ingest(cfg.fetch.ignoreBouncesBefore) + ingest(cfg.fetch.ignoreBouncesBefore.plusMillis(10)) checkCount(10, 2) } diff --git a/nexus/src/test/kotlin/RegistrationTest.kt b/nexus/src/test/kotlin/RegistrationTest.kt @@ -50,7 +50,7 @@ class RegistrationTest { /** Register an XML sample into the database */ suspend fun Database.register( - cfg: NexusEbicsConfig, + cfg: NexusConfig, path: String, doc: OrderDoc ) { @@ -196,7 +196,7 @@ class RegistrationTest { )) // Register HAC files - db.register(cfg.ebics, "sample/platform/hac.xml", OrderDoc.acknowledgement) + db.register(cfg, "sample/platform/hac.xml", OrderDoc.acknowledgement) // Check state db.check( @@ -228,7 +228,7 @@ class RegistrationTest { )) // Register pain files - db.register(cfg.ebics, "sample/platform/pain002.xml", OrderDoc.status) + db.register(cfg, "sample/platform/pain002.xml", OrderDoc.status) // Check state db.check( @@ -284,8 +284,8 @@ class RegistrationTest { )) // Register camt files - db.register(cfg.ebics, "sample/platform/gls_camt052.xml", OrderDoc.report) - db.register(cfg.ebics, "sample/platform/gls_camt053.xml", OrderDoc.statement) + db.register(cfg, "sample/platform/gls_camt052.xml", OrderDoc.report) + db.register(cfg, "sample/platform/gls_camt053.xml", OrderDoc.statement) // TODO camt054 with missing id before and after // Check state diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt @@ -103,11 +103,10 @@ class Cli : CliktCommand() { [libeufin-nexusdb-postgres] CONFIG = postgres:///libeufintestbench """) - val nexusCfg = nexusConfig(conf) - val cfg = nexusCfg.ebics + val cfg = nexusConfig(conf) // Check if platform is known - val kind = when (cfg.hostBaseUrl) { + val kind = when (cfg.ebics.hostBaseUrl) { "https://isotest.postfinance.ch/ebicsweb/ebicsweb" -> Kind("PostFinance IsoTest", "https://isotest.postfinance.ch/corporates/user/settings/ebics") "https://iso20022test.credit-suisse.com/ebicsweb/ebicsweb" -> @@ -126,8 +125,8 @@ class Cli : CliktCommand() { val flags = " -c $conf -L $log" val debugFlags = "$flags --debug-ebics test/$platform" val ebicsFlags = "$debugFlags --transient" - val clientKeysPath = cfg.clientPrivateKeysPath - val bankKeysPath = cfg.bankPublicKeysPath + val clientKeysPath = cfg.ebics.clientPrivateKeysPath + val bankKeysPath = cfg.ebics.bankPublicKeysPath val currency = cfg.currency val dummyPaytos = mapOf( diff --git a/testbench/src/test/kotlin/Iso20022Test.kt b/testbench/src/test/kotlin/Iso20022Test.kt @@ -100,10 +100,9 @@ class Iso20022Test { } // Load config - val nexusCfg = nexusConfig(platform.resolve("ebics.conf")) - val cfg = nexusCfg.ebics + val cfg = nexusConfig(platform.resolve("ebics.conf")) val currency = cfg.currency - val dialect = cfg.dialect + val dialect = cfg.ebics.dialect // Parse logs for (log in logs) { val content = Files.newInputStream(log)