summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMS <ms@taler.net>2023-03-13 10:15:42 +0100
committerMS <ms@taler.net>2023-03-13 10:15:42 +0100
commit7dfdeeda061e63136d7ae805bf2e621d34de52fd (patch)
treec70b3ee119d1f4aa9b89432a97fe3895d9bf3592
parent0e7ebe417cb41b9e9942616af74dbce6337a6d1a (diff)
downloadlibeufin-7dfdeeda061e63136d7ae805bf2e621d34de52fd.tar.gz
libeufin-7dfdeeda061e63136d7ae805bf2e621d34de52fd.tar.bz2
libeufin-7dfdeeda061e63136d7ae805bf2e621d34de52fd.zip
Addressing #7515 (core change).v0.9.2-dev.0
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt131
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt40
2 files changed, 152 insertions, 19 deletions
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 3281b67a..00688082 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -19,6 +19,7 @@
package tech.libeufin.sandbox
+import io.ktor.http.*
import org.jetbrains.exposed.dao.Entity
import org.jetbrains.exposed.dao.EntityClass
import org.jetbrains.exposed.dao.IntEntity
@@ -32,7 +33,10 @@ import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
+import tech.libeufin.util.internalServerError
import java.sql.Connection
+import kotlin.reflect.*
+import kotlin.reflect.full.*
/**
* All the states to give a subscriber.
@@ -87,29 +91,122 @@ enum class KeyState {
RELEASED
}
+/**
+ * Stores one config object to the database. Each field
+ * name and value populate respectively the configKey and
+ * configValue columns. Rows are defined in the following way:
+ * demobankName | configKey | configValue
+ */
+fun insertConfigPairs(config: DemobankConfig, override: Boolean = false) {
+ // Fill the config key-value pairs in the DB.
+ config::class.declaredMemberProperties.forEach { configField ->
+ val maybeValue = configField.getter.call(config)
+ if (override) {
+ val maybeConfigPair = DemobankConfigPairEntity.find {
+ DemobankConfigPairsTable.configKey eq configField.name
+ }.firstOrNull()
+ if (maybeConfigPair == null)
+ throw internalServerError("Cannot override config value '${configField.name}' not found.")
+ maybeConfigPair.configValue = maybeValue?.toString()
+ return@forEach
+ }
+ DemobankConfigPairEntity.new {
+ this.demobankName = config.demobankName
+ this.configKey = configField.name
+ this.configValue = maybeValue?.toString()
+ }
+ }
+}
+
+object DemobankConfigPairsTable : LongIdTable() {
+ val demobankName = text("demobankName")
+ val configKey = text("configKey")
+ val configValue = text("configValue").nullable()
+}
+
+class DemobankConfigPairEntity(id: EntityID<Long>) : LongEntity(id) {
+ companion object : LongEntityClass<DemobankConfigPairEntity>(DemobankConfigPairsTable)
+ var demobankName by DemobankConfigPairsTable.demobankName
+ var configKey by DemobankConfigPairsTable.configKey
+ var configValue by DemobankConfigPairsTable.configValue
+}
+
object DemobankConfigsTable : LongIdTable() {
- val currency = text("currency")
- val allowRegistrations = bool("allowRegistrations")
- val withSignupBonus = bool("withSignupBonus")
- val bankDebtLimit = integer("bankDebtLimit")
- val usersDebtLimit = integer("usersDebtLimit")
val name = text("hostname")
- val suggestedExchangeBaseUrl = text("suggestedExchangeBaseUrl").nullable()
- val suggestedExchangePayto = text("suggestedExchangePayto").nullable()
- val captchaUrl = text("captchaUrl").nullable()
+}
+
+// Helpers for handling config values in memory.
+typealias DemobankConfigKey = String
+typealias DemobankConfigValue = String?
+fun Pair<DemobankConfigKey, DemobankConfigValue>.expectValue(): String {
+ if (this.second == null) throw internalServerError("Config value for '${this.first}' is null in the database.")
+ return this.second as String
}
class DemobankConfigEntity(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<DemobankConfigEntity>(DemobankConfigsTable)
- var currency by DemobankConfigsTable.currency
- var allowRegistrations by DemobankConfigsTable.allowRegistrations
- var withSignupBonus by DemobankConfigsTable.withSignupBonus
- var bankDebtLimit by DemobankConfigsTable.bankDebtLimit
- var usersDebtLimit by DemobankConfigsTable.usersDebtLimit
var name by DemobankConfigsTable.name
- var captchaUrl by DemobankConfigsTable.captchaUrl
- var suggestedExchangeBaseUrl by DemobankConfigsTable.suggestedExchangeBaseUrl
- var suggestedExchangePayto by DemobankConfigsTable.suggestedExchangePayto
+ /**
+ * This object gets defined by parsing all the configuration
+ * values found in the DB for one demobank. Those values are
+ * retrieved from _another_ table.
+ */
+ val config: DemobankConfig by lazy {
+ // Getting all the values for this demobank.
+ val configPairs: List<Pair<DemobankConfigKey, DemobankConfigValue>> = transaction {
+ val maybeConfigPairs = DemobankConfigPairEntity.find {
+ DemobankConfigPairsTable.demobankName.eq(name)
+ }
+ if (maybeConfigPairs.empty()) throw SandboxError(
+ HttpStatusCode.InternalServerError,
+ "No config values of $name were found in the database"
+ )
+ // Copying results to a DB-agnostic list, to later operate out of "transaction {}"
+ maybeConfigPairs.map { Pair(it.configKey, it.configValue) }
+ }
+ // Building the args to instantiate a DemobankConfig (non-Exposed) object.
+ val args = mutableMapOf<KParameter, Any?>()
+ // For each constructor parameter name, find the same-named database entry.
+ val configClass = DemobankConfig::class
+ if (configClass.primaryConstructor == null) {
+ throw SandboxError(
+ HttpStatusCode.InternalServerError,
+ "${configClass.simpleName} primaryConstructor is null."
+ )
+ }
+ if (configClass.primaryConstructor?.parameters == null) {
+ throw SandboxError(
+ HttpStatusCode.InternalServerError,
+ "${configClass.simpleName} primaryConstructor" +
+ " arguments is null. Cannot set any config value."
+ )
+ }
+ // For each field in the config object, find the respective DB row.
+ configClass.primaryConstructor?.parameters?.forEach { par: KParameter ->
+ val configPairFromDb: Pair<DemobankConfigKey, DemobankConfigValue>?
+ = configPairs.firstOrNull {
+ configPair: Pair<DemobankConfigKey, DemobankConfigValue> ->
+ configPair.first == par.name
+ }
+ if (configPairFromDb == null) {
+ throw SandboxError(
+ HttpStatusCode.InternalServerError,
+ "Config key '${par.name}' not found in the database."
+ )
+ }
+ when(par.type) {
+ // non-nullable
+ typeOf<Boolean>() -> { args[par] = configPairFromDb.expectValue().toBoolean() }
+ typeOf<Int>() -> { args[par] = configPairFromDb.expectValue().toInt() }
+ // nullable
+ typeOf<Boolean?>() -> { args[par] = configPairFromDb.second?.toBoolean() }
+ typeOf<Int?>() -> { args[par] = configPairFromDb.second?.toInt() }
+ else -> args[par] = configPairFromDb.second
+ }
+ }
+ // Proceeding now to instantiate the config class, and make it a field of this type.
+ configClass.primaryConstructor!!.callBy(args)
+ }
}
/**
@@ -538,6 +635,7 @@ fun dbDropTables(dbConnectionString: String) {
BankAccountReportsTable,
BankAccountStatementsTable,
DemobankConfigsTable,
+ DemobankConfigPairsTable,
TalerWithdrawalsTable,
DemobankCustomersTable,
CashoutOperationsTable
@@ -551,6 +649,7 @@ fun dbCreateTables(dbConnectionString: String) {
transaction {
SchemaUtils.create(
DemobankConfigsTable,
+ DemobankConfigPairsTable,
EbicsSubscribersTable,
EbicsHostsTable,
EbicsDownloadTransactionsTable,
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 16f1d6b9..912c2ee6 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -19,6 +19,8 @@
package tech.libeufin.sandbox
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.server.application.*
import io.ktor.http.HttpStatusCode
import io.ktor.server.request.*
@@ -30,6 +32,20 @@ import java.security.interfaces.RSAPublicKey
import java.util.*
import java.util.zip.DeflaterInputStream
+data class DemobankConfig(
+ val allowRegistrations: Boolean,
+ val currency: String,
+ val bankDebtLimit: Int,
+ val usersDebtLimit: Int,
+ val withSignupBonus: Boolean,
+ val demobankName: String, // demobank name.
+ val captchaUrl: String? = null,
+ val smsTan: String? = null, // fixme: move the config subcommand
+ val emailTan: String? = null, // fixme: same as above.
+ val suggestedExchangeBaseUrl: String? = null,
+ val suggestedExchangePayto: String? = null
+)
+
/**
* Helps to communicate Camt values without having
* to parse the XML each time one is needed.
@@ -120,8 +136,8 @@ fun insertNewAccount(username: String,
this.demoBank = demobankFromDb
this.isPublic = isPublic
}
- if (demobankFromDb.withSignupBonus)
- newBankAccount.bonus("${demobankFromDb.currency}:100")
+ if (demobankFromDb.config.withSignupBonus)
+ newBankAccount.bonus("${demobankFromDb.config.currency}:100")
AccountPair(customer = newCustomer, bankAccount = newBankAccount)
}
}
@@ -220,6 +236,24 @@ fun getHistoryElementFromTransactionRow(dbRow: BankAccountTransactionEntity): Ra
)
}
+fun printConfig(demobank: DemobankConfigEntity) {
+ val ret = ObjectMapper()
+ ret.configure(SerializationFeature.INDENT_OUTPUT, true)
+ println(
+ ret.writeValueAsString(object {
+ val currency = demobank.config.currency
+ val bankDebtLimit = demobank.config.bankDebtLimit
+ val usersDebtLimit = demobank.config.usersDebtLimit
+ val allowRegistrations = demobank.config.allowRegistrations
+ val name = demobank.name // always 'default'
+ val withSignupBonus = demobank.config.withSignupBonus
+ val captchaUrl = demobank.config.captchaUrl
+ val suggestedExchangeBaseUrl = demobank.config.suggestedExchangeBaseUrl
+ val suggestedExchangePayto = demobank.config.suggestedExchangePayto
+ })
+ )
+}
+
fun getHistoryElementFromTransactionRow(
dbRow: BankAccountFreshTransactionEntity
): RawPayment {
@@ -440,4 +474,4 @@ fun prepareEbicsPayload(
val enc = CryptoUtil.encryptEbicsE002(compressedResponse, pub)
return Pair(Base64.getEncoder().encodeToString(enc.encryptedData), enc)
-} \ No newline at end of file
+}