[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: Bank: error handling and amount parser
From: |
gnunet |
Subject: |
[libeufin] branch master updated: Bank: error handling and amount parser. |
Date: |
Wed, 13 Sep 2023 16:30:23 +0200 |
This is an automated email from the git hooks/post-receive script.
ms pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new 4f8b94fa Bank: error handling and amount parser.
4f8b94fa is described below
commit 4f8b94faf1822d24ae1a4e9ece93bda2f797ab59
Author: MS <ms@taler.net>
AuthorDate: Wed Sep 13 16:26:33 2023 +0200
Bank: error handling and amount parser.
Ktor's StatusPages catches any Exception thrown in the application
and try to build a Taler ErrorDetail to respond to the client.
---
.idea/gradle.xml | 1 +
.idea/workspace.xml | 162 ++++++++++-----------
.../src/main/kotlin/tech/libeufin/bank/Database.kt | 8 +-
bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt | 86 +++++++++++
bank/src/main/kotlin/tech/libeufin/bank/Main.kt | 128 +++++++++++++---
bank/src/test/kotlin/AmountTest.kt | 20 +++
bank/src/test/kotlin/DatabaseTest.kt | 4 +
bank/src/test/kotlin/LibeuFinApiTest.kt | 18 ++-
contrib/wallet-core | 2 +-
util/src/test/kotlin/AmountTest.kt | 45 ------
10 files changed, 315 insertions(+), 159 deletions(-)
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index e1d33bca..7b700961 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -10,6 +10,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/bank" />
+ <option value="$PROJECT_DIR$/nexus" />
<option value="$PROJECT_DIR$/util" />
</set>
</option>
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 12738e16..ccdc86c3 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,74 +5,16 @@
</component>
<component name="ChangeListManager">
<list default="true" id="9436eb1e-de48-4f11-8ff7-f359340cb458"
name="Changes" comment="">
- <change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
- <change
afterPath="$PROJECT_DIR$/bank/src/test/kotlin/LibeuFinApiTest.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/$PRODUCT_WORKSPACE_FILE$"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/codeStyles/Project.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/dictionaries/dold.xml"
beforeDir="false" />
+ <change
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt"
afterDir="false" />
+ <change afterPath="$PROJECT_DIR$/bank/src/test/kotlin/AmountTest.kt"
afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false"
afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/kotlinc.xml" beforeDir="false"
afterPath="$PROJECT_DIR$/.idea/kotlinc.xml" afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/libraries-with-intellij-classes.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false"
afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/runConfigurations/SchedulingTest.xml"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/runConfigurations/run_sandbox.xml"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/runConfigurations/test_nexus.xml"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/.idea/runConfigurations/test_sandbox.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/uiDesigner.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/README" beforeDir="false"
afterPath="$PROJECT_DIR$/bank/README" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/build.gradle" beforeDir="false"
afterPath="$PROJECT_DIR$/bank/build.gradle" afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/CircuitApi.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/CircuitApi.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/ConversionService.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/ConversionService.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/DB.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/DB.kt"
afterDir="false" />
+ <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/Database.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/Database.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/EbicsProtocolBackend.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/EbicsProtocolBackend.kt"
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/JSON.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/JSON.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/main/kotlin/tech/libeufin/bank/XMLEbicsConverter.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/XMLEbicsConverter.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/bankAccount.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/main/kotlin/tech/libeufin/bank/bankAccount.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/src/main/resources/logback.xml"
beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/main/resources/logback.xml"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/src/test/kotlin/BalanceTest.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/test/kotlin/BalanceTest.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/src/test/kotlin/DBTest.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/test/kotlin/DBTest.kt"
afterDir="false" />
<change beforePath="$PROJECT_DIR$/bank/src/test/kotlin/DatabaseTest.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/test/kotlin/DatabaseTest.kt" afterDir="false"
/>
- <change
beforePath="$PROJECT_DIR$/bank/src/test/kotlin/EbicsErrorTest.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/test/kotlin/EbicsErrorTest.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/bank/src/test/kotlin/StringsTest.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/bank/src/test/kotlin/StringsTest.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/database-versioning/new/libeufin-bank-0001.sql"
beforeDir="false"
afterPath="$PROJECT_DIR$/database-versioning/new/libeufin-bank-0001.sql"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/database-versioning/new/procedures.sql"
beforeDir="false"
afterPath="$PROJECT_DIR$/database-versioning/new/procedures.sql"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/build.gradle" beforeDir="false"
afterPath="$PROJECT_DIR$/nexus/build.gradle" afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/ConversionServiceTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/DbEventTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/EbicsTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/Iso20022Test.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/JsonTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/LetterFormatTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/MakeEnv.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/NexusApiTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/PainTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/PostFinance.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SandboxAccessApiTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SandboxBankAccountTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SandboxCircuitApiTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SandboxLegacyApiTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SchedulingTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SplitString.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/SubjectNormalization.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/TalerTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/XLibeufinBankTest.kt"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/nexus/src/test/kotlin/XPathTest.kt"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/resources/iso20022-samples/camt.053/de.camt.053.001.02.xml"
beforeDir="false" />
- <change
beforePath="$PROJECT_DIR$/nexus/src/test/resources/logback-test.xml"
beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/util/build.gradle" beforeDir="false"
afterPath="$PROJECT_DIR$/util/build.gradle" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/Config.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/Config.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/DB.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/DB.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/HTTP.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/HTTP.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/iban.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/iban.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/startServer.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/startServer.kt"
afterDir="false" />
- <change beforePath="$PROJECT_DIR$/util/src/main/kotlin/time.kt"
beforeDir="false" afterPath="$PROJECT_DIR$/util/src/main/kotlin/time.kt"
afterDir="false" />
- <change
beforePath="$PROJECT_DIR$/util/src/test/kotlin/StartServerTest.kt"
beforeDir="false" />
+ <change
beforePath="$PROJECT_DIR$/bank/src/test/kotlin/LibeuFinApiTest.kt"
beforeDir="false"
afterPath="$PROJECT_DIR$/bank/src/test/kotlin/LibeuFinApiTest.kt"
afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/contrib/wallet-core" beforeDir="false"
afterPath="$PROJECT_DIR$/contrib/wallet-core" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/util/src/test/kotlin/AmountTest.kt"
beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -90,10 +32,28 @@
<task path="$PROJECT_DIR$">
<activation />
</task>
- <projects_view />
+ <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>
@@ -108,11 +68,16 @@
<component name="PropertiesComponent">{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
- "RunOnceActivity.ShowReadmeOnStart": "true"
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "project.structure.last.edited": "Project",
+ "project.structure.proportion": "0.0",
+ "project.structure.side.proportion": "0.0",
+ "settings.editor.selected.configurable":
"project.kotlinCompiler",
+ "settings.editor.splitter.proportion": "0.31419808"
}
}</component>
- <component name="RunManager"
selected="Gradle.LibeuFinApiTest.createAccountTest">
- <configuration name="DatabaseTest" type="GradleRunConfiguration"
factoryName="Gradle" temporary="true">
+ <component name="RunManager" selected="Gradle.DatabaseTest.bankAccountTest">
+ <configuration name="AmountTest.parseAmountTest"
type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
@@ -125,7 +90,7 @@
<list>
<option value=":bank:test" />
<option value="--tests" />
- <option value=""DatabaseTest"" />
+ <option value=""AmountTest.parseAmountTest"" />
</list>
</option>
<option name="vmOptions" />
@@ -135,7 +100,7 @@
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
- <configuration name="DatabaseTest.bearerTokenTest"
type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
+ <configuration name="AmountTest.parseTalerAmountTest"
type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
@@ -148,7 +113,7 @@
<list>
<option value=":bank:test" />
<option value="--tests" />
- <option value=""DatabaseTest.bearerTokenTest"" />
+ <option value=""AmountTest.parseTalerAmountTest"" />
</list>
</option>
<option name="vmOptions" />
@@ -158,7 +123,30 @@
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
- <configuration name="LibeuFinApiTest.createAccountTest"
type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
+ <configuration name="DatabaseTest.bankAccountTest"
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=":bank:test" />
+ <option value="--tests" />
+ <option value=""DatabaseTest.bankAccountTest"" />
+ </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$" />
@@ -171,7 +159,7 @@
<list>
<option value=":bank:test" />
<option value="--tests" />
- <option value=""LibeuFinApiTest.createAccountTest"" />
+ <option value=""JsonTest.deserializationTest"" />
</list>
</option>
<option name="vmOptions" />
@@ -181,39 +169,43 @@
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
- <configuration name="libeufin [dependencies]"
type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
+ <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" />
+ <option name="scriptParameters" value="--quiet" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
- <option value="dependencies" />
+ <option value=":bank:test" />
+ <option value="--tests" />
+ <option value=""LibeuFinApiTest.createAccountTest"" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
- <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
<list>
- <item itemvalue="Gradle.libeufin [dependencies]" />
- <item itemvalue="Gradle.DatabaseTest" />
- <item itemvalue="Gradle.DatabaseTest.bearerTokenTest" />
+ <item itemvalue="Gradle.DatabaseTest.bankAccountTest" />
+ <item itemvalue="Gradle.AmountTest.parseTalerAmountTest" />
+ <item itemvalue="Gradle.AmountTest.parseAmountTest" />
+ <item itemvalue="Gradle.JsonTest.deserializationTest" />
<item itemvalue="Gradle.LibeuFinApiTest.createAccountTest" />
</list>
<recent_temporary>
<list>
+ <item itemvalue="Gradle.DatabaseTest.bankAccountTest" />
<item itemvalue="Gradle.LibeuFinApiTest.createAccountTest" />
- <item itemvalue="Gradle.libeufin [dependencies]" />
- <item itemvalue="Gradle.DatabaseTest" />
- <item itemvalue="Gradle.DatabaseTest.bearerTokenTest" />
+ <item itemvalue="Gradle.AmountTest.parseTalerAmountTest" />
+ <item itemvalue="Gradle.AmountTest.parseAmountTest" />
+ <item itemvalue="Gradle.JsonTest.deserializationTest" />
</list>
</recent_temporary>
</component>
@@ -228,4 +220,8 @@
</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
index 799788c0..2239a50c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -15,10 +15,10 @@ data class Customer(
val passwordHash: String,
val name: String,
val dbRowId: Long? = null, // mostly used when retrieving records.
- val email: String?,
- val phone: String?,
- val cashoutPayto: String?,
- val cashoutCurrency: String?
+ val email: String? = null,
+ val phone: String? = null,
+ val cashoutPayto: String? = null,
+ val cashoutCurrency: String? = null
)
fun Customer.expectRowId(): Long = this.dbRowId ?: throw
internalServerError("Cutsomer '${this.login}' had no DB row ID")
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt
b/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt
new file mode 100644
index 00000000..19b1150c
--- /dev/null
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Helpers.kt
@@ -0,0 +1,86 @@
+/*
+ * 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 io.ktor.http.*
+import tech.libeufin.util.getIban
+import java.lang.NumberFormatException
+
+// HELPERS. FIXME: make unit tests for them.
+
+fun internalServerError(hint: String): LibeufinBankException =
+ LibeufinBankException(
+ httpStatus = HttpStatusCode.InternalServerError,
+ talerError = TalerError(
+ code = GENERIC_INTERNAL_INVARIANT_FAILURE,
+ hint = hint
+ )
+ )
+// Generates a new Payto-URI with IBAN scheme.
+fun genIbanPaytoUri(): String = "payto://iban/SANDBOXX/${getIban()}"
+
+fun parseTalerAmount(
+ amount: String,
+ fracDigits: FracDigits = FracDigits.EIGHT
+): TalerAmount {
+ val format = when (fracDigits) {
+ FracDigits.TWO ->
+ Pair("^([A-Z]+):([0-9])(\\.[0-9][0-9]?)?$", 100)
+ FracDigits.EIGHT ->
+ Pair(
+
"^([A-Z]+):([0-9])(\\.[0-9][0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?)?\$",
+ 100000000
+ )
+ }
+ val match = Regex(format.first).find(amount) ?: throw
LibeufinBankException(
+ httpStatus = HttpStatusCode.BadRequest,
+ talerError = TalerError(
+ code = BANK_BAD_FORMAT_AMOUNT,
+ hint = "Invalid amount: $amount"
+ ))
+ val _value = match.destructured.component2()
+ // Fraction is at most 8 digits, so it's always < than MAX_INT.
+ val fraction: Int = match.destructured.component3().run {
+ var frac = 0
+ var power = format.second
+ if (this.isNotEmpty())
+ // Skips the dot and processes the fractional chars.
+ this.substring(1).forEach { chr ->
+ power /= 10
+ frac += power * chr.digitToInt()
+ }
+ return@run frac
+ }
+ val value: Long = try {
+ _value.toLong()
+ } catch (e: NumberFormatException) {
+ throw LibeufinBankException(
+ httpStatus = HttpStatusCode.BadRequest,
+ talerError = TalerError(
+ code = BANK_BAD_FORMAT_AMOUNT,
+ hint = "Invalid amount: ${amount}, could not extract the value
part."
+ )
+ )
+ }
+ return TalerAmount(
+ value = value,
+ frac = fraction
+ )
+}
\ 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
index 2662a051..2967732c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -1,3 +1,22 @@
+/*
+ * 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 io.ktor.http.*
@@ -22,11 +41,25 @@ import tech.libeufin.util.*
// GLOBALS
val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank")
val db = Database(System.getProperty("BANK_DB_CONNECTION_STRING"))
+// fixme: make enum out of error codes.
const val GENERIC_JSON_INVALID = 22
const val GENERIC_PARAMETER_MALFORMED = 26
const val GENERIC_PARAMETER_MISSING = 25
+const val BANK_UNMANAGED_EXCEPTION = 5110
+const val BANK_BAD_FORMAT_AMOUNT = 5108
+const val GENERIC_HTTP_HEADERS_MALFORMED = 23
+const val GENERIC_INTERNAL_INVARIANT_FAILURE = 60
+const val BANK_LOGIN_FAILED = 5105
+const val GENERIC_UNAUTHORIZED = 40
+const val GENERIC_UNDEFINED = -1 // Filler for ECs that don't exist yet.
// TYPES
+
+enum class FracDigits(howMany: Int) {
+ TWO(2),
+ EIGHT(8)
+}
+
@Serializable
data class TalerError(
val code: Int,
@@ -50,19 +83,11 @@ data class RegisterAccountRequest(
val internal_payto_uri: String? = null
)
-// Generates a new Payto-URI with IBAN scheme.
-fun genIbanPaytoUri(): String = "payto://iban/SANDBOXX/${getIban()}"
-fun parseTalerAmount(amount: String): TalerAmount {
- val amountWithCurrencyRe = "^([A-Z]+):([0-9]+(\\.[0-9][0-9]?)?)$"
- val match = Regex(amountWithCurrencyRe).find(amount) ?:
- throw badRequest("Invalid amount")
- val value = match.destructured.component2()
- val fraction: Int = match.destructured.component3().run {
- if (this.isEmpty()) return@run 0
- return@run this.substring(1).toInt()
- }
- return TalerAmount(value.toLong(), fraction)
-}
+class LibeufinBankException(
+ val httpStatus: HttpStatusCode,
+ val talerError: TalerError
+) : Exception(talerError.hint)
+
/**
* Performs the HTTP basic authentication. Returns the
@@ -79,7 +104,14 @@ fun doBasicAuth(encodedCredentials: String): Customer? {
*/
limit = 2
)
- if (userAndPassSplit.size != 2) throw badRequest("Malformed Basic auth
credentials found in the Authorization header.")
+ if (userAndPassSplit.size != 2)
+ throw LibeufinBankException(
+ httpStatus = HttpStatusCode.BadRequest,
+ talerError = TalerError(
+ code = GENERIC_HTTP_HEADERS_MALFORMED, // 23
+ "Malformed Basic auth credentials found in the Authorization
header."
+ )
+ )
val login = userAndPassSplit[0]
val plainPassword = userAndPassSplit[1]
return db.customerPwAuth(login, CryptoUtil.hashpw(plainPassword))
@@ -96,7 +128,12 @@ fun doTokenAuth(
if (isExpired || maybeToken.scope != requiredScope) return null // FIXME:
mention the reason?
// Getting the related username.
return db.customerGetFromRowId(maybeToken.bankCustomer)
- ?: throw internalServerError("Customer not found, despite token
mentions it.")
+ ?: throw LibeufinBankException(
+ httpStatus = HttpStatusCode.InternalServerError,
+ talerError = TalerError(
+ code = GENERIC_INTERNAL_INVARIANT_FAILURE,
+ hint = "Customer not found, despite token mentions it.",
+ ))
}
/**
@@ -115,7 +152,13 @@ fun ApplicationCall.myAuth(requiredScope: TokenScope):
Customer? {
return when (authDetails.scheme) {
"Basic" -> doBasicAuth(authDetails.content)
"Bearer" -> doTokenAuth(authDetails.content, requiredScope)
- else -> throw badRequest("Authorization scheme '${authDetails.scheme}'
is not supported.")
+ else -> throw LibeufinBankException(
+ httpStatus = HttpStatusCode.Unauthorized,
+ talerError = TalerError(
+ code = GENERIC_UNAUTHORIZED,
+ hint = "Authorization method wrong or not supported."
+ )
+ )
}
}
@@ -145,8 +188,15 @@ val webApp: Application.() -> Unit = {
}
install(RequestValidation)
install(StatusPages) {
+ /**
+ * This branch triggers when the Ktor layers detect one
+ * invalid request. It _might_ be thrown by the bank's
+ * actual logic, but that should be avoided because this
+ * (Ktor native) type doesn't easily map to the Taler error
+ * format.
+ */
exception<BadRequestException> {call, cause ->
- // Discouraged use, but the only helpful message.
+ // Discouraged use, but the only helpful message:
var rootCause: Throwable? = cause.cause
while (rootCause?.cause != null)
rootCause = rootCause.cause
@@ -158,12 +208,36 @@ val webApp: Application.() -> Unit = {
else -> GENERIC_JSON_INVALID // 22
}
call.respond(
- HttpStatusCode.BadRequest,
- TalerError(
+ status = HttpStatusCode.BadRequest,
+ message = TalerError(
code = talerErrorCode,
hint = rootCause?.message
))
}
+ /**
+ * This branch triggers when a bank handler throws it, and namely
+ * after one logical failure of the request(-handling). This branch
+ * should be preferred to catch errors, as it allows to include the
+ * Taler specific error detail.
+ */
+ exception<LibeufinBankException> {call, cause ->
+ logger.error(cause.talerError.hint)
+ call.respond(
+ status = cause.httpStatus,
+ message = cause.talerError
+ )
+ }
+ // Catch-all branch to mean that the bank wasn't able to manage one
error.
+ exception<Exception> {call, cause ->
+ logger.error(cause.message)
+ call.respond(
+ status = HttpStatusCode.InternalServerError,
+ message = TalerError(
+ code = BANK_UNMANAGED_EXCEPTION,// 5110, bank's fault
+ hint = cause.message
+ )
+ )
+ }
}
routing {
post("/accounts") {
@@ -173,13 +247,25 @@ val webApp: Application.() -> Unit = {
val customer: Customer? = call.myAuth(TokenScope.readwrite)
if (customer == null || customer.login != "admin")
// OK to leak the only-admin policy here?
- throw unauthorized("Only admin allowed, and it failed to
authenticate.")
+ throw LibeufinBankException(
+ httpStatus = HttpStatusCode.Unauthorized,
+ talerError = TalerError(
+ code = BANK_LOGIN_FAILED,
+ hint = "Only admin allowed."
+ )
+ )
}
// auth passed, proceed with activity.
val req = call.receive<RegisterAccountRequest>()
// Prohibit reserved usernames:
if (req.username == "admin" || req.username == "bank")
- throw conflict("Username '${req.username}' is reserved.")
+ throw LibeufinBankException(
+ httpStatus = HttpStatusCode.Conflict,
+ talerError = TalerError(
+ code = GENERIC_UNDEFINED, // FIXME: this waits GANA.
+ hint = "Username '${req.username}' is reserved."
+ )
+ )
// Checking imdepotency.
val maybeCustomerExists = db.customerGetFromLogin(req.username)
// Can be null if previous call crashed before completion.
diff --git a/bank/src/test/kotlin/AmountTest.kt
b/bank/src/test/kotlin/AmountTest.kt
new file mode 100644
index 00000000..2d8ffab0
--- /dev/null
+++ b/bank/src/test/kotlin/AmountTest.kt
@@ -0,0 +1,20 @@
+import org.junit.Test
+import tech.libeufin.bank.parseTalerAmount
+
+class AmountTest {
+ @Test
+ fun parseTalerAmountTest() {
+ val one = "EUR:1"
+ var obj = parseTalerAmount(one)
+ assert(obj.value == 1L && obj.frac == 0)
+ val onePointZero = "EUR:1.00"
+ obj = parseTalerAmount(onePointZero)
+ assert(obj.value == 1L && obj.frac == 0)
+ val onePointZeroOne = "EUR:1.01"
+ obj = parseTalerAmount(onePointZeroOne)
+ assert(obj.value == 1L && obj.frac == 1000000)
+ // Invalid tries
+ obj = parseTalerAmount("EUR:0.00000001")
+ assert(obj.value == 0L && obj.frac == 1)
+ }
+}
\ No newline at end of file
diff --git a/bank/src/test/kotlin/DatabaseTest.kt
b/bank/src/test/kotlin/DatabaseTest.kt
index cb4755db..94f7aa67 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -41,6 +41,10 @@ class DatabaseTest {
)
fun initDb(): Database {
+ System.setProperty(
+ "BANK_DB_CONNECTION_STRING",
+ "jdbc:postgresql:///libeufincheck"
+ )
execCommand(
listOf(
"libeufin-bank-dbinit",
diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt
b/bank/src/test/kotlin/LibeuFinApiTest.kt
index 501a447b..227af75f 100644
--- a/bank/src/test/kotlin/LibeuFinApiTest.kt
+++ b/bank/src/test/kotlin/LibeuFinApiTest.kt
@@ -4,6 +4,7 @@ import io.ktor.http.*
import io.ktor.server.testing.*
import kotlinx.serialization.json.Json
import org.junit.Test
+import tech.libeufin.bank.Customer
import tech.libeufin.bank.Database
import tech.libeufin.bank.RegisterAccountRequest
import tech.libeufin.bank.webApp
@@ -11,6 +12,10 @@ import tech.libeufin.util.execCommand
class LibeuFinApiTest {
fun initDb(): Database {
+ System.setProperty(
+ "BANK_DB_CONNECTION_STRING",
+ "jdbc:postgresql:///libeufincheck"
+ )
execCommand(
listOf(
"libeufin-bank-dbinit",
@@ -26,15 +31,17 @@ class LibeuFinApiTest {
@Test
fun createAccountTest() {
testApplication {
- System.setProperty(
- "BANK_DB_CONNECTION_STRING",
- "jdbc:postgresql:///libeufincheck"
- )
val db = initDb()
db.configSet("max_debt_ordinary_customers", "KUDOS:11")
db.configSet("only_admin_registrations", "yes")
+ db.customerCreate(Customer(
+ "admin",
+ "pass",
+ "CFO"
+ ))
application(webApp)
- client.post("/accounts") {
+ val resp = client.post("/accounts") {
+ expectSuccess = false
contentType(ContentType.Application.Json)
basicAuth("admin", "bar")
setBody("""{
@@ -43,6 +50,7 @@ class LibeuFinApiTest {
"name": "Jane"
}""".trimIndent())
}
+ println("Resp status code: ${resp.status}")
}
}
}
\ No newline at end of file
diff --git a/contrib/wallet-core b/contrib/wallet-core
index fa191419..c5a3cd4c 160000
--- a/contrib/wallet-core
+++ b/contrib/wallet-core
@@ -1 +1 @@
-Subproject commit fa191419fcf8cc4e2b17400b791dbdf4e673f5aa
+Subproject commit c5a3cd4c50676c49fa6c67cbdeb609101c38e764
diff --git a/util/src/test/kotlin/AmountTest.kt
b/util/src/test/kotlin/AmountTest.kt
deleted file mode 100644
index 636c9060..00000000
--- a/util/src/test/kotlin/AmountTest.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-import io.ktor.util.reflect.*
-import org.junit.Test
-import tech.libeufin.util.isAmountZero
-import tech.libeufin.util.parseAmount
-import tech.libeufin.util.validatePlainAmount
-import java.math.BigDecimal
-import kotlin.reflect.typeOf
-
-inline fun <reified ExceptionType> assertException(block: () -> Unit) {
- try {
- block()
- } catch (e: Throwable) {
- assert(e.javaClass == ExceptionType::class.java)
- return
- }
- return assert(false)
-}
-class AmountTest {
- @Test
- fun equalTest() {
- assert(isAmountZero(BigDecimal("-0000000000.0000000000")))
- assert(!isAmountZero(BigDecimal("1")))
- assert(isAmountZero(BigDecimal("0.00")))
- assert(isAmountZero(BigDecimal("0")))
- assert(!isAmountZero(BigDecimal("00000000000001")))
- assert(!isAmountZero(BigDecimal("-1.00000000")))
- }
-
- @Test
- fun parse() {
- var res = parseAmount("KUDOS:5.5")
- assert(res.amount == "5.5")
- assert(res.currency == "KUDOS")
- assert(validatePlainAmount("1.0"))
- assert(validatePlainAmount("1.00"))
- assert(!validatePlainAmount("1.000"))
- res = parseAmount("TESTKUDOS:1.11")
- assert(res.amount == "1.11")
- assert(res.currency == "TESTKUDOS")
- assertException<UtilError> { parseAmount("TESTKUDOS:1.") }
- assertException<UtilError> { parseAmount("TESTKUDOS:.1") }
- assertException<UtilError> { parseAmount("TESTKUDOS:1.000") }
- assertException<UtilError> { parseAmount("TESTKUDOS:1..") }
- }
-}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: Bank: error handling and amount parser.,
gnunet <=