libeufin

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

commit 8a6e63869af864673aeaa7dd2409695b10db23af
parent 77918308e35c3cc9ed2f03332ac95b5ec52023eb
Author: MS <ms@taler.net>
Date:   Fri, 15 Sep 2023 11:08:01 +0200

TalerAmount defaults to the internal currency.

Diffstat:
M.gitignore | 1+
D.idea/workspace.xml | 222-------------------------------------------------------------------------------
Mbank/src/main/kotlin/tech/libeufin/bank/Database.kt | 49++++++++++++++++++++++++++++++++++++++-----------
Mbank/src/main/kotlin/tech/libeufin/bank/Helpers.kt | 4+---
Mbank/src/main/kotlin/tech/libeufin/bank/Main.kt | 2+-
Mbank/src/test/kotlin/AmountTest.kt | 31+++++++++++++++++++++++++++++++
Abank/src/test/kotlin/Common.kt | 40++++++++++++++++++++++++++++++++++++++++
Mbank/src/test/kotlin/DatabaseTest.kt | 38++++++++++++++++++++------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Anastasis.kt | 1-
Mnexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt | 3---
Mnexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt | 1-
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt | 1-
Mutil/src/main/kotlin/CamtJsonMapping.kt | 1-
Mutil/src/main/kotlin/DB.kt | 1-
Mutil/src/main/kotlin/HTTP.kt | 1+
15 files changed, 133 insertions(+), 263 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ +.idea /nexus/bin/ /sandbox/bin/ /util/bin/ diff --git a/.idea/workspace.xml b/.idea/workspace.xml @@ -1,221 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="AutoImportSettings"> - <option name="autoReloadType" value="SELECTIVE" /> - </component> - <component name="ChangeListManager"> - <list default="true" id="9436eb1e-de48-4f11-8ff7-f359340cb458" name="Changes" comment=""> - <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt" beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Main.kt" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/bank/src/test/kotlin/JsonTest.kt" beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/test/kotlin/JsonTest.kt" afterDir="false" /> - </list> - <option name="SHOW_DIALOG" value="false" /> - <option name="HIGHLIGHT_CONFLICTS" value="true" /> - <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> - <option name="LAST_RESOLUTION" value="IGNORE" /> - </component> - <component name="ExternalProjectsData"> - <projectState path="$PROJECT_DIR$"> - <ProjectState /> - </projectState> - </component> - <component name="ExternalProjectsManager"> - <system id="GRADLE"> - <state> - <task path="$PROJECT_DIR$"> - <activation /> - </task> - <projects_view> - <tree_state> - <expand> - <path> - <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" /> - <item name="libeufin" type="f1a62948:ProjectNode" /> - </path> - </expand> - <select /> - </tree_state> - </projects_view> - </state> - </system> - </component> - <component name="FileTemplateManagerImpl"> - <option name="RECENT_TEMPLATES"> - <list> - <option value="Kotlin Class" /> - <option value="Kotlin File" /> - </list> - </option> - </component> - <component name="Git.Settings"> - <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> - </component> - <component name="MarkdownSettingsMigration"> - <option name="stateVersion" value="1" /> - </component> - <component name="ProjectId" id="2V4jS1FHAIvLu5eYODKLzGxNSP4" /> - <component name="ProjectViewState"> - <option name="hideEmptyMiddlePackages" value="true" /> - <option name="showLibraryContents" value="true" /> - </component> - <component name="PropertiesComponent">{ - &quot;keyToString&quot;: { - &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;, - &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, - &quot;project.structure.last.edited&quot;: &quot;Project&quot;, - &quot;project.structure.proportion&quot;: &quot;0.0&quot;, - &quot;project.structure.side.proportion&quot;: &quot;0.0&quot;, - &quot;settings.editor.selected.configurable&quot;: &quot;project.kotlinCompiler&quot;, - &quot;settings.editor.splitter.proportion&quot;: &quot;0.31419808&quot; - } -}</component> - <component name="RunManager" selected="Gradle.JsonTest.unionTypeTest"> - <configuration name="AmountTest.parseTalerAmountTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" value="--quiet" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value=":bank:test" /> - <option value="--tests" /> - <option value="&quot;AmountTest.parseTalerAmountTest&quot;" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> - <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> - <DebugAllEnabled>false</DebugAllEnabled> - <method v="2" /> - </configuration> - <configuration name="CryptoUtilTest.passwordHashing" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" value="" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value=":util:test" /> - <option value="--tests" /> - <option value="&quot;CryptoUtilTest.passwordHashing&quot;" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> - <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> - <DebugAllEnabled>false</DebugAllEnabled> - <method v="2" /> - </configuration> - <configuration name="JsonTest.deserializationTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" value="--quiet" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value=":bank:test" /> - <option value="--tests" /> - <option value="&quot;JsonTest.deserializationTest&quot;" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> - <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> - <DebugAllEnabled>false</DebugAllEnabled> - <method v="2" /> - </configuration> - <configuration name="JsonTest.unionTypeTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" value="--quiet" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value=":bank:test" /> - <option value="--tests" /> - <option value="&quot;JsonTest.unionTypeTest&quot;" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> - <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> - <DebugAllEnabled>false</DebugAllEnabled> - <method v="2" /> - </configuration> - <configuration name="LibeuFinApiTest.createAccountTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> - <ExternalSystemSettings> - <option name="executionName" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="externalSystemIdString" value="GRADLE" /> - <option name="scriptParameters" value="--quiet" /> - <option name="taskDescriptions"> - <list /> - </option> - <option name="taskNames"> - <list> - <option value=":bank:test" /> - <option value="--tests" /> - <option value="&quot;LibeuFinApiTest.createAccountTest&quot;" /> - </list> - </option> - <option name="vmOptions" /> - </ExternalSystemSettings> - <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> - <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> - <DebugAllEnabled>false</DebugAllEnabled> - <method v="2" /> - </configuration> - <list> - <item itemvalue="Gradle.JsonTest.unionTypeTest" /> - <item itemvalue="Gradle.CryptoUtilTest.passwordHashing" /> - <item itemvalue="Gradle.AmountTest.parseTalerAmountTest" /> - <item itemvalue="Gradle.JsonTest.deserializationTest" /> - <item itemvalue="Gradle.LibeuFinApiTest.createAccountTest" /> - </list> - <recent_temporary> - <list> - <item itemvalue="Gradle.JsonTest.unionTypeTest" /> - <item itemvalue="Gradle.JsonTest.deserializationTest" /> - <item itemvalue="Gradle.LibeuFinApiTest.createAccountTest" /> - <item itemvalue="Gradle.CryptoUtilTest.passwordHashing" /> - <item itemvalue="Gradle.AmountTest.parseTalerAmountTest" /> - </list> - </recent_temporary> - </component> - <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> - <component name="TaskManager"> - <task active="true" id="Default" summary="Default task"> - <changelist id="9436eb1e-de48-4f11-8ff7-f359340cb458" name="Changes" comment="" /> - <created>1694102242996</created> - <option name="number" value="Default" /> - <option name="presentableId" value="Default" /> - <updated>1694102242996</updated> - </task> - <servers /> - </component> - <component name="XSLT-Support.FileAssociations.UIState"> - <expand /> - <select /> - </component> -</project> -\ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt @@ -1,8 +1,26 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + + package tech.libeufin.bank import org.postgresql.jdbc.PgConnection -import tech.libeufin.util.internalServerError - import java.sql.DriverManager import java.sql.PreparedStatement import java.sql.SQLException @@ -20,22 +38,31 @@ data class Customer( val cashoutPayto: String? = null, val cashoutCurrency: String? = null ) -fun Customer.expectRowId(): Long = this.dbRowId ?: throw internalServerError("Cutsomer '${this.login}' had no DB row ID") +fun Customer.expectRowId(): Long = this.dbRowId ?: throw internalServerError("Cutsomer '$login' had no DB row ID") /** * Represents a Taler amount. This type can be used both * to hold database records and amounts coming from the parser. + * If maybeCurrency is null, then the constructor defaults it + * to be the "internal currency". Internal currency is the one + * with which Libeufin-Bank moves funds within itself, therefore + * not to be mistaken with the cashout currency, which is the one + * that gets credited to Libeufin-Bank users to their cashout_payto_uri. + * + * maybeCurrency is typically null when the TalerAmount object gets + * defined by the Database class. */ -data class TalerAmount( +class TalerAmount( val value: Long, val frac: Int, - /** - * The currency is likely null when the object is defined - * from database records. It is instead not null when the - * object comes from the parsing of serialized amounts. - */ - val currency: String? = null -) + maybeCurrency: String? = null +) { + val currency: String = if (maybeCurrency == null) { + val internalCurrency = db.configGet("internal_currency") + ?: throw internalServerError("internal_currency not found in the config") + internalCurrency + } else maybeCurrency +} // BIC got removed, because it'll be expressed in the internal_payto_uri. data class BankAccount( diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt @@ -25,8 +25,6 @@ import java.lang.NumberFormatException // HELPERS. - - /** * Performs the HTTP basic authentication. Returns the * authenticated customer on success, or null otherwise. @@ -155,6 +153,6 @@ fun parseTalerAmount( return TalerAmount( value = value, frac = fraction, - currency = match.destructured.component1() + maybeCurrency = match.destructured.component1() ) } \ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -111,7 +111,7 @@ object RelativeTimeSerializer : KSerializer<RelativeTime> { val json = try { jsonInput.decodeJsonElement().jsonObject } catch (e: Exception) { - throw badRequest(e.message) // JSON was malformed. + throw badRequest("Did not find a RelativeTime JSON object: ${e.message}") } val maybeDUs = json["d_us"]?.jsonPrimitive ?: throw badRequest("Relative time invalid: d_us field not found") if (maybeDUs.isString) { diff --git a/bank/src/test/kotlin/AmountTest.kt b/bank/src/test/kotlin/AmountTest.kt @@ -1,8 +1,39 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + + import org.junit.Test import tech.libeufin.bank.FracDigits +import tech.libeufin.bank.TalerAmount import tech.libeufin.bank.parseTalerAmount class AmountTest { + /* Testing that currency is fetched from the config + and set in the TalerAmount dedicated field. */ + @Test + fun testAutoCurrency() { + val db = initDb() + db.configSet("internal_currency", "KUDOS") + val a = TalerAmount(1L, 0) + assert(a.currency == "KUDOS") + } + @Test fun parseTalerAmountTest() { val one = "EUR:1" diff --git a/bank/src/test/kotlin/Common.kt b/bank/src/test/kotlin/Common.kt @@ -0,0 +1,39 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +import tech.libeufin.bank.Database +import tech.libeufin.util.execCommand + +fun initDb(): Database { + System.setProperty( + "BANK_DB_CONNECTION_STRING", + "jdbc:postgresql:///libeufincheck" + ) + execCommand( + listOf( + "libeufin-bank-dbinit", + "-d", + "libeufincheck", + "-r" + ), + throwIfFails = true + ) + val db = Database("jdbc:postgresql:///libeufincheck") + return db +} +\ No newline at end of file diff --git a/bank/src/test/kotlin/DatabaseTest.kt b/bank/src/test/kotlin/DatabaseTest.kt @@ -1,3 +1,23 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + + import org.junit.Test import tech.libeufin.bank.* import tech.libeufin.util.execCommand @@ -40,24 +60,6 @@ class DatabaseTest { maxDebt = TalerAmount(10, 1) ) - fun initDb(): Database { - System.setProperty( - "BANK_DB_CONNECTION_STRING", - "jdbc:postgresql:///libeufincheck" - ) - execCommand( - listOf( - "libeufin-bank-dbinit", - "-d", - "libeufincheck", - "-r" - ), - throwIfFails = true - ) - val db = Database("jdbc:postgresql:///libeufincheck") - return db - } - @Test fun bearerTokenTest() { val db = initDb() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Anastasis.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Anastasis.kt @@ -1,7 +1,6 @@ package tech.libeufin.nexus import TransactionDetails -import io.ktor.client.* import io.ktor.http.* import org.jetbrains.exposed.sql.transactions.transaction import tech.libeufin.nexus.server.PermissionQuery diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt @@ -44,9 +44,6 @@ import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.transaction -import tech.libeufin.nexus.bankaccount.addPaymentInitiation -import tech.libeufin.nexus.bankaccount.fetchBankAccountTransactions -import tech.libeufin.nexus.iso20022.* import tech.libeufin.nexus.server.* import tech.libeufin.nexus.xlibeufinbank.ingestXLibeufinBankMessage import tech.libeufin.util.* diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt @@ -24,7 +24,6 @@ import io.ktor.server.application.ApplicationCall import io.ktor.client.HttpClient import io.ktor.http.HttpStatusCode import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction import tech.libeufin.nexus.* import tech.libeufin.nexus.iso20022.* diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt @@ -31,7 +31,6 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import tech.libeufin.nexus.NexusError import tech.libeufin.util.* -import tech.libeufin.util.ebics_h005.Ebics3Request import java.util.* private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util") diff --git a/util/src/main/kotlin/CamtJsonMapping.kt b/util/src/main/kotlin/CamtJsonMapping.kt @@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.ser.std.StdSerializer -import org.jetbrains.exposed.sql.Transaction import tech.libeufin.util.internalServerError enum class CreditDebitIndicator { diff --git a/util/src/main/kotlin/DB.kt b/util/src/main/kotlin/DB.kt @@ -30,7 +30,6 @@ import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.transaction import org.postgresql.jdbc.PgConnection import java.net.URI -import kotlin.system.exitProcess fun Transaction.isPostgres(): Boolean { return this.db.vendor == "postgresql" diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt @@ -67,6 +67,7 @@ fun nullConfigValueError( ): Throwable { return internalServerError("Configuration value for '$configKey' at demobank '$demobankName' is null.") } + fun internalServerError( reason: String, libeufinErrorCode: LibeufinErrorCode? = LibeufinErrorCode.LIBEUFIN_EC_NONE