commit 92f6950d87b04eef772bf87279a09cee9ebab08c parent af5cbaada39dc6d3738b354c642e941eb1b2900f Author: Antoine A <> Date: Mon, 17 Jun 2024 12:40:39 +0200 Clean code Diffstat:
54 files changed, 192 insertions(+), 267 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt @@ -142,8 +142,6 @@ fun TalerConfig.loadBankConfig(): BankConfig { ) } -fun String.notEmptyOrNull(): String? = if (isEmpty()) null else this - fun TalerConfig.currencySpecificationFor(currency: String): CurrencySpecification = sections.find { it.startsWith("CURRENCY-") && requireBoolean(it, "enabled") && requireString(it, "code") == currency diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Error.kt b/bank/src/main/kotlin/tech/libeufin/bank/Error.kt @@ -18,11 +18,6 @@ */ package tech.libeufin.bank -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.response.* -import io.ktor.util.* -import kotlinx.serialization.Serializable import tech.libeufin.common.* /* ----- Currency checks ----- */ diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -29,34 +29,22 @@ import com.github.ajalt.clikt.parameters.groups.cooccurring import com.github.ajalt.clikt.parameters.groups.provideDelegate import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.boolean -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* -import io.ktor.server.engine.* import io.ktor.server.http.content.* -import io.ktor.server.netty.* -import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.utils.io.* -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json -import org.postgresql.util.PSQLState import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.slf4j.event.Level import tech.libeufin.bank.api.* import tech.libeufin.bank.db.AccountDAO.* import tech.libeufin.bank.db.Database import tech.libeufin.common.* -import tech.libeufin.common.api.* +import tech.libeufin.common.api.serve +import tech.libeufin.common.api.talerApi import tech.libeufin.common.db.dbInit import tech.libeufin.common.db.pgDataSource -import java.net.InetAddress -import java.sql.SQLException import java.time.Instant -import java.util.zip.DataFormatException -import java.util.zip.Inflater import kotlin.io.path.Path import kotlin.io.path.exists import kotlin.io.path.readText diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt @@ -21,8 +21,6 @@ package tech.libeufin.bank import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -30,10 +28,8 @@ import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.longOrNull -import tech.libeufin.common.* -import java.net.URL +import tech.libeufin.common.badRequest import java.time.Duration -import java.time.Instant import java.time.temporal.ChronoUnit import java.util.concurrent.TimeUnit @@ -41,7 +37,7 @@ import java.util.concurrent.TimeUnit * Internal representation of relative times. The * "forever" case is represented with Long.MAX_VALUE. */ -@Serializable() +@Serializable data class RelativeTime( @Serializable(with = Serializer::class) val d_us: Duration diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/ConversionApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/ConversionApi.kt @@ -33,13 +33,11 @@ import tech.libeufin.common.* fun Routing.conversionApi(db: Database, ctx: BankConfig) = conditional(ctx.allowConversion) { get("/conversion-info/config") { val config = db.conversion.getConfig(ctx.regionalCurrency, ctx.fiatCurrency!!) - if (config == null) { - throw apiError( - HttpStatusCode.NotImplemented, - "conversion rate not configured yet", + ?: throw apiError( + HttpStatusCode.NotImplemented, + "conversion rate not configured yet", TalerErrorCode.END ) - } call.respond( ConversionConfig( regional_currency = ctx.regionalCurrency, diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt @@ -185,7 +185,7 @@ suspend fun createAccount( ) } else if (req.tan_channel != null) { - if (cfg.tanChannels.get(req.tan_channel) == null) { + if (cfg.tanChannels[req.tan_channel] == null) { throw unsupportedTanChannel(req.tan_channel) } val missing = when (req.tan_channel) { @@ -688,7 +688,7 @@ private fun Routing.coreBankTanApi(db: Database, ctx: BankConfig) { ) is TanSendResult.Success -> { res.tanCode?.run { - val (tanScript, tanEnv) = ctx.tanChannels.get(res.tanChannel) + val (tanScript, tanEnv) = ctx.tanChannels[res.tanChannel] ?: throw unsupportedTanChannel(res.tanChannel) val msg = "T-${res.tanCode} is your ${ctx.name} verification code" val exitValue = withContext(Dispatchers.IO) { @@ -707,7 +707,7 @@ private fun Routing.coreBankTanApi(db: Database, ctx: BankConfig) { val exitValue = process.exitValue() if (exitValue != 0) { val out = runCatching { - process.getInputStream().use { + process.inputStream.use { reader().readText() } }.getOrDefault("") diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt @@ -16,14 +16,14 @@ * License along with LibEuFin; see the file COPYING. If not, see * <http://www.gnu.org/licenses/> */ -package tech.libeufin.bank +package tech.libeufin.bank.auth import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import kotlinx.serialization.json.Json -import tech.libeufin.bank.auth.username +import tech.libeufin.bank.* import tech.libeufin.bank.db.Database import tech.libeufin.bank.db.TanDAO.Challenge import java.security.SecureRandom @@ -38,9 +38,9 @@ import java.time.Instant * to send the TAN code, otherwise defaults will be used. */ suspend inline fun <reified B> ApplicationCall.respondChallenge( - db: Database, - op: Operation, - body: B, + db: Database, + op: Operation, + body: B, channel: TanChannel? = null, info: String? = null ) { diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt @@ -172,7 +172,7 @@ private suspend fun ApplicationCall.doTokenAuth( TalerErrorCode.GENERIC_HTTP_HEADERS_MALFORMED ) val decoded = try { - Base32Crockford.decode(bearer.slice(13..bearer.length-1)) + Base32Crockford.decode(bearer.slice(13..<bearer.length)) } catch (e: Exception) { throw badRequest( e.message, TalerErrorCode.GENERIC_HTTP_HEADERS_MALFORMED diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt @@ -19,7 +19,6 @@ package tech.libeufin.bank.db -import tech.libeufin.bank.* import tech.libeufin.common.* import tech.libeufin.common.db.* import java.time.Instant diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt @@ -25,7 +25,6 @@ import io.ktor.server.plugins.* import io.ktor.server.request.* import io.ktor.server.routing.* import io.ktor.server.util.* -import io.ktor.util.pipeline.* import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind @@ -37,7 +36,7 @@ import tech.libeufin.bank.auth.username import tech.libeufin.bank.db.AccountDAO.AccountCreationResult import tech.libeufin.bank.db.Database import tech.libeufin.common.* -import tech.libeufin.common.api.* +import tech.libeufin.common.api.intercept import java.util.* fun ApplicationCall.uuidPath(name: String): UUID { diff --git a/bank/src/main/kotlin/tech/libeufin/bank/params.kt b/bank/src/main/kotlin/tech/libeufin/bank/params.kt @@ -25,7 +25,6 @@ import java.time.Instant import java.time.LocalDateTime import java.time.ZoneOffset import java.time.temporal.TemporalAdjusters -import java.util.* data class MonitorParams( val timeframe: Timeframe, @@ -49,7 +48,7 @@ data class MonitorParams( val names_fmt = names.joinToString() fun extract(params: Parameters): MonitorParams { - val raw = params.get("timeframe") ?: "hour" + val raw = params["timeframe"] ?: "hour" if (!names.contains(raw)) { throw badRequest("Param 'timeframe' must be one of $names_fmt", TalerErrorCode.GENERIC_PARAMETER_MALFORMED) } @@ -88,7 +87,7 @@ data class AccountParams( ) { companion object { fun extract(params: Parameters): AccountParams { - val loginFilter = params.get("filter_name")?.run { "%$this%" } ?: "%" + val loginFilter = params["filter_name"]?.run { "%$this%" } ?: "%" return AccountParams(PageParams.extract(params), loginFilter) } } @@ -116,10 +115,10 @@ data class StatusParams( val old_state: WithdrawalStatus ) { companion object { - val names = WithdrawalStatus.entries.map { it.name } - val names_fmt = names.joinToString() + private val names = WithdrawalStatus.entries.map { it.name } + private val names_fmt = names.joinToString() fun extract(params: Parameters): StatusParams { - val old_state = params.get("old_state") ?: "pending" + val old_state = params["old_state"] ?: "pending" if (!names.contains(old_state)) { throw badRequest("Param 'old_state' must be one of $names_fmt", TalerErrorCode.GENERIC_PARAMETER_MALFORMED) } diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -988,7 +988,7 @@ class CoreBankTransactionsApiTest { }, { // Ignore transactions of other accounts - tx("exchange", "KUDOS:0.1", "merchant",) + tx("exchange", "KUDOS:0.1", "merchant") } ) ) @@ -1619,7 +1619,7 @@ class CoreBankCashoutApiTest { historyRoutine<Cashouts>( url = "/accounts/customer/cashouts", ids = { it.cashouts.map { it.cashout_id } }, - registered = listOf({ cashout("KUDOS:0.1") }), + registered = listOf { cashout("KUDOS:0.1") }, polling = false ) } @@ -1631,7 +1631,7 @@ class CoreBankCashoutApiTest { historyRoutine<GlobalCashouts>( url = "/cashouts", ids = { it.cashouts.map { it.cashout_id } }, - registered = listOf({ cashout("KUDOS:0.1") }), + registered = listOf { cashout("KUDOS:0.1") }, polling = false, auth = "admin" ) diff --git a/bank/src/test/kotlin/GcTest.kt b/bank/src/test/kotlin/GcTest.kt @@ -35,18 +35,18 @@ import kotlin.test.assertIs class GcTest { @Test fun gc() = bankSetup { db -> db.conn { conn -> - suspend fun assertNb(nb: Int, stmt: String) { + fun assertNb(nb: Int, stmt: String) { assertEquals(nb, conn.prepareStatement(stmt).one { it.getInt(1) }) } - suspend fun assertNbAccount(nb: Int) = assertNb(nb, "SELECT count(*) from bank_accounts") - suspend fun assertNbTokens(nb: Int) = assertNb(nb, "SELECT count(*) from bearer_tokens") - suspend fun assertNbTan(nb: Int) = assertNb(nb, "SELECT count(*) from tan_challenges") - suspend fun assertNbCashout(nb: Int) = assertNb(nb, "SELECT count(*) from cashout_operations") - suspend fun assertNbWithdrawal(nb: Int) = assertNb(nb, "SELECT count(*) from taler_withdrawal_operations") - suspend fun assertNbBankTx(nb: Int) = assertNb(nb, "SELECT count(*) from bank_transaction_operations") - suspend fun assertNbTx(nb: Int) = assertNb(nb, "SELECT count(*) from bank_account_transactions") - suspend fun assertNbIncoming(nb: Int) = assertNb(nb, "SELECT count(*) from taler_exchange_incoming") - suspend fun assertNbOutgoing(nb: Int) = assertNb(nb, "SELECT count(*) from taler_exchange_outgoing") + fun assertNbAccount(nb: Int) = assertNb(nb, "SELECT count(*) from bank_accounts") + fun assertNbTokens(nb: Int) = assertNb(nb, "SELECT count(*) from bearer_tokens") + fun assertNbTan(nb: Int) = assertNb(nb, "SELECT count(*) from tan_challenges") + fun assertNbCashout(nb: Int) = assertNb(nb, "SELECT count(*) from cashout_operations") + fun assertNbWithdrawal(nb: Int) = assertNb(nb, "SELECT count(*) from taler_withdrawal_operations") + fun assertNbBankTx(nb: Int) = assertNb(nb, "SELECT count(*) from bank_transaction_operations") + fun assertNbTx(nb: Int) = assertNb(nb, "SELECT count(*) from bank_account_transactions") + fun assertNbIncoming(nb: Int) = assertNb(nb, "SELECT count(*) from taler_exchange_incoming") + fun assertNbOutgoing(nb: Int) = assertNb(nb, "SELECT count(*) from taler_exchange_outgoing") // Time calculation val abortAfter = Duration.ofMinutes(15) diff --git a/bank/src/test/kotlin/SecurityTest.kt b/bank/src/test/kotlin/SecurityTest.kt @@ -26,7 +26,7 @@ import tech.libeufin.common.* inline fun <reified B> HttpRequestBuilder.jsonDeflate(b: B) { val json = Json.encodeToString(kotlinx.serialization.serializer<B>(), b) contentType(ContentType.Application.Json) - headers.set(HttpHeaders.ContentEncoding, "deflate") + headers[HttpHeaders.ContentEncoding] = "deflate" setBody(json.toByteArray().inputStream().deflate().readBytes()) } @@ -57,7 +57,7 @@ class SecurityTest { // Check unknown encoding client.postA("/accounts/merchant/transactions") { - headers.set(HttpHeaders.ContentEncoding, "unknown") + headers[HttpHeaders.ContentEncoding] = "unknown" json(valid_req) }.assertStatus(HttpStatusCode.UnsupportedMediaType, TalerErrorCode.GENERIC_COMPRESSION_INVALID) } diff --git a/bank/src/test/kotlin/StatsTest.kt b/bank/src/test/kotlin/StatsTest.kt @@ -123,7 +123,7 @@ class StatsTest { @Test fun timeframe() = bankSetup { db -> db.conn { conn -> - suspend fun register(timestamp: LocalDateTime, amount: TalerAmount) { + fun register(timestamp: LocalDateTime, amount: TalerAmount) { val stmt = conn.prepareStatement( "CALL stats_register_payment('taler_out', ?::timestamp, (?, ?)::taler_amount, null)" ) diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt @@ -304,7 +304,7 @@ suspend fun ApplicationTestBuilder.convert(amount: String): TalerAmount { .assertOkJson<ConversionResponse>().amount_credit } -suspend fun tanCode(info: String): String? { +fun tanCode(info: String): String? { try { val file = Path("/tmp/tan-$info.txt") val code = file.readText().split(" ", limit=2).first() @@ -397,7 +397,7 @@ fun HttpRequestBuilder.pwAuth(username: String? = null) { } else if (url.pathSegments[1] == "accounts") { // Extract username from path val login = url.pathSegments[2] - basicAuth("$login", "$login-password") + basicAuth(login, "$login-password") } } diff --git a/bank/src/test/kotlin/routines.kt b/bank/src/test/kotlin/routines.kt @@ -18,7 +18,6 @@ */ import io.ktor.client.request.* -import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.server.testing.* import kotlinx.coroutines.coroutineScope @@ -28,7 +27,8 @@ import kotlinx.serialization.json.JsonObject import tech.libeufin.bank.BankAccountCreateWithdrawalResponse import tech.libeufin.bank.WithdrawalStatus import tech.libeufin.common.* -import tech.libeufin.common.test.* +import tech.libeufin.common.test.abstractHistoryRoutine +import tech.libeufin.common.test.assertTime import kotlin.test.assertEquals // Test endpoint is correctly authenticated diff --git a/common/src/main/kotlin/ApiError.kt b/common/src/main/kotlin/ApiError.kt @@ -23,8 +23,6 @@ import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.util.* import kotlinx.serialization.Serializable -import tech.libeufin.common.TalerAmount -import tech.libeufin.common.TalerErrorCode /** * Convenience type to throw errors along the API activity diff --git a/common/src/main/kotlin/Cli.kt b/common/src/main/kotlin/Cli.kt @@ -111,15 +111,11 @@ private class CliConfigGet(private val configSource: ConfigSource) : CliktComman val config = configSource.fromFile(common.config) if (isPath) { val res = config.lookupPath(section, option) - if (res == null) { - throw Exception("option '$option' in section '$section' not found in config") - } + ?: throw Exception("option '$option' in section '$section' not found in config") println(res) } else { val res = config.lookupString(section, option) - if (res == null) { - throw Exception("option '$option' in section '$section' not found in config") - } + ?: throw Exception("option '$option' in section '$section' not found in config") println(res) } } diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt @@ -20,7 +20,7 @@ package tech.libeufin.common import io.ktor.http.* -import io.ktor.server.plugins.BadRequestException +import io.ktor.server.plugins.* import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind @@ -32,13 +32,10 @@ import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.longOrNull -import tech.libeufin.common.* import java.net.URI import java.net.URL -import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit -import java.util.concurrent.TimeUnit sealed class CommonError(msg: String): Exception(msg) { class AmountFormat(msg: String): CommonError(msg) @@ -92,12 +89,8 @@ data class TalerProtocolTimestamp( @Serializable(with = ExchangeUrl.Serializer::class) -class ExchangeUrl { - val url: String - - constructor(raw: String) { - url = URL(raw).toString() - } +class ExchangeUrl(raw: String) { + val url: String = URL(raw).toString() override fun toString(): String = url @@ -147,10 +140,10 @@ class DecimalNumber { } override fun toString(): String { - if (frac == 0) { - return "$value" + return if (frac == 0) { + "$value" } else { - return "$value.${frac.toString().padStart(8, '0')}" + "$value.${frac.toString().padStart(8, '0')}" .dropLastWhile { it == '0' } // Trim useless fractional trailing 0 } } @@ -217,10 +210,10 @@ class TalerAmount { } override fun toString(): String { - if (frac == 0) { - return "$currency:$value" + return if (frac == 0) { + "$currency:$value" } else { - return "$currency:$value.${frac.toString().padStart(8, '0')}" + "$currency:$value.${frac.toString().padStart(8, '0')}" .dropLastWhile { it == '0' } // Trim useless fractional trailing 0 } } diff --git a/common/src/main/kotlin/TalerConfig.kt b/common/src/main/kotlin/TalerConfig.kt @@ -301,15 +301,14 @@ class TalerConfig internal constructor( } val result = StringBuilder() var l = 0 - val s = x - while (l < s.length) { - if (s[l] != '$') { + while (l < x.length) { + if (x[l] != '$') { // normal character - result.append(s[l]) + result.append(x[l]) l++ continue } - if (l + 1 < s.length && s[l + 1] == '{') { + if (l + 1 < x.length && x[l + 1] == '{') { // ${var} var depth = 1 val start = l @@ -317,14 +316,14 @@ class TalerConfig internal constructor( var hasDefault = false var insideNamePath = true // Find end of the ${...} expression - while (p < s.length) { - if (s[p] == '}') { + while (p < x.length) { + if (x[p] == '}') { insideNamePath = false depth-- - } else if (s.length > p + 1 && s[p] == '$' && s[p + 1] == '{') { + } else if (x.length > p + 1 && x[p] == '$' && x[p + 1] == '{') { depth++ insideNamePath = false - } else if (s.length > p + 1 && insideNamePath && s[p] == ':' && s[p + 1] == '-') { + } else if (x.length > p + 1 && insideNamePath && x[p] == ':' && x[p + 1] == '-') { hasDefault = true } p++ @@ -333,7 +332,7 @@ class TalerConfig internal constructor( } } if (depth == 0) { - val inner = s.substring(start + 2, p - 1) + val inner = x.substring(start + 2, p - 1) val varName: String val varDefault: String? if (hasDefault) { @@ -362,10 +361,10 @@ class TalerConfig internal constructor( } else { // $var var varEnd = l + 1 - while (varEnd < s.length && (s[varEnd].isLetterOrDigit() || s[varEnd] == '_')) { + while (varEnd < x.length && (x[varEnd].isLetterOrDigit() || x[varEnd] == '_')) { varEnd++ } - val varName = s.substring(l + 1, varEnd) + val varName = x.substring(l + 1, varEnd) val res = variableLookup(varName) if (res != null) { result.append(res) @@ -439,8 +438,7 @@ class TalerConfig internal constructor( val canonSection = section.uppercase() val canonOption = option.uppercase() val str = this.sectionMap[canonSection]?.entries?.get(canonOption) - if (str == null) return null - if (str == "") return null + if (str == null || str == "") return null return str } diff --git a/common/src/main/kotlin/TalerMessage.kt b/common/src/main/kotlin/TalerMessage.kt @@ -19,13 +19,7 @@ package tech.libeufin.common -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder /** Response GET /taler-wire-gateway/config */ @Serializable diff --git a/common/src/main/kotlin/api/route.kt b/common/src/main/kotlin/api/route.kt @@ -19,11 +19,8 @@ package tech.libeufin.common.api -import io.ktor.http.* import io.ktor.server.application.* -import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.util.* import io.ktor.util.pipeline.* fun Route.intercept(callback: Route.() -> Unit, interceptor: suspend PipelineContext<Unit, ApplicationCall>.() -> Unit): Route { diff --git a/common/src/main/kotlin/api/server.kt b/common/src/main/kotlin/api/server.kt @@ -23,7 +23,6 @@ import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* import io.ktor.server.engine.* -import io.ktor.server.http.content.* import io.ktor.server.netty.* import io.ktor.server.plugins.* import io.ktor.server.plugins.callloging.* @@ -32,26 +31,18 @@ import io.ktor.server.plugins.cors.routing.* import io.ktor.server.plugins.forwardedheaders.* import io.ktor.server.plugins.statuspages.* import io.ktor.server.request.* -import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.utils.io.* import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import org.postgresql.util.PSQLState import org.slf4j.Logger -import org.slf4j.LoggerFactory import org.slf4j.event.Level import tech.libeufin.common.* -import tech.libeufin.common.db.dbInit -import tech.libeufin.common.db.pgDataSource import java.net.InetAddress import java.sql.SQLException -import java.time.Instant import java.util.zip.DataFormatException import java.util.zip.Inflater -import kotlin.io.path.Path -import kotlin.io.path.exists -import kotlin.io.path.readText /** * This plugin checks for body length limit and inflates the requests that have "Content-Encoding: deflate" diff --git a/common/src/main/kotlin/db/helpers.kt b/common/src/main/kotlin/db/helpers.kt @@ -19,18 +19,15 @@ package tech.libeufin.common.db -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.* -import tech.libeufin.common.db.DbPool -import tech.libeufin.common.PageParams +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeoutOrNull import tech.libeufin.common.HistoryParams -import org.postgresql.jdbc.PgConnection -import org.postgresql.util.PSQLState -import org.slf4j.Logger -import org.slf4j.LoggerFactory +import tech.libeufin.common.PageParams import java.sql.PreparedStatement import java.sql.ResultSet -import java.sql.SQLException import kotlin.math.abs /** Apply paging logic to a sql query */ diff --git a/common/src/main/kotlin/db/notifications.kt b/common/src/main/kotlin/db/notifications.kt @@ -19,14 +19,14 @@ package tech.libeufin.common.db -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.runBlocking import org.postgresql.ds.PGSimpleDataSource import org.slf4j.Logger -import org.slf4j.LoggerFactory -import tech.libeufin.common.* -import tech.libeufin.common.db.* -import java.util.* +import tech.libeufin.common.ExpoBackoffDecorr +import tech.libeufin.common.fmtLog import java.util.concurrent.ConcurrentHashMap // SharedFlow that are manually counted for manual garbage collection diff --git a/common/src/main/kotlin/db/schema.kt b/common/src/main/kotlin/db/schema.kt @@ -69,7 +69,7 @@ private fun initializeDatabaseTables(conn: PgConnection, cfg: DatabaseConfig, sq val path = Path("${cfg.sqlDir}/$patchName.sql") if (!path.exists()) { - logger.debug("path $path doesn't exist anymore, stopping") + logger.debug("path {} doesn't exist anymore, stopping", path) break } logger.info("applying patch $path") diff --git a/common/src/main/kotlin/helpers.kt b/common/src/main/kotlin/helpers.kt @@ -61,8 +61,7 @@ fun ByteArray.encodeBase64(): String = Base64.getEncoder().encodeToString(this) inline fun InputStream.unzipEach(lambda: (String, InputStream) -> Unit) { ZipInputStream(this).use { zip -> while (true) { - val entry = zip.getNextEntry() - if (entry == null) break + val entry = zip.getNextEntry() ?: break val entryStream = object: FilterInputStream(zip) { override fun close() { zip.closeEntry() diff --git a/common/src/main/kotlin/params.kt b/common/src/main/kotlin/params.kt @@ -20,12 +20,6 @@ package tech.libeufin.common import io.ktor.http.* -import tech.libeufin.common.TalerAmount -import tech.libeufin.common.TalerErrorCode -import java.time.Instant -import java.time.LocalDateTime -import java.time.ZoneOffset -import java.time.temporal.TemporalAdjusters import java.util.* fun Parameters.expect(name: String): String diff --git a/common/src/main/kotlin/test/helpers.kt b/common/src/main/kotlin/test/helpers.kt @@ -19,14 +19,11 @@ package tech.libeufin.common.test -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.client.statement.* -import io.ktor.http.* +import tech.libeufin.common.PageParams +import tech.libeufin.common.assertOk +import tech.libeufin.common.json import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlin.test.assertNotNull -import tech.libeufin.common.* /* ----- Assert ----- */ diff --git a/common/src/main/kotlin/test/routines.kt b/common/src/main/kotlin/test/routines.kt @@ -19,16 +19,14 @@ package tech.libeufin.common.test -import io.ktor.client.request.* import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.testing.* import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import tech.libeufin.common.* +import tech.libeufin.common.assertNoContent +import tech.libeufin.common.assertOkJson -suspend inline fun <reified B> ApplicationTestBuilder.abstractHistoryRoutine( +suspend inline fun <reified B> abstractHistoryRoutine( crossinline ids: (B) -> List<Long>, registered: List<suspend () -> Unit>, ignored: List<suspend () -> Unit> = listOf(), @@ -48,11 +46,11 @@ suspend inline fun <reified B> ApplicationTestBuilder.abstractHistoryRoutine( history("delta=7").assertNoContent() // Run interleaved registered and ignore transactions - val registered_iter = registered.iterator() - val ignored_iter = ignored.iterator() - while (registered_iter.hasNext() || ignored_iter.hasNext()) { - if (registered_iter.hasNext()) registered_iter.next()() - if (ignored_iter.hasNext()) ignored_iter.next()() + val registeredIter = registered.iterator() + val ignoredIter = ignored.iterator() + while (registeredIter.hasNext() || ignoredIter.hasNext()) { + if (registeredIter.hasNext()) registeredIter.next()() + if (ignoredIter.hasNext()) ignoredIter.next()() } val nbRegistered = registered.size diff --git a/common/src/main/kotlin/time.kt b/common/src/main/kotlin/time.kt @@ -39,7 +39,7 @@ fun Instant.micros(): Long { if (micros == Long.MAX_VALUE) throw ArithmeticException() return micros } catch (e: ArithmeticException) { - throw Exception("${this} is too big to be converted to micros resolution", e) + throw Exception("$this is too big to be converted to micros resolution", e) } } @@ -54,7 +54,7 @@ fun Long.asInstant(): Instant { return try { Instant.EPOCH.plus(this, ChronoUnit.MICROS) } catch (e: ArithmeticException ) { - throw Exception("${this} is too big to be converted to Instant", e) + throw Exception("$this is too big to be converted to Instant", e) } } diff --git a/common/src/test/kotlin/CryptoUtilTest.kt b/common/src/test/kotlin/CryptoUtilTest.kt @@ -114,8 +114,7 @@ class CryptoUtilTest { @Test fun base32Test() { - val validKey = "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0" - val enc = validKey + val enc = "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0" val obj = Base32Crockford.decode(enc) assertTrue(obj.size == 32) val roundTrip = Base32Crockford.encode(obj) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt @@ -105,7 +105,7 @@ suspend fun ingestOutgoingPayment( else logger.warn("$payment recovered") } else { - logger.debug("$payment already seen") + logger.debug("{} already seen", payment) } } @@ -136,7 +136,7 @@ suspend fun ingestIncomingPayment( if (result.new) { logger.info("$payment bounced in '${result.bounceId}': $msg") } else { - logger.debug("$payment already seen and bounced in '${result.bounceId}': $msg") + logger.debug("{} already seen and bounced in '{}': {}", payment, result.bounceId, msg) } } AccountType.normal -> { @@ -144,21 +144,20 @@ suspend fun ingestIncomingPayment( if (res.new) { logger.info("$payment") } else { - logger.debug("$payment already seen") + logger.debug("{} already seen", payment) } } } } runCatching { parseIncomingTxMetadata(payment.wireTransferSubject) }.fold( onSuccess = { reservePub -> - val res = db.payment.registerTalerableIncoming(payment, reservePub) - when (res) { + when (val res = db.payment.registerTalerableIncoming(payment, reservePub)) { IncomingRegistrationResult.ReservePubReuse -> bounce("reverse pub reuse") is IncomingRegistrationResult.Success -> { if (res.new) { logger.info("$payment") } else { - logger.debug("$payment already seen") + logger.debug("{} already seen", payment) } } } @@ -178,7 +177,7 @@ private suspend fun ingestDocument( try { parseTx(xml, cfg.currency, cfg.dialect).forEach { if (cfg.fetch.ignoreBefore != null && it.executionTime < cfg.fetch.ignoreBefore) { - logger.debug("IGNORE $it") + logger.debug("IGNORE {}", it) } else { when (it) { is IncomingPayment -> ingestIncomingPayment(db, it, cfg.accountType) @@ -199,7 +198,7 @@ private suspend fun ingestDocument( for (ack in acks) { when (ack.actionType) { HacAction.ORDER_HAC_FINAL_POS -> { - logger.debug("$ack") + logger.debug("{}", ack) db.initiated.logSuccess(ack.orderId!!)?.let { requestUID -> logger.info("Payment '$requestUID' accepted at ${ack.timestamp.fmtDateTime()}") } @@ -276,6 +275,7 @@ private suspend fun ingestDocuments( * This parameter makes the last incoming transaction timestamp in * the database IGNORED. Only useful when running in --transient * mode to download past documents / debug. + * TODO update doc */ private suspend fun fetchDocuments( db: Database, @@ -363,7 +363,7 @@ class EbicsFetch: CliktCommand("Fetches EBICS files") { ).flag(default = false) private val documents: Set<EbicsDocument> by argument( help = "Which documents should be fetched? If none are specified, all supported documents will be fetched", - helpTags = EbicsDocument.entries.map { Pair(it.name, it.shortDescription()) }.toMap() + helpTags = EbicsDocument.entries.associate { Pair(it.name, it.shortDescription()) } ).enum<EbicsDocument>().multiple().unique() private val pinnedStart by option( help = "Constant YYYY-MM-DD date for the earliest document" + diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt @@ -93,6 +93,7 @@ private fun askUserToAcceptKeys(bankKeys: BankPublicKeysFile): Boolean { * @param orderType INI or HIA. * @param autoAcceptBankKeys only given in case of HPB. Expresses * the --auto-accept-key CLI flag. + * TODO update doc */ suspend fun doKeysRequestAndUpdateState( cfg: NexusConfig, @@ -210,7 +211,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { // Check EBICS 3 support val versions = HEV(client, cfg) - logger.debug("HEV: $versions") + logger.debug("HEV: {}", versions) if (!versions.contains(VersionNumber(3.0f, "H005")) && !versions.contains(VersionNumber(3.02f, "H005"))) { throw Exception("EBICS 3 is not supported by your bank") } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt @@ -107,6 +107,7 @@ private suspend fun submitInitiatedPayment( * @param httpClient HTTP connection handle. * @param clientKeys subscriber private keys. * @param bankKeys bank public keys. + * TODO update doc */ private suspend fun submitBatch( ctx: SubmissionContext, @@ -162,7 +163,7 @@ class EbicsSubmit : CliktCommand("Submits any initiated payment found in the dat logger.info("Transient mode: submitting what found and returning.") Duration.ZERO } else { - var frequency = cfg.config.requireDuration("nexus-submit", "frequency") + val frequency = cfg.config.requireDuration("nexus-submit", "frequency") val raw = cfg.config.requireString("nexus-submit", "frequency") logger.debug("Running with a frequency of $raw") if (frequency == Duration.ZERO) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt @@ -18,12 +18,15 @@ */ package tech.libeufin.nexus -import tech.libeufin.common.* +import tech.libeufin.common.IbanPayto +import tech.libeufin.common.TalerAmount import tech.libeufin.nexus.ebics.Dialect import java.io.InputStream -import java.net.URLEncoder -import java.time.* -import java.time.format.* +import java.time.Instant +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter /** * Gets the amount number, also converting it from the @@ -51,7 +54,7 @@ fun getAmountNoCurrency(amount: TalerAmount): String { * Although this is NOT the pain.001 creation timestamp, it * will help making idempotent requests where one MsgId is * always associated with one, and only one creation timestamp. - * @param debtorAccount [IbanPayto] bank account information of the EBICS subscriber that + * @param debitAccount [IbanPayto] bank account information of the EBICS subscriber that * sends this request. It's expected to contain IBAN, BIC, and NAME. * @param amount amount to pay. The caller is responsible for sanity-checking this * value to match the bank expectation. For example, that the decimal @@ -59,6 +62,7 @@ fun getAmountNoCurrency(amount: TalerAmount): String { * @param wireTransferSubject wire transfer subject. * @param creditAccount payment receiver in [IbanPayto]. It should contain IBAN and NAME. * @return raw pain.001 XML, or throws if the debtor BIC is not found. + * TODO update doc */ fun createPain001( requestUid: String, @@ -144,7 +148,7 @@ data class CustomerAck( val timestamp: Instant ) { fun msg(): String = buildString { - append("${actionType}") + append("$actionType") if (code != null) append(" ${code.isoCode}") append(" - '${actionType.description}'") if (code != null) append(" '${code.description}'") @@ -152,8 +156,8 @@ data class CustomerAck( } override fun toString(): String = buildString { - append("${timestamp.fmtDateTime()}") - if (orderId != null) append(" ${orderId}") + append(timestamp.fmtDateTime()) + if (orderId != null) append(" $orderId") append(" ${msg()}") } } @@ -194,7 +198,7 @@ data class PaymentStatus( val reasons: List<Reason> ) { fun id(): String { - var str = "$msgId" + var str = msgId if (paymentId != null) str += ".$paymentId" if (txId != null) str += ".$txId" return str @@ -586,7 +590,7 @@ fun parseTx( } }} - return txsInfo.mapNotNull { it -> + return txsInfo.mapNotNull { try { parseTxLogic(it) } catch (e: TxErr) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -26,34 +26,34 @@ package tech.libeufin.nexus import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.ProgramResult import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.arguments.* -import com.github.ajalt.clikt.parameters.groups.* +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.arguments.convert +import com.github.ajalt.clikt.parameters.groups.provideDelegate import com.github.ajalt.clikt.parameters.options.* -import com.github.ajalt.clikt.parameters.types.* -import com.github.ajalt.clikt.core.ProgramResult +import com.github.ajalt.clikt.parameters.types.enum import io.ktor.client.* import io.ktor.client.plugins.* -import kotlinx.serialization.json.Json -import kotlinx.serialization.Serializable -import kotlin.io.path.* -import kotlin.math.max import io.ktor.server.application.* import org.slf4j.Logger -import org.slf4j.event.Level import org.slf4j.LoggerFactory import tech.libeufin.common.* -import tech.libeufin.common.api.* -import tech.libeufin.common.crypto.* +import tech.libeufin.common.api.serve +import tech.libeufin.common.api.talerApi import tech.libeufin.common.db.DatabaseConfig -import tech.libeufin.nexus.api.* -import tech.libeufin.nexus.ebics.* +import tech.libeufin.nexus.api.revenueApi +import tech.libeufin.nexus.api.wireGatewayApi import tech.libeufin.nexus.db.Database import tech.libeufin.nexus.db.InitiatedPayment +import tech.libeufin.nexus.ebics.EbicsOrder +import tech.libeufin.nexus.ebics.ebicsDownload import java.nio.file.Path -import java.time.* +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId import java.time.format.DateTimeFormatter -import javax.crypto.EncryptedPrivateKeyInfo +import kotlin.math.max internal val logger: Logger = LoggerFactory.getLogger("libeufin-nexus") @@ -314,7 +314,7 @@ class ListCmd: CliktCommand("List nexus transactions", name = "list") { private val common by CommonOption() private val kind: ListKind by argument( help = "Which list to print", - helpTags = ListKind.entries.map { Pair(it.name, it.description()) }.toMap() + helpTags = ListKind.entries.associate { Pair(it.name, it.description()) } ).enum<ListKind>() override fun run() = cliCmd(logger, common.log) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/XmlCombinators.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/XmlCombinators.kt @@ -45,7 +45,7 @@ interface XmlBuilder { fun toBytes(root: String, f: XmlBuilder.() -> Unit): ByteArray { val factory = XMLOutputFactory.newFactory() val stream = StringWriter() - var writer = factory.createXMLStreamWriter(stream) + val writer = factory.createXMLStreamWriter(stream) /** * NOTE: commenting out because it wasn't obvious how to output the * "standalone = 'yes' directive". Manual forge was therefore preferred. @@ -66,7 +66,7 @@ interface XmlBuilder { doc.xmlVersion = "1.0" doc.xmlStandalone = true val root = doc.createElementNS(schema, root) - doc.appendChild(root); + doc.appendChild(root) XmlDOMBuilder(doc, schema, root).f() doc.normalize() return doc @@ -119,7 +119,7 @@ private class XmlDOMBuilder(private val doc: Document, private val schema: Strin } override fun text(content: String) { - node.appendChild(doc.createTextNode(content)); + node.appendChild(doc.createTextNode(content)) } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt @@ -26,11 +26,13 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.util.pipeline.* import tech.libeufin.common.* -import tech.libeufin.nexus.* -import tech.libeufin.nexus.db.* -import tech.libeufin.nexus.db.PaymentDAO.* -import tech.libeufin.nexus.db.InitiatedDAO.* -import tech.libeufin.nexus.db.ExchangeDAO.* +import tech.libeufin.nexus.IncomingPayment +import tech.libeufin.nexus.NexusConfig +import tech.libeufin.nexus.checkCurrency +import tech.libeufin.nexus.db.Database +import tech.libeufin.nexus.db.ExchangeDAO +import tech.libeufin.nexus.db.ExchangeDAO.TransferResult +import tech.libeufin.nexus.db.PaymentDAO.IncomingRegistrationResult import java.time.Instant diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/helpers.kt @@ -19,15 +19,13 @@ package tech.libeufin.nexus.api -import tech.libeufin.nexus.* -import tech.libeufin.common.* -import tech.libeufin.common.api.* import io.ktor.http.* -import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.util.* -import io.ktor.util.pipeline.* +import tech.libeufin.common.* +import tech.libeufin.common.api.intercept +import tech.libeufin.nexus.ApiConfig +import tech.libeufin.nexus.AuthMethod /** Apply api configuration for a route: conditional access and authentication */ fun Route.authApi(cfg: ApiConfig?, callback: Route.() -> Unit): Route = diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt @@ -18,12 +18,13 @@ */ package tech.libeufin.nexus.db -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* -import org.slf4j.Logger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import org.slf4j.LoggerFactory import tech.libeufin.common.TalerAmount -import tech.libeufin.common.db.* +import tech.libeufin.common.db.DatabaseConfig +import tech.libeufin.common.db.DbPool +import tech.libeufin.common.db.watchNotifications import java.time.Instant /** diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt @@ -19,9 +19,11 @@ package tech.libeufin.nexus.db -import tech.libeufin.common.db.* import tech.libeufin.common.* -import java.sql.ResultSet +import tech.libeufin.common.db.getAmount +import tech.libeufin.common.db.getTalerTimestamp +import tech.libeufin.common.db.one +import tech.libeufin.common.db.poolHistoryGlobal import java.time.Instant /** Data access logic for exchange specific logic */ @@ -51,7 +53,7 @@ class ExchangeDAO(private val db: Database) { ) } - /** Query [exchangeId] history of taler outgoing transactions */ + /** Query exchange history of taler outgoing transactions */ suspend fun outgoingHistory( params: HistoryParams ): List<OutgoingTransaction> diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt @@ -21,10 +21,9 @@ package tech.libeufin.nexus.db import tech.libeufin.common.asInstant import tech.libeufin.common.db.all -import tech.libeufin.common.db.executeUpdateViolation -import tech.libeufin.common.db.oneUniqueViolation import tech.libeufin.common.db.getAmount import tech.libeufin.common.db.oneOrNull +import tech.libeufin.common.db.oneUniqueViolation import tech.libeufin.common.micros import java.sql.ResultSet import java.time.Instant @@ -54,7 +53,7 @@ class InitiatedDAO(private val db: Database) { stmt.setLong(1, paymentData.amount.value) stmt.setInt(2, paymentData.amount.frac) stmt.setString(3, paymentData.wireTransferSubject) - stmt.setString(4, paymentData.creditPaytoUri.toString()) + stmt.setString(4, paymentData.creditPaytoUri) stmt.setLong(5, paymentData.initiationTime.micros()) stmt.setString(6, paymentData.requestUid) stmt.oneUniqueViolation(PaymentInitiationResult.RequestUidReuse) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt @@ -86,7 +86,7 @@ object EbicsAdministrative { OrderInfo( one("AdminOrderType").text(), opt("Service") { - var params = StringBuilder() + val params = StringBuilder() opt("ServiceName")?.run { params.append(" ${text()}") } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt @@ -340,7 +340,7 @@ class EbicsBTS( } } -data class BTSResponse( +class BTSResponse( val transactionID: String?, val orderID: String?, val dataEncryptionInfo: DataEncryptionInfo?, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -99,9 +99,10 @@ sealed class EbicsError(msg: String, cause: Throwable? = null): Exception(msg, c /** * POSTs the EBICS message to the bank. * - * @param URL where the bank serves EBICS requests. + * @param bankUrl where the bank serves EBICS requests. * @param msg EBICS message as raw bytes. * @return the raw bank response. + * TODO update doc */ suspend fun HttpClient.postToBank(bankUrl: String, msg: ByteArray, phase: String): Document { val res = try { @@ -158,6 +159,7 @@ suspend fun EbicsBTS.postBTS( * @param processing processing lambda receiving EBICS files as a byte stream if the transaction was not empty. * @return T if the transaction was successful. If the failure is at the EBICS * level EbicsSideException is thrown else ités the exception of the processing lambda. + * TODO update doc */ suspend fun ebicsDownload( client: HttpClient, @@ -271,8 +273,8 @@ suspend fun HEV( * @param clientKeys client keys. * @param bankKeys bank keys. * @param payload business payload to send to the bank, typically ISO20022. - * @param isEbics3 true if the payload travels on EBICS 3. * @return [PreparedUploadData] + * TODO update doc */ fun prepareUploadPayload( cfg: NexusConfig, @@ -325,7 +327,7 @@ fun prepareUploadPayload( * @param clientKeys client EBICS private keys. * @param bankKeys bank EBICS public keys. * @param payload binary business paylaod. - * @return [EbicsResponseContent] or throws [EbicsUploadException] + * TODO update doc */ suspend fun doEbicsUpload( client: HttpClient, @@ -386,7 +388,7 @@ class EbicsResponse<T>( ) { /** Checks that return codes are both EBICS_OK or throw an exception */ fun okOrFail(phase: String): T { - logger.debug("$phase return codes: $technicalCode & $bankCode") + logger.debug("{} return codes: {} & {}", phase, technicalCode, bankCode) require(technicalCode.kind() != EbicsReturnCode.Kind.Error) { "$phase has technical error: $technicalCode" } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt @@ -74,7 +74,7 @@ class EbicsKeyMng( val doc = XmlBuilder.toDom(name, "urn:org:ebics:$schema") { attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:$schema") attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#") - attr("Version", "$schema") + attr("Version", schema) attr("Revision", "1") el("header") { attr("authenticate", "true") diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt @@ -18,19 +18,18 @@ */ import org.junit.Test -import tech.libeufin.common.* -import tech.libeufin.nexus.* -import tech.libeufin.nexus.ebics.* -import tech.libeufin.nexus.TxNotification.* +import tech.libeufin.common.TalerAmount +import tech.libeufin.nexus.IncomingPayment +import tech.libeufin.nexus.OutgoingPayment +import tech.libeufin.nexus.TxNotification.Reversal +import tech.libeufin.nexus.ebics.Dialect +import tech.libeufin.nexus.parseTx import java.nio.file.Files -import java.time.LocalDate import java.time.Instant +import java.time.LocalDate import java.time.ZoneOffset import java.time.format.DateTimeFormatter import kotlin.io.path.Path -import kotlin.io.path.exists -import kotlin.io.path.isDirectory -import kotlin.io.path.listDirectoryEntries import kotlin.test.assertEquals private fun instant(date: String): Instant = diff --git a/nexus/src/test/kotlin/RevenueApiTest.kt b/nexus/src/test/kotlin/RevenueApiTest.kt @@ -19,8 +19,9 @@ import io.ktor.http.* import org.junit.Test -import tech.libeufin.common.* -import tech.libeufin.nexus.* +import tech.libeufin.common.RevenueIncomingHistory +import tech.libeufin.common.assertNotImplemented +import tech.libeufin.common.assertOk class RevenueApiTest { // GET /taler-revenue/config diff --git a/nexus/src/test/kotlin/WireGatewayApiTest.kt b/nexus/src/test/kotlin/WireGatewayApiTest.kt @@ -17,14 +17,11 @@ * <http://www.gnu.org/licenses/> */ -import io.ktor.client.* import io.ktor.client.request.* -import io.ktor.client.statement.* import io.ktor.http.* -import io.ktor.server.testing.* import org.junit.Test import tech.libeufin.common.* -import tech.libeufin.nexus.* +import tech.libeufin.nexus.ingestOutgoingPayment class WireGatewayApiTest { // GET /taler-wire-gateway/config diff --git a/nexus/src/test/kotlin/helpers.kt b/nexus/src/test/kotlin/helpers.kt @@ -21,11 +21,11 @@ import io.ktor.client.* import io.ktor.client.engine.mock.* import io.ktor.client.request.* import io.ktor.client.statement.* -import io.ktor.http.* import io.ktor.server.testing.* import kotlinx.coroutines.runBlocking import tech.libeufin.common.* -import tech.libeufin.common.db.* +import tech.libeufin.common.db.dbInit +import tech.libeufin.common.db.pgDataSource import tech.libeufin.nexus.* import tech.libeufin.nexus.db.Database import tech.libeufin.nexus.db.InitiatedPayment @@ -66,7 +66,7 @@ fun serverSetup( } } -val grothoffPayto = "payto://iban/CH4189144589712575493?receiver-name=Grothoff%20Hans" +const val grothoffPayto = "payto://iban/CH4189144589712575493?receiver-name=Grothoff%20Hans" val clientKeys = generateNewKeys() diff --git a/nexus/src/test/kotlin/routines.kt b/nexus/src/test/kotlin/routines.kt @@ -18,16 +18,12 @@ */ import io.ktor.client.request.* -import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.server.testing.* -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.serialization.json.JsonObject -import tech.libeufin.common.* -import tech.libeufin.common.test.* -import kotlin.test.assertEquals +import tech.libeufin.common.TalerErrorCode +import tech.libeufin.common.assertBadRequest +import tech.libeufin.common.assertUnauthorized +import tech.libeufin.common.test.abstractHistoryRoutine // Test endpoint is correctly authenticated diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt @@ -133,7 +133,6 @@ class Cli : CliktCommand("Run integration tests on banks provider") { val dummyPayto = dummyPaytos[currency] ?: throw Exception("Missing dummy payto for $currency") val payto = benchCfg.payto[currency] ?: dummyPayto - ?: throw Exception("Missing test payto for $currency") val recoverDoc = "report statement notification" runBlocking { @@ -141,7 +140,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") { assert(nexusCmd.run("dbinit $flags")) - val cmds = buildMap<String, suspend () -> Unit> { + val cmds = buildMap { fun put(name: String, args: String) { put(name, suspend { nexusCmd.run(args) @@ -200,7 +199,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") { } put("tx-bad-name", suspend { val badPayto = URLBuilder().takeFrom(payto) - badPayto.parameters.set("receiver-name", "John Smith") + badPayto.parameters["receiver-name"] = "John Smith" step("Submit new transaction with a bad name") nexusCmd.run("initiate-payment $flags \"$badPayto&amount=$currency:1.1&message=This%20should%20fail%20because%20bad%20name\"") nexusCmd.run("ebics-submit $ebicsFlags") @@ -215,7 +214,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") { } while (true) { var clientKeys = loadClientKeys(clientKeysPath) - var bankKeys = loadBankKeys(bankKeysPath) + val bankKeys = loadBankKeys(bankKeysPath) if (!kind.test && clientKeys == null) { throw Exception("Clients keys are required to run netzbon tests") } else if (clientKeys == null || !clientKeys.submitted_ini || !clientKeys.submitted_hia || bankKeys == null || !bankKeys.accepted) {