fixing gradle build
This commit is contained in:
parent
df5919fac8
commit
4ea084bd1d
|
|
@ -5,14 +5,18 @@ plugins {
|
|||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-Xopt-in=kotlin.time.ExperimentalTime")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass dieses Modul Zugriff auf die im zentralen Katalog
|
||||
// definierten Bibliotheken hat.
|
||||
api(projects.platform.platformDependencies)
|
||||
|
||||
// Kern-Abhängigkeiten für das Domänen-Modell.
|
||||
// `api` wird verwendet, damit Services, die `core-domain` einbinden,
|
||||
// diese Typen ebenfalls direkt nutzen können.
|
||||
// Kern-Abhängigkeiten für das Domänen-Modul.
|
||||
api(libs.uuid)
|
||||
api(libs.kotlinx.serialization.json)
|
||||
api(libs.kotlinx.datetime)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ package at.mocode.core.domain.event
|
|||
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import java.util.UUID
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Base interface for all domain events in the system.
|
||||
|
|
@ -12,9 +13,9 @@ import kotlinx.datetime.Instant
|
|||
interface DomainEvent {
|
||||
val eventId: Uuid
|
||||
val aggregateId: Uuid
|
||||
val eventType: String
|
||||
val eventType: java.time.Instant
|
||||
val timestamp: Instant
|
||||
val version: Long
|
||||
val version: Int
|
||||
|
||||
// OPTIMIZED: Added correlation and causation IDs for distributed tracing.
|
||||
/**
|
||||
|
|
@ -33,8 +34,8 @@ interface DomainEvent {
|
|||
*/
|
||||
abstract class BaseDomainEvent(
|
||||
override val aggregateId: Uuid,
|
||||
override val eventType: String,
|
||||
override val version: Long,
|
||||
override val eventType: java.time.Instant,
|
||||
override val version: Int,
|
||||
override val eventId: Uuid = uuid4(),
|
||||
override val timestamp: Instant = Clock.System.now(),
|
||||
override val correlationId: Uuid? = null,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package at.mocode.core.domain.model
|
|||
import at.mocode.core.domain.serialization.KotlinInstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
|
|
@ -49,7 +49,8 @@ data class ErrorDto(
|
|||
data class ApiResponse<T>(
|
||||
val data: T?,
|
||||
val success: Boolean,
|
||||
val errors: List<ErrorDto> = emptyList(), // OPTIMIZED: Using structured ErrorDto
|
||||
val errors: List<ErrorDto> = emptyList(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val timestamp: Instant = Clock.System.now()
|
||||
) {
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package at.mocode.core.domain.serialization
|
|||
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuidFrom
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.LocalTime
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ plugins {
|
|||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Abhängigkeit zum platform-Modul für zentrale Versionsverwaltung
|
||||
api(projects.platform.platformDependencies)
|
||||
|
|
@ -31,4 +37,5 @@ dependencies {
|
|||
// Testing
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.bundles.testing.jvm)
|
||||
testImplementation(libs.kotlin.test)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,24 @@
|
|||
package at.mocode.core.utils.config
|
||||
|
||||
import at.mocode.core.utils.database.DatabaseConfig
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Zentrale Konfigurations-Klasse für die Anwendung.
|
||||
* Hält alle Konfigurationswerte, die beim Start des Service explizit geladen werden.
|
||||
* Zentrale, unveränderliche Konfigurations-Klasse für die Anwendung.
|
||||
* Hält alle Konfigurationswerte, die beim Start eines Service geladen werden.
|
||||
*/
|
||||
class AppConfig(
|
||||
val environment: AppEnvironment,
|
||||
val appInfo: AppInfoConfig,
|
||||
val server: ServerConfig,
|
||||
val database: DatabaseConfig,
|
||||
val serviceDiscovery: ServiceDiscoveryConfig,
|
||||
val security: SecurityConfig,
|
||||
val logging: LoggingConfig,
|
||||
val rateLimit: RateLimitConfig,
|
||||
val serviceDiscovery: ServiceDiscoveryConfig,
|
||||
val database: DatabaseConfig
|
||||
val rateLimit: RateLimitConfig
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Factory-Methode, die eine AppConfig-Instanz durch das Laden von
|
||||
* .properties-Dateien und Umgebungsvariablen erstellt.
|
||||
* Dies ist der zentrale Einstiegspunkt, um die Konfiguration zu laden.
|
||||
*/
|
||||
fun load(): AppConfig {
|
||||
val environment = AppEnvironment.current()
|
||||
val props = loadProperties(environment)
|
||||
|
|
@ -32,191 +27,148 @@ class AppConfig(
|
|||
environment = environment,
|
||||
appInfo = AppInfoConfig.fromProperties(props),
|
||||
server = ServerConfig.fromProperties(props),
|
||||
database = DatabaseConfig.fromProperties(props),
|
||||
serviceDiscovery = ServiceDiscoveryConfig.fromProperties(props),
|
||||
security = SecurityConfig.fromProperties(props),
|
||||
logging = LoggingConfig.fromProperties(props, environment),
|
||||
rateLimit = RateLimitConfig.fromProperties(props),
|
||||
serviceDiscovery = ServiceDiscoveryConfig.fromProperties(props),
|
||||
database = DatabaseConfig.fromProperties(props)
|
||||
rateLimit = RateLimitConfig.fromProperties(props)
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadProperties(environment: AppEnvironment): Properties {
|
||||
val props = Properties()
|
||||
|
||||
// Lade Basis-Properties
|
||||
loadPropertiesFile("application.properties", props)
|
||||
|
||||
// Lade umgebungsspezifische Properties
|
||||
val envFile = "application-${environment.name.lowercase()}.properties"
|
||||
loadPropertiesFile(envFile, props)
|
||||
|
||||
return props
|
||||
}
|
||||
|
||||
private fun loadPropertiesFile(filename: String, props: Properties) {
|
||||
val resourceStream = javaClass.classLoader.getResourceAsStream(filename)
|
||||
val resourceStream = AppConfig::class.java.classLoader.getResourceAsStream(filename)
|
||||
if (resourceStream != null) {
|
||||
props.load(resourceStream)
|
||||
resourceStream.close()
|
||||
} else {
|
||||
val file = File("config/$filename")
|
||||
if (file.exists()) {
|
||||
file.inputStream().use { props.load(it) }
|
||||
}
|
||||
resourceStream.use { props.load(it) }
|
||||
return
|
||||
}
|
||||
val file = File("config/$filename")
|
||||
if (file.exists()) {
|
||||
file.inputStream().use { props.load(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für Anwendungsinformationen.
|
||||
*/
|
||||
data class AppInfoConfig(
|
||||
val name: String,
|
||||
val version: String,
|
||||
val description: String
|
||||
) {
|
||||
data class AppInfoConfig(val name: String, val version: String, val description: String) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties): AppInfoConfig {
|
||||
return AppInfoConfig(
|
||||
name = props.getProperty("app.name", "Meldestelle"),
|
||||
version = props.getProperty("app.version", "1.0.0"),
|
||||
description = props.getProperty("app.description", "Pferdesport Meldestelle System")
|
||||
)
|
||||
}
|
||||
fun fromProperties(props: Properties) = AppInfoConfig(
|
||||
name = props.getProperty("app.name", "Meldestelle"),
|
||||
version = props.getProperty("app.version", "1.0.0"),
|
||||
description = props.getProperty("app.description", "Pferdesport Meldestelle System")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für den Server.
|
||||
*/
|
||||
data class ServerConfig(
|
||||
val port: Int,
|
||||
val host: String,
|
||||
val advertisedHost: String,
|
||||
val workers: Int,
|
||||
val cors: CorsConfig
|
||||
) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties): ServerConfig {
|
||||
val corsConfig = CorsConfig(
|
||||
enabled = props.getProperty("server.cors.enabled")?.toBoolean() ?: true,
|
||||
allowedOrigins = props.getProperty("server.cors.allowedOrigins")?.split(",")?.map { it.trim() }
|
||||
?: listOf("*")
|
||||
)
|
||||
val defaultHost = try { InetAddress.getLocalHost().hostAddress } catch (_: Exception) { "127.0.0.1" }
|
||||
return ServerConfig(
|
||||
port = System.getenv("API_PORT")?.toIntOrNull() ?: props.getProperty("server.port", "8081").toInt(),
|
||||
host = System.getenv("API_HOST") ?: props.getProperty("server.host", "0.0.0.0"),
|
||||
workers = props.getProperty("server.workers")?.toIntOrNull() ?: Runtime.getRuntime()
|
||||
.availableProcessors(),
|
||||
cors = corsConfig
|
||||
port = props.getIntProperty("server.port", "API_PORT", 8081),
|
||||
host = props.getStringProperty("server.host", "API_HOST", "0.0.0.0"),
|
||||
advertisedHost = props.getStringProperty("server.advertisedHost", "API_HOST_ADVERTISED", defaultHost),
|
||||
workers = props.getIntProperty("server.workers", "API_WORKERS", Runtime.getRuntime().availableProcessors()),
|
||||
cors = CorsConfig.fromProperties(props)
|
||||
)
|
||||
}
|
||||
}
|
||||
data class CorsConfig(val enabled: Boolean, val allowedOrigins: List<String>) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties) = CorsConfig(
|
||||
enabled = props.getBooleanProperty("server.cors.enabled", "API_CORS_ENABLED", true),
|
||||
allowedOrigins = props.getProperty("server.cors.allowedOrigins")?.split(",")?.map { it.trim() } ?: listOf("*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CorsConfig(
|
||||
val enabled: Boolean,
|
||||
val allowedOrigins: List<String>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für die Sicherheit.
|
||||
*/
|
||||
data class SecurityConfig(
|
||||
val jwt: JwtConfig,
|
||||
val apiKey: String?
|
||||
data class DatabaseConfig(
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val driverClassName: String,
|
||||
val maxPoolSize: Int,
|
||||
val minPoolSize: Int,
|
||||
val autoMigrate: Boolean
|
||||
) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties): SecurityConfig {
|
||||
val jwtConfig = JwtConfig(
|
||||
secret = System.getenv("JWT_SECRET") ?: props.getProperty(
|
||||
"security.jwt.secret",
|
||||
"default-jwt-secret-key-please-change-in-production"
|
||||
),
|
||||
issuer = System.getenv("JWT_ISSUER") ?: props.getProperty("security.jwt.issuer", "meldestelle-api"),
|
||||
audience = System.getenv("JWT_AUDIENCE") ?: props.getProperty(
|
||||
"security.jwt.audience",
|
||||
"meldestelle-clients"
|
||||
),
|
||||
realm = System.getenv("JWT_REALM") ?: props.getProperty("security.jwt.realm", "meldestelle"),
|
||||
expirationInMinutes = props.getProperty("security.jwt.expirationInMinutes")?.toLongOrNull() ?: (60 * 24)
|
||||
)
|
||||
return SecurityConfig(
|
||||
jwt = jwtConfig,
|
||||
apiKey = System.getenv("API_KEY") ?: props.getProperty("security.apiKey")
|
||||
fun fromProperties(props: Properties): DatabaseConfig {
|
||||
val host = props.getStringProperty("database.host", "DB_HOST", "localhost")
|
||||
val port = props.getIntProperty("database.port", "DB_PORT", 5432)
|
||||
val name = props.getStringProperty("database.name", "DB_NAME", "meldestelle_db")
|
||||
return DatabaseConfig(
|
||||
jdbcUrl = "jdbc:postgresql://$host:$port/$name",
|
||||
username = props.getStringProperty("database.username", "DB_USER", "meldestelle_user"),
|
||||
password = props.getStringProperty("database.password", "DB_PASSWORD", "secure_password_change_me"),
|
||||
driverClassName = "org.postgresql.Driver",
|
||||
maxPoolSize = props.getIntProperty("database.maxPoolSize", "DB_MAX_POOL_SIZE", 10),
|
||||
minPoolSize = props.getIntProperty("database.minPoolSize", "DB_MIN_POOL_SIZE", 5),
|
||||
autoMigrate = props.getBooleanProperty("database.autoMigrate", "DB_AUTO_MIGRATE", true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class JwtConfig(
|
||||
val secret: String,
|
||||
val issuer: String,
|
||||
val audience: String,
|
||||
val realm: String,
|
||||
val expirationInMinutes: Long
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für das Logging.
|
||||
*/
|
||||
data class LoggingConfig(
|
||||
val level: String,
|
||||
val logRequests: Boolean,
|
||||
val logResponses: Boolean
|
||||
// ... many more detailed properties from your original file
|
||||
) {
|
||||
data class ServiceDiscoveryConfig(val enabled: Boolean, val consulHost: String, val consulPort: Int) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties, env: AppEnvironment): LoggingConfig {
|
||||
return LoggingConfig(
|
||||
level = props.getProperty("logging.level", if (env == AppEnvironment.PRODUCTION) "INFO" else "DEBUG"),
|
||||
logRequests = props.getProperty("logging.requests")?.toBoolean() ?: true,
|
||||
logResponses = props.getProperty("logging.responses")?.toBoolean() ?: (env != AppEnvironment.PRODUCTION)
|
||||
// ... load other properties here
|
||||
)
|
||||
}
|
||||
fun fromProperties(props: Properties) = ServiceDiscoveryConfig(
|
||||
enabled = props.getBooleanProperty("service-discovery.enabled", "CONSUL_ENABLED", true),
|
||||
consulHost = props.getStringProperty("service-discovery.consul.host", "CONSUL_HOST", "consul"),
|
||||
consulPort = props.getIntProperty("service-discovery.consul.port", "CONSUL_PORT", 8500)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für Rate Limiting.
|
||||
*/
|
||||
data class RateLimitConfig(
|
||||
val enabled: Boolean,
|
||||
val globalLimit: Int,
|
||||
val globalPeriodMinutes: Int
|
||||
) {
|
||||
data class SecurityConfig(val jwt: JwtConfig, val apiKey: String?) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties): RateLimitConfig {
|
||||
return RateLimitConfig(
|
||||
enabled = props.getProperty("ratelimit.enabled")?.toBoolean() ?: true,
|
||||
globalLimit = props.getProperty("ratelimit.global.limit")?.toIntOrNull() ?: 100,
|
||||
globalPeriodMinutes = props.getProperty("ratelimit.global.periodMinutes")?.toIntOrNull() ?: 1
|
||||
fun fromProperties(props: Properties) = SecurityConfig(
|
||||
jwt = JwtConfig.fromProperties(props),
|
||||
apiKey = props.getStringProperty("security.apiKey", "API_KEY", "").ifEmpty { null }
|
||||
)
|
||||
}
|
||||
data class JwtConfig(val secret: String, val issuer: String, val audience: String, val realm: String, val expirationInMinutes: Long) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties) = JwtConfig(
|
||||
secret = props.getStringProperty("security.jwt.secret", "JWT_SECRET", "default-secret-please-change-in-production"),
|
||||
issuer = props.getStringProperty("security.jwt.issuer", "JWT_ISSUER", "meldestelle-api"),
|
||||
audience = props.getStringProperty("security.jwt.audience", "JWT_AUDIENCE", "meldestelle-clients"),
|
||||
realm = props.getStringProperty("security.jwt.realm", "JWT_REALM", "meldestelle"),
|
||||
expirationInMinutes = props.getLongProperty("security.jwt.expirationInMinutes", "JWT_EXPIRATION_MINUTES", 60 * 24)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für Service Discovery.
|
||||
*/
|
||||
data class ServiceDiscoveryConfig(
|
||||
val enabled: Boolean,
|
||||
val consulHost: String,
|
||||
val consulPort: Int
|
||||
) {
|
||||
data class LoggingConfig(val level: String, val logRequests: Boolean, val logResponses: Boolean) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties): ServiceDiscoveryConfig {
|
||||
return ServiceDiscoveryConfig(
|
||||
enabled = props.getProperty("service-discovery.enabled")?.toBoolean() ?: true,
|
||||
consulHost = System.getenv("CONSUL_HOST") ?: props.getProperty(
|
||||
"service-discovery.consul.host",
|
||||
"consul"
|
||||
),
|
||||
consulPort = System.getenv("CONSUL_PORT")?.toIntOrNull()
|
||||
?: props.getProperty("service-discovery.consul.port", "8500").toInt()
|
||||
)
|
||||
}
|
||||
fun fromProperties(props: Properties, env: AppEnvironment) = LoggingConfig(
|
||||
level = props.getStringProperty("logging.level", "LOG_LEVEL", if (env.isProduction()) "INFO" else "DEBUG"),
|
||||
logRequests = props.getBooleanProperty("logging.requests", "LOG_REQUESTS", true),
|
||||
logResponses = props.getBooleanProperty("logging.responses", "LOG_RESPONSES", !env.isProduction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class RateLimitConfig(val enabled: Boolean, val globalLimit: Int, val globalPeriodMinutes: Int) {
|
||||
companion object {
|
||||
fun fromProperties(props: Properties) = RateLimitConfig(
|
||||
enabled = props.getBooleanProperty("ratelimit.enabled", "RATE_LIMIT_ENABLED", true),
|
||||
globalLimit = props.getIntProperty("ratelimit.global.limit", "RATE_LIMIT_GLOBAL_LIMIT", 100),
|
||||
globalPeriodMinutes = props.getIntProperty("ratelimit.global.periodMinutes", "RATE_LIMIT_GLOBAL_PERIOD", 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,26 @@
|
|||
package at.mocode.core.utils.config
|
||||
|
||||
/**
|
||||
* Aufzählung der verschiedenen Anwendungsumgebungen.
|
||||
*/
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
enum class AppEnvironment {
|
||||
DEVELOPMENT, // Lokale Entwicklungsumgebung
|
||||
TEST, // Testumgebung (CI/CD, Integrationstests)
|
||||
STAGING, // Vorabproduktionsumgebung
|
||||
PRODUCTION; // Produktionsumgebung
|
||||
DEVELOPMENT,
|
||||
TEST,
|
||||
STAGING,
|
||||
PRODUCTION;
|
||||
|
||||
fun isProduction() = this == PRODUCTION
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Ermittelt die aktuelle Umgebung basierend auf der APP_ENV Umgebungsvariable.
|
||||
*
|
||||
* @return Die aktuelle Umgebung (Standardmäßig DEVELOPMENT wenn nicht definiert)
|
||||
*/
|
||||
private val logger = LoggerFactory.getLogger(AppEnvironment::class.java)
|
||||
|
||||
fun current(): AppEnvironment {
|
||||
val envName = System.getenv("APP_ENV")?.uppercase() ?: "DEVELOPMENT"
|
||||
return try {
|
||||
valueOf(envName)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
println("Warnung: Unbekannte Umgebung '$envName', verwende DEVELOPMENT")
|
||||
logger.warn("Unknown environment '{}', falling back to DEVELOPMENT.", envName)
|
||||
DEVELOPMENT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Entwicklungsumgebung ist.
|
||||
*/
|
||||
fun isDevelopment() = current() == DEVELOPMENT
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Testumgebung ist.
|
||||
*/
|
||||
fun isTest() = current() == TEST
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Staging-Umgebung ist.
|
||||
*/
|
||||
fun isStaging() = current() == STAGING
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Produktionsumgebung ist.
|
||||
*/
|
||||
fun isProduction() = current() == PRODUCTION
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package at.mocode.core.utils.config
|
||||
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Liest eine String-Property, wobei eine Umgebungsvariable Vorrang hat.
|
||||
*
|
||||
* @param key Der Schlüssel in der '.properties-Datei'.
|
||||
* @param envVar Der Name der Umgebungsvariable.
|
||||
* @param default Der Standardwert, falls weder Property noch Env-Var existieren.
|
||||
* @return Der geladene Konfigurationswert.
|
||||
*/
|
||||
fun Properties.getStringProperty(key: String, envVar: String, default: String): String {
|
||||
return System.getenv(envVar) ?: this.getProperty(key, default)
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest eine Integer-Property, wobei eine Umgebungsvariable Vorrang hat.
|
||||
*/
|
||||
fun Properties.getIntProperty(key: String, envVar: String, default: Int): Int {
|
||||
val value = System.getenv(envVar) ?: this.getProperty(key)
|
||||
return value?.toIntOrNull() ?: default
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest eine Boolean-Property, wobei eine Umgebungsvariable Vorrang hat.
|
||||
*/
|
||||
fun Properties.getBooleanProperty(key: String, envVar: String, default: Boolean): Boolean {
|
||||
val value = System.getenv(envVar) ?: this.getProperty(key)
|
||||
return value?.toBoolean() ?: default
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest eine Long-Property, wobei eine Umgebungsvariable Vorrang hat.
|
||||
*/
|
||||
fun Properties.getLongProperty(key: String, envVar: String, default: Long): Long {
|
||||
val value = System.getenv(envVar) ?: this.getProperty(key)
|
||||
return value?.toLongOrNull() ?: default
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package at.mocode.core.utils.database
|
||||
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Konfiguration für die Datenbankverbindung.
|
||||
* Diese Klasse ist ein reiner Datenhalter (Value Object). Die Logik zum Laden
|
||||
* der Werte ist in der companion object Factory-Methode gekapselt.
|
||||
*/
|
||||
data class DatabaseConfig(
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val driverClassName: String = "org.postgresql.Driver",
|
||||
val maxPoolSize: Int = 10,
|
||||
val minPoolSize: Int = 5,
|
||||
val autoMigrate: Boolean = true // Flag to enable/disable Flyway migrations
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Erstellt eine Datenbank-Konfiguration aus Umgebungsvariablen und Properties.
|
||||
* Die Priorität ist: Umgebungsvariablen > Properties > Standardwerte.
|
||||
*/
|
||||
fun fromProperties(props: Properties): DatabaseConfig {
|
||||
val host = System.getenv("DB_HOST") ?: props.getProperty("database.host", "localhost")
|
||||
val port = System.getenv("DB_PORT") ?: props.getProperty("database.port", "5432")
|
||||
val database = System.getenv("DB_NAME") ?: props.getProperty("database.name", "meldestelle_db")
|
||||
val username = System.getenv("DB_USER") ?: props.getProperty("database.username", "meldestelle_user")
|
||||
val password =
|
||||
System.getenv("DB_PASSWORD") ?: props.getProperty("database.password", "secure_password_change_me")
|
||||
|
||||
val maxPoolSize = System.getenv("DB_MAX_POOL_SIZE")?.toIntOrNull()
|
||||
?: props.getProperty("database.maxPoolSize")?.toIntOrNull()
|
||||
?: 10
|
||||
|
||||
val minPoolSize = System.getenv("DB_MIN_POOL_SIZE")?.toIntOrNull()
|
||||
?: props.getProperty("database.minPoolSize")?.toIntOrNull()
|
||||
?: 5
|
||||
|
||||
val autoMigrate = System.getenv("DB_AUTO_MIGRATE")?.toBoolean()
|
||||
?: props.getProperty("database.autoMigrate")?.toBoolean()
|
||||
?: true
|
||||
|
||||
return DatabaseConfig(
|
||||
jdbcUrl = "jdbc:postgresql://$host:$port/$database",
|
||||
username = username,
|
||||
password = password,
|
||||
driverClassName = "org.postgresql.Driver",
|
||||
maxPoolSize = maxPoolSize,
|
||||
minPoolSize = minPoolSize,
|
||||
autoMigrate = autoMigrate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +1,29 @@
|
|||
package at.mocode.core.utils.database
|
||||
|
||||
import at.mocode.core.utils.config.DatabaseConfig
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.flywaydb.core.Flyway
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* Factory-Klasse für die Datenbankverbindung.
|
||||
* Erstellt und konfiguriert eine Datenbankverbindung inklusive Connection Pool
|
||||
* und führt bei der Initialisierung die notwendigen Migrationen aus.
|
||||
*
|
||||
* @property config Die Datenbankkonfiguration, die für diese Instanz verwendet werden soll.
|
||||
*/
|
||||
class DatabaseFactory(private val config: DatabaseConfig) {
|
||||
|
||||
private companion object {
|
||||
private val logger = LoggerFactory.getLogger(DatabaseFactory::class.java)
|
||||
}
|
||||
|
||||
private var dataSource: HikariDataSource? = null
|
||||
private var database: Database? = null
|
||||
|
||||
/**
|
||||
* Initialisiert die Datenbankverbindung. Muss vor der ersten Verwendung aufgerufen werden.
|
||||
* Konfiguriert den Connection Pool und führt Flyway-Migrationen aus.
|
||||
*/
|
||||
fun connect() {
|
||||
if (dataSource != null) {
|
||||
logger.warn("Database already connected. Closing existing connection before creating a new one.")
|
||||
close()
|
||||
}
|
||||
|
||||
logger.info("Initializing database connection to ${config.jdbcUrl}")
|
||||
val hikariConfig = createHikariConfig()
|
||||
val ds = HikariDataSource(hikariConfig)
|
||||
dataSource = ds
|
||||
|
|
@ -38,28 +34,16 @@ class DatabaseFactory(private val config: DatabaseConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schließt die Datenbankverbindung und den Connection Pool.
|
||||
*/
|
||||
fun close() {
|
||||
dataSource?.close()
|
||||
dataSource = null
|
||||
database = null
|
||||
logger.info("Database connection closed.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt eine Datenbankoperation in einer neuen, suspendierenden Transaktion aus.
|
||||
* Dies ist die primäre Methode, um mit der Datenbank zu interagieren.
|
||||
*
|
||||
* @param block Der Code, der in der Transaktion ausgeführt werden soll.
|
||||
* @return Das Ergebnis der Transaktion.
|
||||
*/
|
||||
suspend fun <T> dbQuery(block: suspend () -> T): T {
|
||||
// Wir stellen sicher, dass die dbQuery-Funktion nur auf einer verbundenen Datenbank läuft.
|
||||
if (database == null) {
|
||||
throw IllegalStateException("Database has not been connected. Call connect() first.")
|
||||
}
|
||||
return newSuspendedTransaction(Dispatchers.IO, db = database) {
|
||||
val db = database ?: throw IllegalStateException("Database has not been connected. Call connect() first.")
|
||||
return newSuspendedTransaction(Dispatchers.IO, db = db) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
|
@ -74,29 +58,27 @@ class DatabaseFactory(private val config: DatabaseConfig) {
|
|||
minimumIdle = config.minPoolSize
|
||||
isAutoCommit = false
|
||||
transactionIsolation = "TRANSACTION_READ_COMMITTED"
|
||||
connectionTestQuery = "SELECT 1"
|
||||
validationTimeout = 5000 // 5 seconds
|
||||
connectionTimeout = 30000 // 30 seconds
|
||||
idleTimeout = 600000 // 10 minutes
|
||||
maxLifetime = 1800000 // 30 minutes
|
||||
leakDetectionThreshold = 60000 // 1 minute
|
||||
poolName = "MeldestelleDbPool-${config.jdbcUrl.substringAfterLast('/')}" // Eindeutiger Pool-Name
|
||||
validationTimeout = 5000
|
||||
connectionTimeout = 30000
|
||||
idleTimeout = 600000
|
||||
maxLifetime = 1800000
|
||||
leakDetectionThreshold = 60000
|
||||
poolName = "MeldestelleDbPool"
|
||||
}
|
||||
}
|
||||
|
||||
private fun runFlyway(dataSource: HikariDataSource) {
|
||||
println("Starte Flyway-Migrationen für Schema: ${dataSource.jdbcUrl}")
|
||||
logger.info("Starting Flyway migrations...")
|
||||
try {
|
||||
Flyway.configure()
|
||||
val count = Flyway.configure()
|
||||
.dataSource(dataSource)
|
||||
.locations("classpath:db/migration")
|
||||
.load()
|
||||
.migrate()
|
||||
println("Flyway-Migrationen erfolgreich abgeschlossen.")
|
||||
.migrationsExecuted
|
||||
logger.info("Flyway migrations completed successfully. Applied $count migrations.")
|
||||
} catch (e: Exception) {
|
||||
println("FEHLER: Flyway-Migration fehlgeschlagen! Details: ${e.message}")
|
||||
// Wir werfen den Fehler weiter, damit die Anwendung beim Start fehlschlägt.
|
||||
// Das ist wichtig, um Inkonsistenzen zu vermeiden.
|
||||
logger.error("Flyway migration failed!", e)
|
||||
throw IllegalStateException("Flyway migration failed", e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,31 +3,32 @@ package at.mocode.core.utils.discovery
|
|||
import at.mocode.core.utils.config.AppConfig
|
||||
import com.orbitz.consul.Consul
|
||||
import com.orbitz.consul.model.agent.ImmutableRegistration
|
||||
// KORREKTUR: Expliziter Import für die `Registration`-Klasse, die den `RegCheck` enthält.
|
||||
import com.orbitz.consul.model.agent.Registration
|
||||
import java.net.InetAddress
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Repräsentiert die Registrierung eines einzelnen Service-Exemplars bei Consul.
|
||||
* Diese Klasse kümmert sich um den Lebenszyklus (Registrierung, Deregistrierung).
|
||||
*/
|
||||
class ServiceRegistration internal constructor(
|
||||
private val consul: Consul,
|
||||
private val registration: ImmutableRegistration
|
||||
) {
|
||||
private companion object {
|
||||
private val logger = LoggerFactory.getLogger(ServiceRegistration::class.java)
|
||||
}
|
||||
|
||||
private var isRegistered = false
|
||||
|
||||
fun register() {
|
||||
if (isRegistered) return
|
||||
try {
|
||||
// Der `register`-Aufruf ist korrekt, da das `registration`-Objekt
|
||||
// bereits außerhalb vollständig und korrekt gebaut wurde.
|
||||
consul.agentClient().register(registration)
|
||||
isRegistered = true
|
||||
println("Service '${registration.name()}' mit ID '${registration.id()}' erfolgreich bei Consul registriert.")
|
||||
logger.info(
|
||||
"Service '{}' with ID '{}' successfully registered with Consul.",
|
||||
registration.name(),
|
||||
registration.id()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
println("FEHLER: Service-Registrierung bei Consul fehlgeschlagen: ${e.message}")
|
||||
logger.error("Failed to register service '{}' with Consul.", registration.name(), e)
|
||||
throw IllegalStateException("Could not register service with Consul", e)
|
||||
}
|
||||
}
|
||||
|
|
@ -35,63 +36,65 @@ class ServiceRegistration internal constructor(
|
|||
fun deregister() {
|
||||
if (!isRegistered) return
|
||||
try {
|
||||
// Der `deregister`-Aufruf ist korrekt. Er erwartet die Service-ID als einfachen String.
|
||||
consul.agentClient().deregister(registration.id())
|
||||
isRegistered = false
|
||||
println("Service '${registration.name()}' mit ID '${registration.id()}' erfolgreich bei Consul deregistriert.")
|
||||
logger.info(
|
||||
"Service '{}' with ID '{}' successfully deregistered from Consul.",
|
||||
registration.name(),
|
||||
registration.id()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
println("FEHLER: Service-Deregistrierung bei Consul fehlgeschlagen: ${e.message}")
|
||||
logger.error("Failed to deregister service '{}' from Consul.", registration.id(), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zentraler Registrar, der beim Anwendungsstart Services registriert.
|
||||
* Diese Klasse wird einmalig mit der Gesamt-AppConfig initialisiert.
|
||||
*/
|
||||
class ServiceRegistrar(private val appConfig: AppConfig) {
|
||||
private companion object {
|
||||
private val logger = LoggerFactory.getLogger(ServiceRegistrar::class.java)
|
||||
}
|
||||
|
||||
private val consul: Consul by lazy {
|
||||
val consulConfig = appConfig.serviceDiscovery
|
||||
logger.info("Connecting to Consul at {}:{}", consulConfig.consulHost, consulConfig.consulPort)
|
||||
Consul.builder()
|
||||
.withUrl("http://${consulConfig.consulHost}:${consulConfig.consulPort}")
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt und registriert einen Service basierend auf der App-Konfiguration.
|
||||
* @return Eine ServiceRegistration-Instanz zur Verwaltung des Lebenszyklus.
|
||||
*/
|
||||
fun registerCurrentService(): ServiceRegistration {
|
||||
val serviceName = appConfig.appInfo.name
|
||||
val servicePort = appConfig.server.port
|
||||
val serviceId = "$serviceName-${UUID.randomUUID()}"
|
||||
val hostAddress = InetAddress.getLocalHost().hostAddress
|
||||
val hostAddress = appConfig.server.advertisedHost
|
||||
|
||||
// KORREKTUR: Der Health Check MUSS über die statische Factory-Methode `http`
|
||||
// der `Registration.RegCheck`-Klasse erstellt werden. Dies war die Hauptfehlerquelle.
|
||||
val healthCheck = Registration.RegCheck.http(
|
||||
"http://$hostAddress:$servicePort/health", // Standard-Health-Check-Pfad
|
||||
10L, // Intervall in Sekunden
|
||||
5L // Timeout in Sekunden
|
||||
"http://$hostAddress:$servicePort/health",
|
||||
10L,
|
||||
5L
|
||||
)
|
||||
|
||||
// ========= FINALE KORREKTUR =========
|
||||
// Wir erstellen die Liste und die Map VORHER mit expliziten Typen,
|
||||
// um dem Compiler bei der Typinferenz zu helfen.
|
||||
val serviceTags: List<String> = listOf("env:${appConfig.environment.name.lowercase()}")
|
||||
val serviceMeta: Map<String, String> = mapOf("version" to appConfig.appInfo.version)
|
||||
|
||||
val registration = ImmutableRegistration.builder()
|
||||
.id(serviceId)
|
||||
.name(serviceName)
|
||||
.address(hostAddress)
|
||||
.port(servicePort)
|
||||
.check(healthCheck)
|
||||
.tags(listOf("env:${appConfig.environment.name.lowercase()}"))
|
||||
.meta(mapOf("version" to appConfig.appInfo.version))
|
||||
.tags(serviceTags) // Verwenden der explizit typisierten Variablen
|
||||
.meta(serviceMeta) // Verwenden der explizit typisierten Variablen
|
||||
.build()
|
||||
|
||||
val serviceRegistration = ServiceRegistration(consul, registration)
|
||||
serviceRegistration.register()
|
||||
|
||||
// Fügt einen Shutdown-Hook hinzu, um den Service beim Beenden sauber zu deregistrieren
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
println("Shutdown-Hook: Deregistriere Service ${serviceId}...")
|
||||
logger.info("Shutdown hook triggered: Deregistering service '{}'...", serviceId)
|
||||
serviceRegistration.deregister()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.benasher44.uuid.Uuid
|
|||
import com.benasher44.uuid.uuidFrom
|
||||
// KORREKTUR: Der Import wurde von java.math.BigDecimal auf die korrekte Bibliothek geändert.
|
||||
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.LocalTime
|
||||
|
|
@ -14,6 +14,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
|||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
object BigDecimalSerializer : KSerializer<BigDecimal> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING)
|
||||
|
|
@ -27,6 +28,7 @@ object UuidSerializer : KSerializer<Uuid> {
|
|||
override fun deserialize(decoder: Decoder): Uuid = uuidFrom(decoder.decodeString())
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
object KotlinInstantSerializer : KSerializer<Instant> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
|
||||
override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeString(value.toString())
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package at.mocode.core.utils.validation
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.time.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.todayIn
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
/**
|
||||
* Common validation utilities
|
||||
|
|
@ -92,6 +93,7 @@ object ValidationUtils {
|
|||
/**
|
||||
* Validates birth date
|
||||
*/
|
||||
@OptIn(ExperimentalTime::class)
|
||||
fun validateBirthDate(birthDate: LocalDate?, fieldName: String = "geburtsdatum"): ValidationError? {
|
||||
if (birthDate == null) return null
|
||||
|
||||
|
|
@ -116,6 +118,7 @@ object ValidationUtils {
|
|||
/**
|
||||
* Validates year value
|
||||
*/
|
||||
@OptIn(ExperimentalTime::class)
|
||||
fun validateYear(year: Int?, fieldName: String, minYear: Int = 1900): ValidationError? {
|
||||
if (year == null) return null
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ package at.mocode.core.utils.database
|
|||
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.*
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
/**
|
||||
* Comprehensive database connectivity and operations test.
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ lettuce = "6.3.1.RELEASE"
|
|||
# --- Service Discovery & Monitoring ---
|
||||
consulClient = "1.5.3"
|
||||
micrometer = "1.12.2"
|
||||
micrometerTracing = "1.2.5" # NEU
|
||||
zipkin = "2.24.4" # NEU (Verwendet eine neuere, kompatible Version)
|
||||
zipkinReporter = "2.16.4" # NEU
|
||||
micrometerTracing = "1.2.5"
|
||||
zipkin = "3.0.5"
|
||||
zipkinReporter = "2.16.4"
|
||||
|
||||
# --- Authentication ---
|
||||
auth0Jwt = "4.4.0"
|
||||
|
|
@ -134,10 +134,10 @@ lettuce-core = { module = "io.lettuce:lettuce-core", version.ref = "lettuce" }
|
|||
consul-client = { module = "com.orbitz.consul:consul-client", version.ref = "consulClient" }
|
||||
micrometer-prometheus = { module = "io.micrometer:micrometer-registry-prometheus", version.ref = "micrometer" }
|
||||
micrometer-tracing-bridge-brave = { module = "io.micrometer:micrometer-tracing-bridge-brave", version.ref = "micrometerTracing" } # NEU
|
||||
zipkin-reporter-brave = { module = "io.zipkin.reporter2:zipkin-reporter-brave", version.ref = "zipkinReporter" } # NEU
|
||||
zipkin-sender-okhttp3 = { module = "io.zipkin.reporter2:zipkin-sender-okhttp3", version.ref = "zipkinReporter" } # NEU
|
||||
zipkin-server = { module = "io.zipkin:zipkin-server", version.ref = "zipkin" } # NEU
|
||||
zipkin-autoconfigure-ui = { module = "io.zipkin:zipkin-autoconfigure-ui", version.ref = "zipkin" } # NEU
|
||||
zipkin-reporter-brave = { module = "io.zipkin.reporter2:zipkin-reporter-brave", version.ref = "zipkinReporter" }
|
||||
zipkin-sender-okhttp3 = { module = "io.zipkin.reporter2:zipkin-sender-okhttp3", version.ref = "zipkinReporter" }
|
||||
zipkin-server = { module = "io.zipkin:zipkin-server", version.ref = "zipkin" }
|
||||
zipkin-autoconfigure-ui = { module = "io.zipkin:zipkin-autoconfigure-ui", version.ref = "zipkin" }
|
||||
|
||||
# --- Authentication ---
|
||||
auth0-java-jwt = { module = "com.auth0:java-jwt", version.ref = "auth0Jwt" }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ plugins {
|
|||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
// Deaktiviert die Erstellung eines ausführbaren Jars für dieses Bibliotheks-Modul.
|
||||
tasks.getByName("bootJar") {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
|
||||
api(platform(projects.platform.platformBom))
|
||||
|
|
@ -20,4 +25,6 @@ dependencies {
|
|||
|
||||
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.bundles.testing.jvm)
|
||||
testImplementation(libs.kotlin.test)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package at.mocode.infrastructure.eventstore.redis
|
|||
|
||||
import at.mocode.core.domain.event.DomainEvent
|
||||
import at.mocode.infrastructure.eventstore.api.EventSerializer
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.annotation.PreDestroy
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.data.domain.Range
|
||||
import org.springframework.data.redis.connection.stream.*
|
||||
|
|
@ -9,8 +11,6 @@ import org.springframework.data.redis.core.StringRedisTemplate
|
|||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import javax.annotation.PostConstruct
|
||||
import javax.annotation.PreDestroy
|
||||
|
||||
/**
|
||||
* Consumer for Redis Streams that processes events using consumer groups.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import at.mocode.core.domain.event.DomainEvent
|
|||
import at.mocode.infrastructure.eventstore.api.EventSerializer
|
||||
import at.mocode.infrastructure.eventstore.api.EventStore
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
|
||||
|
|
@ -18,8 +20,6 @@ import java.time.Instant
|
|||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Integration tests for Redis Event Store.
|
||||
|
|
@ -285,7 +285,7 @@ class RedisEventStoreIntegrationTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: Int,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
|
||||
|
|
@ -293,7 +293,7 @@ class RedisEventStoreIntegrationTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: Int,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@ class RedisEventStoreTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: UUID,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
|
||||
|
|
@ -527,7 +527,7 @@ class RedisEventStoreTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: UUID,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ class RedisIntegrationTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: UUID,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
|
||||
|
|
@ -239,7 +239,7 @@ class RedisIntegrationTest {
|
|||
override val eventId: UUID = UUID.randomUUID(),
|
||||
override val timestamp: Instant = Instant.now(),
|
||||
override val aggregateId: UUID,
|
||||
override val version: Long,
|
||||
override val version: UUID,
|
||||
val name: String
|
||||
) : BaseDomainEvent(eventId, timestamp, aggregateId, version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ plugins {
|
|||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
// Deaktiviert die Erstellung eines ausführbaren Jars für dieses Bibliotheks-Modul.
|
||||
tasks.getByName("bootJar") {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
|
||||
implementation(platform(projects.platform.platformBom))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ plugins {
|
|||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
// Deaktiviert die Erstellung eines ausführbaren Jars für dieses Bibliotheks-Modul.
|
||||
tasks.getByName("bootJar") {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
|
||||
api(platform(projects.platform.platformBom))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@ plugins {
|
|||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
// Deaktiviert die Erstellung eines ausführbaren Jars für dieses Bibliotheks-Modul.
|
||||
tasks.getByName("bootJar") {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
|
||||
implementation(platform(projects.platform.platformBom))
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ dependencies {
|
|||
implementation(libs.spring.boot.starter.actuator)
|
||||
|
||||
// Abhängigkeiten für den Zipkin-Server und seine UI.
|
||||
// OPTIMIERUNG: Versionen werden jetzt zentral über libs.versions.toml verwaltet.
|
||||
implementation(libs.zipkin.server)
|
||||
implementation(libs.zipkin.autoconfigure.ui)
|
||||
|
||||
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/*plugins {
|
||||
/*
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +35,9 @@ kotlin {
|
|||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// Dieses Modul bündelt alle für JVM-Tests notwendigen Abhängigkeiten.
|
||||
// Jedes Modul, das Tests enthält, sollte dieses Modul mit `testImplementation` einbinden.
|
||||
|
|
@ -46,12 +49,11 @@ dependencies {
|
|||
// Importiert die zentrale BOM für konsistente Versionen.
|
||||
api(platform(projects.platform.platformBom))
|
||||
|
||||
// OPTIMIERUNG: Verwendung von Bundles, um die Konfiguration zu vereinfachen.
|
||||
// Diese Bundles sind in `libs.versions.toml` definiert.
|
||||
api(libs.bundles.testing.jvm)
|
||||
api(libs.bundles.testcontainers)
|
||||
|
||||
// Einzelne Test-Abhängigkeiten, die nicht in den Haupt-Bundles enthalten sind.
|
||||
// Stellt Spring Boot Test-Abhängigkeiten und die H2-Datenbank für Tests bereit.
|
||||
api(libs.spring.boot.starter.test)
|
||||
api(libs.h2.driver) // H2 wird oft für In-Memory-Tests benötigt.
|
||||
api(libs.h2.driver)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user