libeufin

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

commit 07e2183998b20585c4e8654137555312ee1e1836
parent 16e22caf9a4117beb7006ae997981f5fe6dc9495
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Wed,  8 Apr 2020 18:15:50 +0200

Complete history-outgoing endpoint.

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 8++++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
2 files changed, 70 insertions(+), 16 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -42,6 +42,14 @@ fun ApplicationCall.expectUrlParameter(name: String): String { ?: throw NexusError(HttpStatusCode.BadRequest, "Parameter '$name' not provided in URI") } +fun expectInt(param: String): Int { + return try { + param.toInt() + } catch (e: Exception) { + throw NexusError(HttpStatusCode.BadRequest,"'$param' is not Int") + } +} + fun expectLong(param: String): Long { return try { param.toLong() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -8,7 +8,10 @@ import io.ktor.response.respondText import io.ktor.routing.Route import io.ktor.routing.get import io.ktor.routing.post +import io.ktor.routing.route +import org.jetbrains.exposed.dao.Entity import org.jetbrains.exposed.dao.EntityID +import org.jetbrains.exposed.dao.LongEntity import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq import org.jetbrains.exposed.sql.transactions.transaction @@ -19,6 +22,7 @@ import tech.libeufin.util.CryptoUtil import tech.libeufin.util.base64ToBytes import java.lang.Exception import javax.sql.rowset.serial.SerialBlob +import kotlin.math.abs /** * This helper function parses a Authorization:-header line, decode the credentials @@ -121,8 +125,8 @@ class Taler(app: Route) { val row_id: Long ) - private fun SizedIterable<TalerIncomingPaymentEntry>.orderTaler(start: Long): List<TalerIncomingPaymentEntry> { - return if (start < 0) { + private fun <T : Entity<Long>> SizedIterable<T>.orderTaler(delta: Int): List<T> { + return if (delta < 0) { this.sortedByDescending { it.id } } else { this.sortedBy { it.id } @@ -149,22 +153,19 @@ class Taler(app: Route) { return subscriber.id.value } - /** - * Implement the Taler wire API transfer method. - */ - private fun transfer(app: Route) { - - } - private fun getPaytoUri(name: String, iban: String, bic: String): String { return "payto://$iban/$bic?receiver-name=$name" } + private fun parseDate(date: String): DateTime { + return DateTime.parse(date, DateTimeFormat.forPattern("YYYY-MM-DD")) + } + /** * Builds the comparison operator for history entries based on the * sign of 'delta' */ - private fun getComparisonOperator(delta: Long, start: Long): Op<Boolean> { + private fun getComparisonOperator(delta: Int, start: Long): Op<Boolean> { return if (delta < 0) { Expression.build { TalerIncomingPayments.id less start @@ -179,7 +180,7 @@ class Taler(app: Route) { /** * Helper handling 'start' being optional and its dependence on 'delta'. */ - private fun handleStartArgument(start: String?, delta: Long): Long { + private fun handleStartArgument(start: String?, delta: Int): Long { return expectLong(start) ?: if (delta >= 0) { /** * Using -1 as the smallest value, as some DBMS might use 0 and some @@ -198,6 +199,17 @@ class Taler(app: Route) { } /** + * Implement the Taler wire API transfer method. + */ + private fun transfer(app: Route) { + app.post("/taler/transfer") { + call.respond(HttpStatusCode.OK, NexusErrorJson("Not implemented")) + return@post + } + } + + + /** * Respond with ONLY the good transfer made to the exchange. * A 'good' transfer is one whose subject line is a plausible * EdDSA public key encoded in Crockford base32. @@ -205,18 +217,18 @@ class Taler(app: Route) { private fun historyIncoming(app: Route) { app.get("/taler/history/incoming") { val subscriberId = authenticateRequest(call.request.headers["Authorization"]) - val delta: Long = expectLong(call.expectUrlParameter("delta")) + val delta: Int = expectInt(call.expectUrlParameter("delta")) val start: Long = handleStartArgument(call.request.queryParameters["start"], delta) val history = TalerIncomingHistory() - val cmpOp = getComparisonOperator(delta, start) + val startCmpOp = getComparisonOperator(delta, start) transaction { val subscriberBankAccount = getBankAccountsInfoFromId(subscriberId) TalerIncomingPaymentEntry.find { - TalerIncomingPayments.valid eq true and cmpOp - }.orderTaler(start).forEach { + TalerIncomingPayments.valid eq true and startCmpOp + }.orderTaler(delta).subList(0, abs(delta)).forEach { history.incoming_transactions.add( TalerIncomingBankTransaction( - date = DateTime.parse(it.payment.bookingDate, DateTimeFormat.forPattern("YYYY-MM-DD")).millis, + date = parseDate(it.payment.bookingDate).millis / 1000, // timestamp in seconds row_id = it.id.value, amount = "${it.payment.currency}:${it.payment.amount}", reserve_pub = it.payment.unstructuredRemittanceInformation, @@ -241,7 +253,41 @@ class Taler(app: Route) { * incoming payment. */ private fun historyOutgoing(app: Route) { + app.get("/taler/history/outgoing") { + + /* sanitize URL arguments */ + val subscriberId = authenticateRequest(call.request.headers["Authorization"]) + val delta: Int = expectInt(call.expectUrlParameter("delta")) + val start: Long = handleStartArgument(call.request.queryParameters["start"], delta) + val startCmpOp = getComparisonOperator(delta, start) + /* retrieve database elements */ + val history = TalerOutgoingHistory() + transaction { + /** Retrieve all the outgoing payments from the _raw transactions table_ */ + val subscriberBankAccount = getBankAccountsInfoFromId(subscriberId) + EbicsRawBankTransactionEntry.find { + EbicsRawBankTransactionsTable.debitorIban eq subscriberBankAccount.first().iban and startCmpOp + }.orderTaler(delta).subList(0, abs(delta)).forEach { + history.outgoing_transactions.add( + TalerOutgoingBankTransaction( + row_id = it.id.value, + amount = "${it.currency}:${it.amount}", + wtid = it.unstructuredRemittanceInformation, + date = parseDate(it.bookingDate).millis / 1000, + credit_account = it.creditorIban, + debit_account = it.debitorIban, + exchange_base_url = "FIXME-to-request-along-subscriber-registration" + ) + ) + } + } + call.respond( + HttpStatusCode.OK, + history + ) + return@get + } } private fun testAuth(app: Route) {