[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: database primitives to check fractional values,
gnunet <=