libeufin

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

commit 09d802d6bcec50eebff668edb151ba39ad38785a
parent e9513cdd2c98b8329b31df4c50aa0efd84c0cf63
Author: MS <ms@taler.net>
Date:   Mon, 22 Aug 2022 16:36:22 +0200

Addressing #7295.

Diffstat:
A.idea/runConfigurations.xml | 11+++++++++++
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 77+++++++++++++++++++++++++++++++----------------------------------------------
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 27+++++++++++++--------------
Msandbox/src/main/resources/logback.xml | 6+++---
4 files changed, 58 insertions(+), 63 deletions(-)

diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RunConfigurationProducerService"> + <option name="ignoredProducers"> + <set> + <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> + </set> + </option> + </component> +</project> +\ No newline at end of file diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -698,9 +698,8 @@ private fun parsePain001(paymentRequest: String): PainParseResult { */ private fun handleCct(paymentRequest: String) { logger.debug("Handling CCT") - logger.debug("Pain.001: $paymentRequest") val parseResult = parsePain001(paymentRequest) - transaction { + transaction(Connection.TRANSACTION_SERIALIZABLE, repetitionAttempts = 10) { val maybeExist = BankAccountTransactionEntity.find { BankAccountTransactionsTable.pmtInfId eq parseResult.pmtInfId }.firstOrNull() @@ -711,15 +710,35 @@ private fun handleCct(paymentRequest: String) { ) return@transaction } - try { - val bankAccount = getBankAccountFromIban(parseResult.debtorIban) - if (parseResult.currency != bankAccount.demoBank.currency) throw EbicsRequestError( - "[EBICS_PROCESSING_ERROR] Currency (${parseResult.currency}) not supported.", - "091116" - ) + val bankAccount = getBankAccountFromIban(parseResult.debtorIban) + if (parseResult.currency != bankAccount.demoBank.currency) throw EbicsRequestError( + "[EBICS_PROCESSING_ERROR] Currency (${parseResult.currency}) not supported.", + "091116" + ) + BankAccountTransactionEntity.new { + account = bankAccount + demobank = bankAccount.demoBank + creditorIban = parseResult.creditorIban + creditorName = parseResult.creditorName + creditorBic = parseResult.creditorBic + debtorIban = parseResult.debtorIban + debtorName = parseResult.debtorName + debtorBic = parseResult.debtorBic + subject = parseResult.subject + amount = parseResult.amount + currency = parseResult.currency + date = getUTCnow().toInstant().toEpochMilli() + pmtInfId = parseResult.pmtInfId + accountServicerReference = "sandboxref-${getRandomString(16)}" + direction = "DBIT" + } + val maybeLocalCreditor = BankAccountEntity.find( + BankAccountsTable.iban eq parseResult.creditorIban + ).firstOrNull() + if (maybeLocalCreditor != null) { BankAccountTransactionEntity.new { - account = bankAccount - demobank = bankAccount.demoBank + account = maybeLocalCreditor + demobank = maybeLocalCreditor.demoBank creditorIban = parseResult.creditorIban creditorName = parseResult.creditorName creditorBic = parseResult.creditorBic @@ -732,36 +751,8 @@ private fun handleCct(paymentRequest: String) { date = getUTCnow().toInstant().toEpochMilli() pmtInfId = parseResult.pmtInfId accountServicerReference = "sandboxref-${getRandomString(16)}" - direction = "DBIT" + direction = "CRDT" } - val maybeLocalCreditor = BankAccountEntity.find( - BankAccountsTable.iban eq parseResult.creditorIban - ).firstOrNull() - if (maybeLocalCreditor != null) { - BankAccountTransactionEntity.new { - account = maybeLocalCreditor - demobank = maybeLocalCreditor.demoBank - creditorIban = parseResult.creditorIban - creditorName = parseResult.creditorName - creditorBic = parseResult.creditorBic - debtorIban = parseResult.debtorIban - debtorName = parseResult.debtorName - debtorBic = parseResult.debtorBic - subject = parseResult.subject - amount = parseResult.amount - currency = parseResult.currency - date = getUTCnow().toInstant().toEpochMilli() - pmtInfId = parseResult.pmtInfId - accountServicerReference = "sandboxref-${getRandomString(16)}" - direction = "CRDT" - } - } - } catch (e: ExposedSQLException) { - logger.warn("Could not insert new payment into the database: ${e}") - throw EbicsRequestError( - "[EBICS_PROCESSING_ERROR] ${e.sqlState}", - "091116" - ) } } } @@ -1219,9 +1210,7 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo this.numSegments = numSegments.toInt() this.transactionKeyEnc = ExposedBlob(transactionKeyEnc) } - logger.debug("after SQL flush") val sigObj = XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8)) - logger.debug("got UserSignatureData: ${plainSigData.toString(Charsets.UTF_8)}") for (sig in sigObj.value.orderSignatureList ?: listOf()) { logger.debug("inserting order signature for orderID $orderID and orderType $orderType") EbicsOrderSignatureEntity.new { @@ -1279,10 +1268,6 @@ private fun handleEbicsUploadTransactionTransmission(requestContext: RequestCont } } if (getOrderTypeFromTransactionId(requestTransactionID) == "CCT") { - logger.debug( - "Attempting a payment in thread (name/id): " + - "${Thread.currentThread().name}/${Thread.currentThread().id}" - ) handleCct(unzippedData.toString(Charsets.UTF_8)) } return EbicsResponse.createForUploadTransferPhase( @@ -1407,7 +1392,7 @@ suspend fun ApplicationCall.ebicsweb() { "ebicsRequest" -> { // logger.debug("ebicsRequest ${XMLUtil.convertDomToString(requestDocument)}") val requestObject = requestDocument.toObject<EbicsRequest>() - val responseXmlStr = transaction { + val responseXmlStr = transaction(Connection.TRANSACTION_SERIALIZABLE, repetitionAttempts = 10) { // Step 1 of 3: Get information about the host and subscriber val requestContext = makeRequestContext(requestObject) // Step 2 of 3: Validate the signature diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt @@ -76,10 +76,7 @@ import io.netty.util.concurrent.AbstractEventExecutor import io.netty.util.concurrent.DefaultEventExecutor import io.netty.util.concurrent.GlobalEventExecutor import io.netty.util.concurrent.SingleThreadEventExecutor -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.newSingleThreadContext -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.statements.api.ExposedBlob @@ -87,6 +84,7 @@ import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransacti import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.slf4j.event.Level import org.w3c.dom.Document import startServer import tech.libeufin.util.* @@ -95,6 +93,7 @@ import java.math.BigDecimal import java.net.BindException import java.net.URL import java.security.interfaces.RSAPublicKey +import java.util.concurrent.Executors import javax.xml.bind.JAXBContext import kotlin.system.exitProcess @@ -469,7 +468,6 @@ suspend inline fun <reified T : Any> ApplicationCall.receiveJson(): T { } } -val singleThreadContext = newSingleThreadContext("DB") fun setJsonHandler(ctx: ObjectMapper) { ctx.enable(SerializationFeature.INDENT_OUTPUT) ctx.setDefaultPrettyPrinter(DefaultPrettyPrinter().apply { @@ -481,7 +479,13 @@ fun setJsonHandler(ctx: ObjectMapper) { val sandboxApp: Application.() -> Unit = { install(CallLogging) { - this.level = org.slf4j.event.Level.DEBUG + this.level = Level.DEBUG + this.logger = tech.libeufin.sandbox.logger + this.format { call -> + val t = Thread.currentThread() + "${call.response.status()}, ${call.request.httpMethod.value} ${call.request.path()}" + + " - thread (id/name/group): ${t.id}/${t.name}/${t.threadGroup.name}" + } } install(CORS) { anyHost() @@ -1139,14 +1143,14 @@ val sandboxApp: Application.() -> Unit = { post("/withdrawal-operation/{wopid}") { val wopid: String = ensureNonNull(call.parameters["wopid"]) val body = call.receiveJson<TalerWithdrawalSelection>() - val transferDone = newSuspendedTransaction(context = singleThreadContext) { + val transferDone = transaction { val wo = TalerWithdrawalEntity.find { TalerWithdrawalsTable.wopid eq java.util.UUID.fromString(wopid) }.firstOrNull() ?: throw SandboxError( HttpStatusCode.NotFound, "Withdrawal operation $wopid not found." ) if (wo.confirmationDone) { - return@newSuspendedTransaction true + return@transaction true } if (wo.selectionDone) { if (body.reserve_pub != wo.reservePub) throw SandboxError( @@ -1157,7 +1161,7 @@ val sandboxApp: Application.() -> Unit = { HttpStatusCode.Conflict, "Selecting a different exchange from the one already selected" ) - return@newSuspendedTransaction false + return@transaction false } // Flow here means never selected, hence must as well never be paid. if (wo.confirmationDone) throw internalServerError( @@ -1613,11 +1617,6 @@ fun serverMain(port: Int) { this.host = "[::1]" } module(sandboxApp) - }, - configure = { - connectionGroupSize = 1 - workerGroupSize = 1 - callGroupSize = 1 } ) logger.info("LibEuFin Sandbox running on port $port") diff --git a/sandbox/src/main/resources/logback.xml b/sandbox/src/main/resources/logback.xml @@ -13,9 +13,9 @@ <appender-ref ref="STDERR" /> </logger> - <logger name="io.netty" level="WARN" /> - <logger name="ktor" level="WARN" /> - <logger name="Exposed" level="WARN" /> + <logger name="io.netty" level="INFO" /> + <logger name="ktor" level="TRACE" /> + <logger name="Exposed" level="INFO" /> <root level="WARN"> <appender-ref ref="STDERR" />