libeufin

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

commit 5f9ad837016c66fe974e413432b2785b58268820
parent e7b3ed81061507985eca53dd67b07b4406ade989
Author: MS <ms@taler.net>
Date:   Tue, 23 Jun 2020 15:04:15 +0200

Import account: not storing raw XML HTD response.

Put values in table columns instead.

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 36++++++++++++++++++------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 91++++++++++++++++++++++++++++++++-----------------------------------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt | 2+-
3 files changed, 56 insertions(+), 73 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt @@ -24,13 +24,9 @@ import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.LongIdTable -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.StdOutSqlLogger -import org.jetbrains.exposed.sql.addLogger +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.transaction -import tech.libeufin.nexus.NexusBankAccountsTable.entityId import tech.libeufin.util.EbicsInitState import tech.libeufin.util.amount import java.sql.Connection @@ -211,21 +207,25 @@ class PaymentInitiationEntity(id: EntityID<Long>) : LongEntity(id) { } /** - * This table associates a bank connection with the raw XML response - * coming from a HTD message. The main purpose here is to store (possibly - * temporarily) the bank accounts belonging to one subscriber, in order - * to allow this latter to import them using more meaningful labels. + * This table contains the bank accounts that are offered by the bank. + * The bank account label (as assigned by the bank) is the primary key. */ -object RawHTDResponsesTable : IdTable<String>() { - // the bank-connection that was used to download this data. - // FIXME: this column should be made a foreign key. - // FIXME: change this table name to something like "offered bank accounts" +object OfferedBankAccountsTable : IdTable<String>() { override val id = text("id").entityId() - val htdResponse = text("htdResponse") + val bankConnection = reference("bankConnection", NexusBankConnectionsTable) + val iban = text("iban") + val bankCode = text("bankCode") + val accountHolder = text("holderName") + val imported = bool("imported").default(false) } -class RawHTDResponseEntity(id: EntityID<String>) : Entity<String>(id) { - companion object : EntityClass<String, RawHTDResponseEntity>(RawHTDResponsesTable) - var htdResponse by RawHTDResponsesTable.htdResponse + +class OfferedBankAccountEntity(id: EntityID<String>) : Entity<String>(id) { + companion object : EntityClass<String, OfferedBankAccountEntity>(OfferedBankAccountsTable) + var bankConnection by NexusBankConnectionEntity referencedOn OfferedBankAccountsTable.bankConnection + var iban by OfferedBankAccountsTable.iban + var bankCode by OfferedBankAccountsTable.bankCode + var accountHolder by OfferedBankAccountsTable.accountHolder + var imported by OfferedBankAccountsTable.imported } /** @@ -405,7 +405,7 @@ fun dbCreateTables(dbName: String) { FacadesTable, TalerFacadeStateTable, NexusScheduledTasksTable, - RawHTDResponsesTable + OfferedBankAccountsTable ) } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt @@ -42,6 +42,8 @@ import io.ktor.response.respondText import io.ktor.routing.Route import io.ktor.routing.get import io.ktor.routing.post +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.not import org.jetbrains.exposed.sql.statements.api.ExposedBlob import org.jetbrains.exposed.sql.transactions.transaction import tech.libeufin.nexus.* @@ -335,7 +337,6 @@ fun Route.ebicsBankProtocolRoutes(client: HttpClient) { fun Route.ebicsBankConnectionRoutes(client: HttpClient) { post("/send-ini") { val subscriber = transaction { - val user = authenticateRequest(call.request) val conn = requireBankConnection(call, "connid") if (conn.type != "ebics") { throw NexusError( @@ -351,7 +352,6 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { post("/send-hia") { val subscriber = transaction { - val user = authenticateRequest(call.request) val conn = requireBankConnection(call, "connid") if (conn.type != "ebics") { throw NexusError(HttpStatusCode.BadRequest, "bank connection is not of type 'ebics'") @@ -364,7 +364,6 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { post("/send-hev") { val subscriber = transaction { - val user = authenticateRequest(call.request) val conn = requireBankConnection(call, "connid") if (conn.type != "ebics") { throw NexusError(HttpStatusCode.BadRequest, "bank connection is not of type 'ebics'") @@ -377,7 +376,6 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { post("/send-hpb") { val subscriberDetails = transaction { - val user = authenticateRequest(call.request) val conn = requireBankConnection(call, "connid") if (conn.type != "ebics") { throw NexusError(HttpStatusCode.BadRequest, "bank connection is not of type 'ebics'") @@ -394,6 +392,7 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { } call.respond(object {}) } + // persists raw / to-be-imported bank accounts post("/accounts/fetch") { val res = transaction { authenticateRequest(call.request) @@ -417,80 +416,65 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { ) } is EbicsDownloadSuccessResult -> { + val payload = XMLUtil.convertStringToJaxb<HTDResponseOrderData>( + response.orderData.toString(Charsets.UTF_8) + ) transaction { - RawHTDResponseEntity.new(res.connid) { - htdResponse = response.orderData.toString(Charsets.UTF_8) + val conn = requireBankConnection(call, "connid") + payload.value.partnerInfo.accountInfoList?.forEach { + OfferedBankAccountEntity.new(id = it.id) { + accountHolder = it.accountHolder ?: "NOT-GIVEN" + iban = it.accountNumberList?.filterIsInstance<EbicsTypes.GeneralAccountNumber>() + ?.find { it.international }?.value + ?: throw NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN") + bankCode = it.bankCodeList?.filterIsInstance<EbicsTypes.GeneralBankCode>() + ?.find { it.international }?.value + ?: throw NexusError( + HttpStatusCode.NotFound, + reason = "bank gave no BIC" + ) + bankConnection = conn + } } } } } call.respond(object {}) } - get("/accounts/imported") { - var ret = BankAccounts() - transaction { - val conn = requireBankConnection(call, "connid") - NexusBankAccountEntity.find { - NexusBankAccountsTable.defaultBankConnection eq conn.id.value - }.forEach { ret.accounts.add( - BankAccount(holder = it.accountHolder, iban = it.iban, bic = it.bankCode, account = it.id.value) - ) } - } - call.respond(ret) - } + + // show only non imported accounts. get("/accounts") { val ret = BankAccounts() transaction { val conn = requireBankConnection(call, "connid") - val hasXml = RawHTDResponseEntity.findById(conn.id.value) ?: throw NexusError( - HttpStatusCode.NotFound, "Bank connection ${conn.id.value} never called HTD" - ) - val payload = XMLUtil.convertStringToJaxb<HTDResponseOrderData>(hasXml.htdResponse) - payload.value.partnerInfo.accountInfoList?.forEach { + OfferedBankAccountEntity.find { + OfferedBankAccountsTable.bankConnection eq conn.id.value and not(OfferedBankAccountsTable.imported) + }.forEach { ret.accounts.add( BankAccount( - holder = it.accountHolder ?: "NOT-GIVEN", - iban = it.accountNumberList?.filterIsInstance<EbicsTypes.GeneralAccountNumber>() - ?.find { it.international }?.value - ?: throw NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN"), - bic = it.bankCodeList?.filterIsInstance<EbicsTypes.GeneralBankCode>() - ?.find { it.international }?.value - ?: throw NexusError( - HttpStatusCode.NotFound, - reason = "bank gave no BIC" - ), - account = it.id + iban = it.iban, bic = it.bankCode, holder = it.accountHolder, account = it.id.value ) ) } } call.respond(ret) } + post("/accounts/import") { val body = call.receive<ImportBankAccount>() transaction { val conn = requireBankConnection(call, "connid") - val hasXml = RawHTDResponseEntity.findById(conn.id.value) ?: throw NexusError( - HttpStatusCode.NotFound, "Could not found raw bank account data for connection '${conn.id.value}'" + val account = OfferedBankAccountEntity.findById(body.accountId) ?: throw NexusError( + HttpStatusCode.NotFound, "Could not found raw bank account '${body.accountId}'" ) - XMLUtil.convertStringToJaxb<HTDResponseOrderData>(hasXml.htdResponse).value.partnerInfo.accountInfoList?.forEach { - if (it.id == body.accountId) { - NexusBankAccountEntity.new(body.localName) { - iban = it.accountNumberList?.filterIsInstance<EbicsTypes.GeneralAccountNumber>() - ?.find { it.international }?.value - ?: throw NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN") - bankCode = it.bankCodeList?.filterIsInstance<EbicsTypes.GeneralBankCode>() - ?.find { it.international }?.value - ?: throw NexusError( - HttpStatusCode.NotFound, - reason = "bank gave no BIC" - ) - defaultBankConnection = conn - highestSeenBankMessageId = 0 - accountHolder = it.accountHolder ?: "NOT GIVEN" - } - } + NexusBankAccountEntity.new(body.localName) { + iban = account.iban + bankCode = account.bankCode + defaultBankConnection = conn + highestSeenBankMessageId = 0 + accountHolder = account.accountHolder } + account.imported = true } call.respond(object {}) } @@ -558,7 +542,6 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { paramsJson.toOrderParams() } val subscriberDetails = transaction { - val user = authenticateRequest(call.request) val conn = requireBankConnection(call, "connid") if (conn.type != "ebics") { throw NexusError(HttpStatusCode.BadRequest, "bank connection is not of type 'ebics'") diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt @@ -761,7 +761,7 @@ fun serverMain(dbName: String, host: String) { return@post } - route("/bank-connections/{connid}/ebics") { + route("/bank-connections/{connid}") { ebicsBankConnectionRoutes(client) }