gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: database primitives to check fractiona


From: gnunet
Subject: [libeufin] branch master updated: database primitives to check fractional values
Date: Fri, 06 Dec 2019 22:26:33 +0100

This is an automated email from the git hooks/post-receive script.

marcello pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new b92daf7  database primitives to check fractional values
b92daf7 is described below

commit b92daf77201c59b31820db077493ec81d94829aa
Author: Marcello Stanisci <address@hidden>
AuthorDate: Fri Dec 6 22:25:32 2019 +0100

    database primitives to check fractional values
---
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    | 77 ++++++++++++++++++---
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 10 +++
 sandbox/src/test/kotlin/DbTest.kt                  | 79 +++++++++++++++++++---
 3 files changed, 145 insertions(+), 21 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 4b7002d..5c2b33d 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -19,10 +19,15 @@
 
 package tech.libeufin.sandbox
 
+import io.ktor.http.HttpStatusCode
 import org.jetbrains.exposed.dao.*
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.TransactionManager
 import org.jetbrains.exposed.sql.transactions.transaction
+import java.lang.ArithmeticException
+import java.math.BigDecimal
+import java.math.MathContext
+import java.math.RoundingMode
 import java.sql.Blob
 import java.sql.Connection
 
@@ -92,14 +97,67 @@ fun Blob.toByteArray(): ByteArray {
     return this.binaryStream.readAllBytes()
 }
 
-object BankTransactionsTable : IntIdTable() {
+/**
+ * Any number can become a Amount IF it does NOT need to be rounded to comply 
to the scale == 2.
+ */
+typealias Amount = BigDecimal
+
+open class IntIdTableWithAmount : IntIdTable() {
+
+    class AmountColumnType : ColumnType() {
+        override fun sqlType(): String  = "DECIMAL(${NUMBER_MAX_DIGITS}, 
${SCALE_TWO})"
+
+        override fun valueFromDB(value: Any): Any {
+
+            val valueFromDB = super.valueFromDB(value)
+            try {
+                return when (valueFromDB) {
+                    is BigDecimal -> valueFromDB.setScale(SCALE_TWO, 
RoundingMode.UNNECESSARY)
+                    is Double -> 
BigDecimal.valueOf(valueFromDB).setScale(SCALE_TWO, RoundingMode.UNNECESSARY)
+                    is Float -> 
BigDecimal(java.lang.Float.toString(valueFromDB)).setScale(
+                        SCALE_TWO,
+                        RoundingMode.UNNECESSARY
+                    )
+                    is Int -> BigDecimal(valueFromDB)
+                    is Long -> BigDecimal.valueOf(valueFromDB)
+                    else -> valueFromDB
+                }
+            } catch (e: Exception) {
+                e.printStackTrace()
+                throw BadAmount(value)
+            }
+
+        }
+
+        override fun valueToDB(value: Any?): Any? {
+
+            try {
+                (value as BigDecimal).setScale(SCALE_TWO, 
RoundingMode.UNNECESSARY)
+            } catch (e: Exception) {
+                e.printStackTrace()
+                throw BadAmount(value)
+            }
+
+            return super.valueToDB(value)
+        }
+    }
+
+    /**
+     * Make sure the number entered by upper layers does not need any rounding
+     * to conform to scale == 2
+     */
+    fun amount(name: String): Column<Amount> {
+        return registerColumn(name, AmountColumnType())
+    }
+}
+
+
+object BankTransactionsTable : IntIdTableWithAmount() {
 
     /* Using varchar to store the IBAN - or possibly other formats
      * - from the counterpart.  */
     val counterpart = varchar("counterpart", MAX_ID_LENGTH)
-    val amountSign = integer("amountSign").check { (it eq 1) or (it eq -1)}
-    val amountValue = integer("amountValue").check { GreaterEqOp(it, 
intParam(0)) }
-    val amountFraction = integer("amountFraction").check { LessOp(it, 
intParam(100)) }
+    val amount = amount("amount")
     val subject = varchar("subject", MAX_SUBJECT_LENGTH)
     val date = date("date")
     val localCustomer = reference("localCustomer", BankCustomersTable)
@@ -119,12 +177,12 @@ class BankTransactionEntity(id: EntityID<Int>) : 
IntEntity(id) {
 
     var subject by BankTransactionsTable.subject
     var date by BankTransactionsTable.date
-
-    var amountValue by BankTransactionsTable.amountValue
-    var amountFraction by BankTransactionsTable.amountFraction
-    var amountSign by BankTransactionsTable.amountSign
+    var amount by BankTransactionsTable.amount
 }
 
+
+
+
 /**
  * This table information *not* related to EBICS, for all
  * its customers.
@@ -311,7 +369,6 @@ fun dbCreateTables() {
     TransactionManager.manager.defaultIsolationLevel = 
Connection.TRANSACTION_SERIALIZABLE
 
     transaction {
-        // addLogger(StdOutSqlLogger)
 
         SchemaUtils.createMissingTablesAndColumns(
             BankCustomersTable,
@@ -323,4 +380,4 @@ fun dbCreateTables() {
             EbicsOrderSignaturesTable
         )
     }
-}
+}
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 74a0745..c44ce30 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -43,6 +43,8 @@ import org.jetbrains.exposed.sql.transactions.transaction
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.w3c.dom.Document
+import java.lang.ArithmeticException
+import java.math.BigDecimal
 import java.security.interfaces.RSAPublicKey
 import java.text.DateFormat
 import javax.sql.rowset.serial.SerialBlob
@@ -53,6 +55,10 @@ val logger: Logger = 
LoggerFactory.getLogger("tech.libeufin.sandbox")
 
 class CustomerNotFound(id: String?) : Exception("Customer ${id} not found")
 class BadInputData(inputData: String?) : Exception("Customer provided invalid 
input data: ${inputData}")
+class BadAmount(badValue: Any?) : Exception("Value '${badValue}' is not a 
valid amount")
+class UnacceptableFractional(statusCode: HttpStatusCode, badNumber: 
BigDecimal) : Exception(
+    "Unacceptable fractional part ${badNumber}"
+)
 
 
 fun findCustomer(id: String?): BankCustomerEntity {
@@ -156,6 +162,10 @@ fun main() {
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
                 call.respondText("Internal server error.", 
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
             }
+            exception<ArithmeticException> { cause ->
+                logger.error("Exception while handling '${call.request.uri}'", 
cause)
+                call.respondText("Invalid arithmetic attempted.", 
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
+            }
         }
         // TODO: add another intercept call that adds schema validation before 
the response is sent
         intercept(ApplicationCallPipeline.Fallback) {
diff --git a/sandbox/src/test/kotlin/DbTest.kt 
b/sandbox/src/test/kotlin/DbTest.kt
index 681ac9d..0bffc83 100644
--- a/sandbox/src/test/kotlin/DbTest.kt
+++ b/sandbox/src/test/kotlin/DbTest.kt
@@ -5,34 +5,91 @@ import org.jetbrains.exposed.sql.Database
 import org.jetbrains.exposed.sql.SchemaUtils
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.joda.time.DateTime
+import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.ExpectedException
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.math.MathContext
+import java.math.RoundingMode
+import kotlin.math.abs
 import kotlin.test.assertFailsWith
 import kotlin.test.assertTrue
 
+
+
+
 class DbTest {
 
-    @Test
-    fun valuesRange() {
+    @Before
+    fun muteStderr() {
+        System.setErr(PrintStream(ByteArrayOutputStream()))
+    }
 
+    @Before
+    fun connectAndMakeTables() {
         Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = 
"org.h2.Driver")
-
         transaction {
-
             SchemaUtils.create(BankTransactionsTable)
             SchemaUtils.create(BankCustomersTable)
+        }
+
+    }
+
+    @Test
+    fun goodAmount() {
+
+        transaction {
 
-            val customer = BankCustomerEntity.new {
-                name = "employee"
+            BankTransactionEntity.new {
+                amount = Amount("1")
+                counterpart = "IBAN"
+                subject = "Salary"
+                date = DateTime.now()
+                localCustomer = BankCustomerEntity.new {
+                    name = "employee"
+                }
+            }
+
+            BankTransactionEntity.new {
+                amount = Amount("1.11")
+                counterpart = "IBAN"
+                subject = "Salary"
+                date = DateTime.now()
+                localCustomer = BankCustomerEntity.new {
+                    name = "employee"
+                }
             }
 
-            val ledgerEntry = BankTransactionEntity.new {
-                amountSign = 1
-                amountValue = 5
-                amountFraction = 0
+            val x = BankTransactionEntity.new {
+                amount = Amount("1.110000000000") // BigDecimal does not crop 
the trailing zeros
                 counterpart = "IBAN"
                 subject = "Salary"
                 date = DateTime.now()
-                localCustomer = customer
+                localCustomer = BankCustomerEntity.new {
+                    name = "employee"
+                }
+            }
+        }
+    }
+
+    @Test
+    fun badAmount() {
+
+        assertFailsWith<BadAmount> {
+            transaction {
+                BankTransactionEntity.new {
+                    amount = Amount("1.10001")
+                    counterpart = "IBAN"
+                    subject = "Salary"
+                    date = DateTime.now()
+                    localCustomer = BankCustomerEntity.new {
+                        name = "employee"
+                    }
+                }
             }
         }
     }

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]