diff options
author | MS <ms@taler.net> | 2023-03-13 10:15:42 +0100 |
---|---|---|
committer | MS <ms@taler.net> | 2023-03-13 10:15:42 +0100 |
commit | 7dfdeeda061e63136d7ae805bf2e621d34de52fd (patch) | |
tree | c70b3ee119d1f4aa9b89432a97fe3895d9bf3592 | |
parent | 0e7ebe417cb41b9e9942616af74dbce6337a6d1a (diff) | |
download | libeufin-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.kt | 131 | ||||
-rw-r--r-- | sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt | 40 |
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 +} |