libeufin

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

commit c47bf440794343d46edeec979817dbbf975a07fd
parent fdb0dd338087815af47f84278c4f5bebcf8fa415
Author: Antoine A <>
Date:   Fri,  1 Aug 2025 16:40:02 +0200

nexus: improve ack logic

Diffstat:
Mdatabase-versioning/libeufin-nexus-0012.sql | 2+-
Mdatabase-versioning/libeufin-nexus-procedures.sql | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/List.kt | 51++++++++++++++++++++++++++++++++-------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/db/ListDAO.kt | 41+++++++++++++++++++++++++++++++++++++++--
Mtestbench/src/main/kotlin/Main.kt | 7++++---
6 files changed, 78 insertions(+), 27 deletions(-)

diff --git a/database-versioning/libeufin-nexus-0012.sql b/database-versioning/libeufin-nexus-0012.sql @@ -20,6 +20,6 @@ SELECT _v.register_patch('libeufin-nexus-0012', NULL, NULL); SET search_path TO libeufin_nexus; ALTER TABLE initiated_outgoing_transactions - ADD COLUMN awaiting_ack BOOLEAN NOT NULL DEFAULT FALSE; + ADD COLUMN awaiting_ack BOOLEAN NOT NULL DEFAULT TRUE; COMMIT; diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql @@ -488,7 +488,7 @@ local_sum taler_amount DEFAULT (0, 0)::taler_amount; tx record; BEGIN IF require_ack THEN - pending = EXISTS(SELECT FROM initiated_outgoing_transactions WHERE initiated_outgoing_batch_id IS NULL AND awaiting_ack); + pending = EXISTS(SELECT FROM initiated_outgoing_transactions WHERE initiated_outgoing_batch_id IS NULL AND NOT awaiting_ack); ELSE pending = EXISTS(SELECT FROM initiated_outgoing_transactions WHERE initiated_outgoing_batch_id IS NULL); END IF; diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/List.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/List.kt @@ -115,26 +115,39 @@ class ListInitiated: TalerCmd("initiated") { override fun run() = cliCmd(logger) { nexusConfig(config).withDb { db, cfg -> - val txs = db.list.initiated(awaitingAck) - for (tx in txs) { - println(buildString{ - append("${tx.date} ${tx.id} ${tx.amount}\n") - append(" creditor: ${fmtPayto(tx.creditor)}\n") - append(" subject: ${tx.subject}\n") - if (tx.batch != null) { - append(" batch: ${tx.batch}") - if (tx.batchOrder != null) - append(" ${tx.batchOrder}") + if (awaitingAck) { + val txs = db.list.initiatedAck() + for (tx in txs) { + println(buildString{ + append("${tx.date} ${tx.id} ${tx.amount}\n") + append(" creditor: ${fmtPayto(tx.creditor)}\n") + append(" subject: ${tx.subject}\n") + append(" ack: ${tx.dbId}") append('\n') - } - append(" submission: ${tx.submissionTime} ${tx.submissionCounter}\n") - append(" status: ${tx.status}") - if (tx.msg != null) { - append(" ${tx.msg}") - } - append('\n') - }) - } + }) + } + } else { + val txs = db.list.initiated() + for (tx in txs) { + println(buildString{ + append("${tx.date} ${tx.id} ${tx.amount}\n") + append(" creditor: ${fmtPayto(tx.creditor)}\n") + append(" subject: ${tx.subject}\n") + if (tx.batch != null) { + append(" batch: ${tx.batch}") + if (tx.batchOrder != null) + append(" ${tx.batchOrder}") + append('\n') + } + append(" submission: ${tx.submissionTime} ${tx.submissionCounter}\n") + append(" status: ${tx.status}") + if (tx.msg != null) { + append(" ${tx.msg}") + } + append('\n') + }) + } + } } } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt @@ -305,7 +305,7 @@ class InitiatedDAO(private val db: Database) { } suspend fun ack(id: Long): Boolean { - return db.serializable("UPDATE initiated_outgoing_transactions SET awaiting_ack=true") { + return db.serializable("UPDATE initiated_outgoing_transactions SET awaiting_ack=false") { executeUpdateCheck() } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ListDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ListDAO.kt @@ -112,7 +112,7 @@ class ListDAO(private val db: Database) { } /** List initiated transaction metadata for debugging */ - suspend fun initiated(awaitingAck: Boolean): List<InitiatedTxMetadata> = db.serializable( + suspend fun initiated(): List<InitiatedTxMetadata> = db.serializable( """ SELECT (amount).val AS amount_val @@ -129,7 +129,6 @@ class ListDAO(private val db: Database) { ,initiated_outgoing_transactions.status_msg FROM initiated_outgoing_transactions LEFT JOIN initiated_outgoing_batches USING (initiated_outgoing_batch_id) - ${if (awaitingAck) "WHERE initiated_outgoing_transactions.initiated_outgoing_batch_id IS NULL AND awaiting_ack = true" else ""} ORDER BY initiation_time """ ) { @@ -149,6 +148,34 @@ class ListDAO(private val db: Database) { ) } } + + /** List initiated transaction metadata pending acknowledgment for debugging */ + suspend fun initiatedAck(): List<InitiatedTxMetadataAck> = db.serializable( + """ + SELECT + (amount).val AS amount_val + ,(amount).frac AS amount_frac + ,subject + ,initiation_time + ,credit_payto + ,end_to_end_id + ,initiated_outgoing_transaction_id + FROM initiated_outgoing_transactions + WHERE initiated_outgoing_batch_id IS NULL AND NOT awaiting_ack + ORDER BY initiation_time + """ + ) { + all { + InitiatedTxMetadataAck( + date = it.getLong("initiation_time").asInstant(), + amount = it.getAmount("amount", db.currency), + subject = it.getString("subject"), + creditor = it.getString("credit_payto"), + id = it.getString("end_to_end_id"), + dbId = it.getLong("initiated_outgoing_transaction_id"), + ) + } + } } /** Incoming transaction metadata for debugging */ @@ -187,4 +214,14 @@ data class InitiatedTxMetadata( val msg: String?, val submissionTime: Instant, val submissionCounter: Int +) + +/** Initiated metadata for debugging */ +data class InitiatedTxMetadataAck( + val date: Instant, + val amount: TalerAmount, + val subject: String, + val creditor: String, + val dbId: Long, + val id: String ) \ No newline at end of file diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt @@ -208,9 +208,10 @@ class Cli : CliktCommand() { put("report", "Fetch BankToCustomerAccountReport", "ebics-fetch $ebicsFlags report") put("notification", "Fetch BankToCustomerDebitCreditNotification", "ebics-fetch $ebicsFlags notification") put("statement", "Fetch BankToCustomerStatement", "ebics-fetch $ebicsFlags statement") - put("list-incoming", "List incoming transaction", "testing list $flags incoming") - put("list-outgoing", "List outgoing transaction", "testing list $flags outgoing") - put("list-initiated", "List initiated payments", "testing list $flags initiated") + put("list-incoming", "List incoming transaction", "list incoming $flags") + put("list-outgoing", "List outgoing transaction", "list outgoing $flags") + put("list-initiated", "List initiated payments", "list initiated $flags") + put("list-ack", "List initiated payments pending manual submission acknowledgement", "list initiated $flags --awaiting-ack") put("wss", "Listen to notification over websocket", "testing wss $debugFlags") put("submit", "Submit pending transactions", "ebics-submit $ebicsFlags") put("export", "Export pending batches as pain001 messages", "manual export $flags payments.zip")