Integrate Ktor HTTP clients and repositories for Veranstalter and Turnier features:

- Add `ApiRoutes` for central backend routing configuration.
- Implement `DefaultVeranstalterRepository` and `DefaultTurnierRepository` with Ktor clients.
- Add domain models (`Turnier`, `Bewerb`, `Abteilung`, `Veranstalter`) and respective repository interfaces.
- Replace fake VeranstalterRepository with real implementation.
- Update DI with `veranstalterModule` and HTTP client injection.
- Simplify TokenProvider and update HttpClient setup (timeouts, retries, logging).
- Mark roadmap tasks B-2 as partially complete.
This commit is contained in:
2026-04-03 01:09:30 +02:00
parent a5c1fb5bae
commit f82dbd64a5
17 changed files with 314 additions and 67 deletions
@@ -0,0 +1,21 @@
package at.mocode.frontend.core.network
/**
* Zentrale API-Routen-Konfiguration. Versionierung erfolgt über den Prefix /api/v3.
*/
object ApiRoutes {
const val API_PREFIX = "/api/v3"
object Veranstalter {
const val ROOT = "$API_PREFIX/veranstalter"
}
object Turniere {
const val ROOT = "$API_PREFIX/turniere"
fun bewerbe(turnierId: Long): String = "$ROOT/$turnierId/bewerbe"
}
object Bewerbe {
fun abteilungen(bewerbId: Long): String = "$API_PREFIX/bewerbe/$bewerbId/abteilungen"
}
}
@@ -0,0 +1 @@
// Removed: superseded by NetworkModule.kt
@@ -13,9 +13,7 @@ import org.koin.dsl.module
/**
* Schnittstelle zur Token-Bereitstellung entkoppelt core-network von core-auth.
*/
interface TokenProvider {
fun getAccessToken(): String?
}
interface TokenProvider { fun getAccessToken(): String? }
/**
* Koin-Modul mit zwei HttpClient-Instanzen:
@@ -28,16 +26,15 @@ val networkModule = module {
single(named("baseHttpClient")) {
HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true })
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
println("[baseClient] $message")
json(
Json {
ignoreUnknownKeys = true
explicitNulls = false
coerceInputValues = true
}
}
level = LogLevel.INFO
)
}
install(Logging) { logger = Logger.SIMPLE; level = LogLevel.NONE }
}
}
@@ -47,29 +44,29 @@ val networkModule = module {
HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true })
json(
Json {
ignoreUnknownKeys = true
explicitNulls = false
coerceInputValues = true
}
)
}
install(HttpTimeout) {
// Defaults laut Spezifikation (Prod)
connectTimeoutMillis = 5_000
requestTimeoutMillis = 15_000
connectTimeoutMillis = 10_000
socketTimeoutMillis = 15_000
socketTimeoutMillis = 30_000
}
install(HttpRequestRetry) {
maxRetries = 3
maxRetries = 2
retryIf { _, response -> response.status.value.let { it == 0 || it >= 500 } }
exponentialDelay()
}
defaultRequest {
url(NetworkConfig.baseUrl.trimEnd('/'))
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
println("[apiClient] $message")
}
}
level = LogLevel.INFO
}
install(Logging) { logger = Logger.SIMPLE; level = LogLevel.NONE }
}.also { client ->
// Bearer-Token pro Request dynamisch injizieren (lazy, damit kein Circular-Dependency)
client.plugin(HttpSend).intercept { request ->