libeufin

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

commit 225bc67cbecee0f031bf3dfb4d1288235d0720da
parent 036598c3f3ae999ed3be17f60e53ac436f845799
Author: ms <ms@taler.net>
Date:   Sun,  9 Jan 2022 16:15:47 +0100

implement history API

Diffstat:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt | 19+++++++++++++++++++
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 36+++++++++++++++++++++++++++++-------
2 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt @@ -338,10 +338,21 @@ object BankAccountTransactionsTable : LongIdTable() { class BankAccountTransactionEntity(id: EntityID<Long>) : LongEntity(id) { companion object : LongEntityClass<BankAccountTransactionEntity>(BankAccountTransactionsTable) { override fun new(init: BankAccountTransactionEntity.() -> Unit): BankAccountTransactionEntity { + /** + * Fresh transactions are those that wait to be included in a + * "history" report, likely a Camt.5x message. The "fresh transactions" + * table keeps a list of such transactions. + */ val freshTx = super.new(init) BankAccountFreshTransactionsTable.insert { it[transactionRef] = freshTx.id } + /** + * The bank account involved in this transaction points to + * it as the "last known" transaction, to make it easier to + * build histories that depend on such record. + */ + freshTx.account.lastTransaction = freshTx return freshTx } } @@ -384,6 +395,13 @@ object BankAccountsTable : IntIdTable() { val owner = text("owner") val isPublic = bool("isPublic").default(false) val demoBank = reference("demoBank", DemobankConfigsTable) + + /** + * Point to the last transaction related to this account, regardless + * of it being credit or debit. This reference helps to construct + * history results that start from / depend on the last transaction. + */ + val lastTransaction = reference("lastTransaction", BankAccountTransactionsTable).nullable() } class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) { @@ -395,6 +413,7 @@ class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) { var owner by BankAccountsTable.owner var isPublic by BankAccountsTable.isPublic var demoBank by DemobankConfigEntity referencedOn BankAccountsTable.demoBank + var lastTransaction by BankAccountTransactionEntity optionalReferencedOn BankAccountsTable.lastTransaction } object BankAccountStatementsTable : IntIdTable() { diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt @@ -1279,8 +1279,7 @@ val sandboxApp: Application.() -> Unit = { }) return@get } - // This endpoint is being called "GET /transaction" in the docs. - get("/accounts/{account_name}/history") { + get("/accounts/{account_name}/transactions") { val demobank = ensureDemobank(call) val bankAccount = getBankAccountFromLabel( call.getUriComponent("account_name"), @@ -1290,13 +1289,36 @@ val sandboxApp: Application.() -> Unit = { if (!authOk && (call.request.basicAuth() != bankAccount.owner)) throw forbidden( "Cannot access bank account ${bankAccount.label}" ) + + val page: Int = Integer.decode(call.request.queryParameters["page"] ?: "1") + val size: Int = Integer.decode(call.request.queryParameters["size"] ?: "5") + val ret = mutableListOf<RawPayment>() + /** + * Case where page number wasn't given, + * therefore the results starts from the last transaction. */ transaction { - BankAccountTransactionEntity.find { - BankAccountTransactionsTable.account eq bankAccount.id - // FIXME: more criteria to come. - }.forEach { - ret.add(getHistoryElementFromTransactionRow(it)) + /** + * Get a history page - from the calling bank account - having + * 'firstElementId' as the latest transaction in it. */ + fun getPage(firstElementId: Long): Iterable<BankAccountTransactionEntity> { + return BankAccountTransactionEntity.find { + (BankAccountTransactionsTable.id lessEq firstElementId) and + (BankAccountTransactionsTable.account eq bankAccount.id) + }.take(size) + } + val lt: BankAccountTransactionEntity? = bankAccount.lastTransaction + if (lt == null) return@transaction + var firstElement: BankAccountTransactionEntity = lt + /** + * This loop fetches (and discards) pages until the + * desired one is found. */ + for (i in 0..(page)) { + val pageBuf = getPage(firstElement.id.value) + firstElement = pageBuf.last() + if (i == page) pageBuf.forEach { + ret.add(getHistoryElementFromTransactionRow(it)) + } } } call.respond(ret)