(fix) Server-Modul

This commit is contained in:
Stefan Mogeritsch 2025-06-29 23:34:04 +02:00
parent 89b8900fb2
commit 2ad447c978
27 changed files with 321 additions and 98 deletions

View File

@ -22,7 +22,7 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
// Configure application // Configure application
application { application {
mainClass.set("at.mocode.server.ApplicationKt") mainClass.set("at.mocode.ApplicationKt")
applicationDefaultJvmArgs = listOf( applicationDefaultJvmArgs = listOf(
"-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}", "-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}",
"-XX:+UseG1GC", // Use G1 Garbage Collector "-XX:+UseG1GC", // Use G1 Garbage Collector
@ -75,7 +75,7 @@ dependencies {
// Testing // Testing
testImplementation(libs.ktor.server.tests) testImplementation(libs.ktor.server.tests)
testImplementation(libs.kotlin.test.junit) testImplementation(libs.kotlin.test)
testImplementation(libs.junitJupiter) testImplementation(libs.junitJupiter)
} }

View File

@ -1,6 +1,7 @@
package at.mocode.server package at.mocode
import at.mocode.server.plugins.configureDatabase import at.mocode.plugins.configureDatabase
import at.mocode.plugins.configureRouting
import io.ktor.http.* import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.* import io.ktor.server.application.*
@ -11,7 +12,6 @@ import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.defaultheaders.* import io.ktor.server.plugins.defaultheaders.*
import io.ktor.server.plugins.statuspages.* import io.ktor.server.plugins.statuspages.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.slf4j.event.Level import org.slf4j.event.Level
@ -22,18 +22,10 @@ fun main(args: Array<String>) {
fun Application.module() { fun Application.module() {
val log = LoggerFactory.getLogger("Application") val log = LoggerFactory.getLogger("Application")
log.info("Initializing application...") log.info("Initializing application...")
// Configure database
configureDatabase() configureDatabase()
// Configure plugins
configurePlugins() configurePlugins()
// Configure routing
configureRouting() configureRouting()
log.info("Application initialized successfully") log.info("Application initialized successfully")
} }
@ -93,7 +85,7 @@ private fun Application.configurePlugins() {
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
// Log the error but continue with default configuration // Log the error but continue with the default configuration
this@configurePlugins.log.warn("Failed to configure CORS from config, using defaults: ${e.message}") this@configurePlugins.log.warn("Failed to configure CORS from config, using defaults: ${e.message}")
} }
} }
@ -115,28 +107,3 @@ private fun Application.configurePlugins() {
} }
} }
} }
/**
* Configures all routes for the application
*/
private fun Application.configureRouting() {
routing {
// Health check endpoint
get("/health") {
call.respondText("OK")
}
// Root endpoint with basic information
get("/") {
// Read application info from config if available
val appName = application.environment.config.propertyOrNull("application.name")?.getString() ?: "Meldestelle API Server"
val appVersion = application.environment.config.propertyOrNull("application.version")?.getString() ?: "1.0.0"
val appEnv = application.environment.config.propertyOrNull("application.environment")?.getString() ?: "development"
call.respondText("$appName v$appVersion - Running in $appEnv mode")
}
// API routes can be organized in separate files and included here
// Example: registerUserRoutes()
}
}

View File

@ -0,0 +1,2 @@
package at.mocode.db

View File

@ -0,0 +1,2 @@
package at.mocode.model

View File

@ -0,0 +1,4 @@
package at.mocode.model
interface EventRepository {
}

View File

@ -0,0 +1,4 @@
package at.mocode.model
class PostgresEventRepository {
}

View File

@ -1,6 +1,5 @@
package at.mocode.server.plugins package at.mocode.plugins
import at.mocode.server.tables.*
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import io.ktor.server.application.* import io.ktor.server.application.*
@ -175,14 +174,14 @@ private fun initializeSchema(log: Logger, isTestEnvironment: Boolean, isIdeaEnvi
try { try {
// Create all tables if they don't exist // Create all tables if they don't exist
SchemaUtils.create( SchemaUtils.create(
VereineTable, _root_ide_package_.at.mocode.tables.VereineTable,
PersonenTable, _root_ide_package_.at.mocode.tables.PersonenTable,
PferdeTable, _root_ide_package_.at.mocode.tables.PferdeTable,
VeranstaltungenTable, _root_ide_package_.at.mocode.tables.VeranstaltungenTable,
TurniereTable, _root_ide_package_.at.mocode.tables.TurniereTable,
ArtikelTable, _root_ide_package_.at.mocode.tables.ArtikelTable,
PlaetzeTable, _root_ide_package_.at.mocode.tables.PlaetzeTable,
LizenzenTable _root_ide_package_.at.mocode.tables.LizenzenTable
// Add more tables here if needed // Add more tables here if needed
) )
log.info("Database schema initialized successfully.") log.info("Database schema initialized successfully.")

View File

@ -0,0 +1,32 @@
package at.mocode.plugins
import io.ktor.server.application.Application
import io.ktor.server.response.respondText
import io.ktor.server.routing.application
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
/**
* Configures all routes for the application
*/
fun Application.configureRouting() {
routing {
// Health check endpoint
get("/health") {
call.respondText("OK")
}
// Root endpoint with basic information
get("/") {
// Read application info from config if available
val appName = application.environment.config.propertyOrNull("application.name")?.getString() ?: "Meldestelle API Server"
val appVersion = application.environment.config.propertyOrNull("application.version")?.getString() ?: "1.0.0"
val appEnv = application.environment.config.propertyOrNull("application.environment")?.getString() ?: "development"
call.respondText("$appName v$appVersion - Running in $appEnv mode")
}
// API routes can be organized in separate files and included here
// Example: registerUserRoutes()
}
}

View File

@ -0,0 +1,2 @@
package at.mocode.plugins

View File

@ -1,4 +1,4 @@
package at.mocode.server.tables package at.mocode.tables
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.kotlin.datetime.timestamp

View File

@ -1,16 +1,17 @@
package at.mocode.server.tables package at.mocode.tables
import at.mocode.shared.model.enums.LizenzTyp
import at.mocode.shared.model.enums.Sparte import at.mocode.shared.enums.LizenzTypE
import at.mocode.shared.enums.SparteE
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.date
object LizenzenTable : Table("lizenzen") { object LizenzenTable : Table("lizenzen") {
val id = uuid("id") val id = uuid("id")
val personId = uuid("person_id").references(PersonenTable.id) val personId = uuid("person_id").references(PersonenTable.id)
val lizenzTyp = enumerationByName("lizenz_typ", 50, LizenzTyp::class) val lizenzTyp = enumerationByName("lizenz_typ", 50, LizenzTypE::class)
val stufe = varchar("stufe", 20).nullable() val stufe = varchar("stufe", 20).nullable()
val sparte = enumerationByName("sparte", 50, Sparte::class).nullable() val sparte = enumerationByName("sparte", 50, SparteE::class).nullable()
val gueltigBisJahr = integer("gueltig_bis_jahr").nullable() val gueltigBisJahr = integer("gueltig_bis_jahr").nullable()
val ausgestelltAm = date("ausgestellt_am").nullable() val ausgestelltAm = date("ausgestellt_am").nullable()

View File

@ -1,6 +1,6 @@
package at.mocode.server.tables package at.mocode.tables
import at.mocode.shared.model.enums.Geschlecht import at.mocode.shared.enums.GeschlechtE
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
@ -12,7 +12,7 @@ object PersonenTable : Table("personen") {
val vorname = varchar("vorname", 100) val vorname = varchar("vorname", 100)
val titel = varchar("titel", 50).nullable() val titel = varchar("titel", 50).nullable()
val geburtsdatum = date("geburtsdatum").nullable() val geburtsdatum = date("geburtsdatum").nullable()
val geschlecht = enumerationByName("geschlecht", 10, Geschlecht::class).nullable() val geschlecht = enumerationByName("geschlecht", 10, GeschlechtE::class).nullable()
val nationalitaet = varchar("nationalitaet", 3).nullable() val nationalitaet = varchar("nationalitaet", 3).nullable()
val email = varchar("email", 255).nullable() val email = varchar("email", 255).nullable()
val telefon = varchar("telefon", 50).nullable() val telefon = varchar("telefon", 50).nullable()

View File

@ -1,6 +1,6 @@
package at.mocode.server.tables package at.mocode.tables
import at.mocode.shared.model.enums.GeschlechtPferd import at.mocode.shared.enums.GeschlechtPferdE
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
@ -11,7 +11,7 @@ object PferdeTable : Table("pferde") {
val name = varchar("name", 255) val name = varchar("name", 255)
val lebensnummer = varchar("lebensnummer", 20).nullable() val lebensnummer = varchar("lebensnummer", 20).nullable()
val feiPassNr = varchar("fei_pass_nr", 20).nullable() val feiPassNr = varchar("fei_pass_nr", 20).nullable()
val geschlecht = enumerationByName("geschlecht", 10, GeschlechtPferd::class).nullable() val geschlecht = enumerationByName("geschlecht", 10, GeschlechtPferdE::class).nullable()
val geburtsjahr = integer("geburtsjahr").nullable() val geburtsjahr = integer("geburtsjahr").nullable()
val rasse = varchar("rasse", 100).nullable() val rasse = varchar("rasse", 100).nullable()
val farbe = varchar("farbe", 50).nullable() val farbe = varchar("farbe", 50).nullable()

View File

@ -1,6 +1,6 @@
package at.mocode.server.tables package at.mocode.tables
import at.mocode.shared.model.enums.PlatzTyp import at.mocode.shared.enums.PlatzTypE
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
object PlaetzeTable : Table("plaetze") { object PlaetzeTable : Table("plaetze") {
@ -9,7 +9,7 @@ object PlaetzeTable : Table("plaetze") {
val name = varchar("name", 100) val name = varchar("name", 100)
val dimension = varchar("dimension", 50).nullable() val dimension = varchar("dimension", 50).nullable()
val boden = varchar("boden", 100).nullable() val boden = varchar("boden", 100).nullable()
val typ = enumerationByName("typ", 20, PlatzTyp::class) val typ = enumerationByName("typ", 20, PlatzTypE::class)
override val primaryKey = PrimaryKey(id) override val primaryKey = PrimaryKey(id)

View File

@ -1,4 +1,4 @@
package at.mocode.server.tables package at.mocode.tables
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date // Für kotlinx-datetime LocalDate import org.jetbrains.exposed.sql.kotlin.datetime.date // Für kotlinx-datetime LocalDate

View File

@ -1,6 +1,6 @@
package at.mocode.server.tables package at.mocode.tables
import at.mocode.shared.model.enums.VeranstalterTyp import at.mocode.shared.enums.VeranstalterTypE
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
@ -13,8 +13,8 @@ object VeranstaltungenTable : Table("veranstaltungen") {
val veranstalterName = varchar("veranstalter_name", 255) val veranstalterName = varchar("veranstalter_name", 255)
val veranstalterOepsNummer = varchar("veranstalter_oeps_nr", 10).nullable() val veranstalterOepsNummer = varchar("veranstalter_oeps_nr", 10).nullable()
val veranstalterTyp = val veranstalterTyp =
enumerationByName("veranstalter_typ", 20, VeranstalterTyp::class).default( enumerationByName("veranstalter_typ", 20, VeranstalterTypE::class).default(
VeranstalterTyp.UNBEKANNT VeranstalterTypE.UNBEKANNT
) )
val veranstaltungsortName = varchar("veranstaltungsort_name", 255) val veranstaltungsortName = varchar("veranstaltungsort_name", 255)
val veranstaltungsortAdresse = varchar("veranstaltungsort_adresse", 500) val veranstaltungsortAdresse = varchar("veranstaltungsort_adresse", 500)

View File

@ -1,4 +1,4 @@
package at.mocode.server.tables package at.mocode.tables
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.kotlin.datetime.timestamp

View File

@ -2,7 +2,7 @@
ktor: ktor:
deployment: deployment:
# Server port configuration # Server port configuration
port: 8081 port: 8080
# Connection timeout in seconds # Connection timeout in seconds
connectionTimeout: 30 connectionTimeout: 30
# Maximum number of concurrent connections # Maximum number of concurrent connections
@ -13,7 +13,7 @@ ktor:
- resources - resources
application: application:
modules: modules:
- at.mocode.server.ApplicationKt.module - at.mocode.ApplicationKt.module
# Database Configuration # Database Configuration
database: database:
@ -40,8 +40,8 @@ security:
issuer: "meldestelle-server" issuer: "meldestelle-server"
audience: "meldestelle-clients" audience: "meldestelle-clients"
realm: "meldestelle" realm: "meldestelle"
# Secret should be set via environment variable in production # Secret should be set via an environment variable in production
secret: "${JWT_SECRET:dev-secret-key-change-in-production" secret: "${JWT_SECRET:dev-secret-key-change-in-production}"
# Token validity duration in milliseconds (24 hours) # Token validity duration in milliseconds (24 hours)
validity: 86400000 validity: 86400000
@ -52,13 +52,6 @@ cors:
- "localhost:3000" - "localhost:3000"
- "127.0.0.1:3000" - "127.0.0.1:3000"
- "meldestelle.mocode.at" - "meldestelle.mocode.at"
# Allow these HTTP methods
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
# Allow credentials (cookies, auth headers) # Allow credentials (cookies, auth headers)
allowCredentials: true allowCredentials: true

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,166 @@
package at.mocode
import at.mocode.plugins.configureRouting
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.defaultheaders.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import kotlinx.serialization.json.Json
import kotlin.test.*
/**
* Comprehensive test suite for the Meldestelle Server Application.
*
* This test class verifies:
* - Application startup and initialization
* - Core routing functionality (health check, root endpoint)
* - Plugin configuration (CORS, content negotiation, default headers)
* - Error handling
* - Basic HTTP functionality
*/
class ApplicationTest {
companion object {
init {
// Set test environment property for database configuration
// This ensures the application uses H2 in-memory database for testing
System.setProperty("isTestEnvironment", "true")
}
}
@Test
fun testApplicationStartup() = testApplication {
application {
module()
}
// Test that the application starts without errors
// This test passes if no exceptions are thrown during module initialization
}
@Test
fun testHealthEndpoint() = testApplication {
application {
module()
}
client.get("/health").apply {
assertEquals(HttpStatusCode.OK, status)
assertEquals("OK", bodyAsText())
}
}
@Test
fun testRootEndpoint() = testApplication {
application {
module()
}
client.get("/").apply {
assertEquals(HttpStatusCode.OK, status)
val responseText = bodyAsText()
// The response format is: "Meldestelle API Server v1.0.0 - Running in development mode"
assertTrue(responseText.contains("Meldestelle API Server"), "Response should contain 'Meldestelle API Server', but was: $responseText")
assertTrue(responseText.contains("v1.0.0"), "Response should contain 'v1.0.0', but was: $responseText")
assertTrue(responseText.contains("development"), "Response should contain 'development', but was: $responseText")
}
}
@Test
fun testNotFoundEndpoint() = testApplication {
application {
module()
}
client.get("/nonexistent").apply {
assertEquals(HttpStatusCode.NotFound, status)
val responseText = bodyAsText()
assertTrue(responseText.contains("404: Page Not Found"))
}
}
@Test
fun testDefaultHeaders() = testApplication {
application {
module()
}
client.get("/health").apply {
assertEquals(HttpStatusCode.OK, status)
// Check that default headers are set
assertEquals("Ktor", headers["X-Engine"])
assertEquals("nosniff", headers["X-Content-Type-Options"])
}
}
@Test
fun testCorsConfiguration() = testApplication {
application {
module()
}
client.get("/health") {
header(HttpHeaders.Origin, "http://localhost:3000")
}.apply {
assertEquals(HttpStatusCode.OK, status)
// Check that CORS headers are present
assertNotNull(headers[HttpHeaders.AccessControlAllowOrigin])
}
}
@Test
fun testContentNegotiation() = testApplication {
application {
module()
}
client.get("/health") {
header(HttpHeaders.Accept, "application/json")
}.apply {
assertEquals(HttpStatusCode.OK, status)
// The response should still be text for the health endpoint
assertEquals("OK", bodyAsText())
}
}
@Test
fun testOptionsRequest() = testApplication {
application {
module()
}
client.options("/health") {
header(HttpHeaders.Origin, "http://localhost:3000")
header(HttpHeaders.AccessControlRequestMethod, "GET")
}.apply {
// OPTIONS requests should be handled by CORS
assertTrue(status.isSuccess() || status == HttpStatusCode.NotFound)
}
}
@Test
fun testBasicRoutingWithoutDatabase() = testApplication {
application {
// Test routing functionality without full application module
// This isolates routing from database dependencies
install(DefaultHeaders) {
header("X-Engine", "Ktor")
header("X-Content-Type-Options", "nosniff")
}
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
configureRouting()
}
client.get("/health").apply {
assertEquals(HttpStatusCode.OK, status)
assertEquals("OK", bodyAsText())
}
}
}

View File

@ -0,0 +1,10 @@
package at.mocode
import kotlin.test.*
class SimpleTest {
@Test
fun testBasic() {
assertEquals(1, 1)
}
}

View File

@ -51,14 +51,5 @@ kotlin {
implementation(kotlin("test")) implementation(kotlin("test"))
} }
} }
// val jvmMain by getting {
// dependsOn(commonMain)
// }
//
// val wasmJsMain by getting {
// dependsOn(commonMain)
// }
} }
} }

View File

@ -0,0 +1,41 @@
package at.mocode.model
import kotlinx.serialization.Serializable
/**
* Represents a tournament (Turnier) with its details and associated competitions (Bewerbe).
* Each tournament can have one or more competitions.
*/
@Serializable
data class Turnier(
/** The name of the tournament, e.g. "CSN-C NEU CSNP-C NEU NEUMARKT/M., OÖ" */
val name: String,
/** The date of the tournament as a formatted string, e.g. "7.JUNI 2025" */
val datum: String,
/** Unique identifier for the tournament */
val number: Int,
/** List of competitions (Bewerbe) associated with this tournament */
var bewerbe: List<Bewerb>
)
/**
* Represents a competition (Bewerb) within a tournament.
* A competition has specific details like number, title, class, and optional task.
*/
@Serializable
data class Bewerb(
/** Competition number, e.g. 1, 2, etc. */
val nummer: Int,
/** Title of the competition, e.g. "Stilspringprüfung" or "Dressurprüfung" */
val titel: String,
/** Class/level of the competition, e.g. "60 cm" or "Kl. A" */
val klasse: String,
/** Optional task identifier, e.g. "DRA 1" */
val task: String?
)

View File

@ -18,7 +18,7 @@ enum class CupSerieTypE { CUP_SERIE }
@Serializable @Serializable
enum class LizenzKategorieE { REITERLIZENZ, FAHRERLIZENZ, STARTKARTE } enum class LizenzKategorieE { REITERLIZENZ, FAHRERLIZENZ, STARTKARTE }
@Serializable @Serializable
enum class LizenzTyp { REITER, FAHRER, VOLTIGIERER, WESTERN, WORKING_EQUITATION, POLO, STARTKARTE_ALLG, STARTKARTE_VOLTIGIEREN, STARTKARTE_WESTERN, STARTKARTE_ISLAND, STARTKARTE_FAHREN_JUGEND, STARTKARTE_HORSEBALL, STARTKARTE_POLO, PARAEQUESTRIAN, SONSTIGE } enum class LizenzTypE { REITER, FAHRER, VOLTIGIERER, WESTERN, WORKING_EQUITATION, POLO, STARTKARTE_ALLG, STARTKARTE_VOLTIGIEREN, STARTKARTE_WESTERN, STARTKARTE_ISLAND, STARTKARTE_FAHREN_JUGEND, STARTKARTE_HORSEBALL, STARTKARTE_POLO, PARAEQUESTRIAN, SONSTIGE }
@Serializable @Serializable
@ -70,4 +70,4 @@ enum class RichterPositionE { C, E, H, M, B, VORSITZ, SEITENRICHTER, SONSTIGE }
@Serializable @Serializable
enum class GeschlechtE { M, W, D, UNBEKANNT } enum class GeschlechtE { M, W, D, UNBEKANNT }
@Serializable @Serializable
enum class GeschlechtPferd { HENGST, STUTE, WALLACH, UNBEKANNT } enum class GeschlechtPferdE { HENGST, STUTE, WALLACH, UNBEKANNT }

View File

@ -1,7 +1,6 @@
package at.mocode.shared.model package at.mocode.shared.model
import at.mocode.shared.model.Artikel import at.mocode.shared.enums.NennungsArtE
import at.mocode.shared.enums.NennungsArt
import at.mocode.shared.serializers.BigDecimalSerializer import at.mocode.shared.serializers.BigDecimalSerializer
import at.mocode.shared.serializers.KotlinInstantSerializer import at.mocode.shared.serializers.KotlinInstantSerializer
import at.mocode.shared.serializers.KotlinLocalDateSerializer import at.mocode.shared.serializers.KotlinLocalDateSerializer

View File

@ -1,6 +1,6 @@
package at.mocode.shared.stammdaten package at.mocode.shared.stammdaten
import at.mocode.shared.enums.LizenzTyp import at.mocode.shared.enums.LizenzTypE
import at.mocode.shared.enums.SparteE import at.mocode.shared.enums.SparteE
import at.mocode.shared.serializers.KotlinLocalDateSerializer import at.mocode.shared.serializers.KotlinLocalDateSerializer
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class LizenzInfo( data class LizenzInfo(
val lizenzTyp: LizenzTyp, val lizenzTyp: LizenzTypE,
val stufe: String?, val stufe: String?,
val sparteE: SparteE?, val sparteE: SparteE?,
val gueltigBisJahr: Int?, // Jahr als Int val gueltigBisJahr: Int?, // Jahr als Int

View File

@ -1,6 +1,6 @@
package at.mocode.shared.stammdaten package at.mocode.shared.stammdaten
import at.mocode.shared.enums.GeschlechtPferd import at.mocode.shared.enums.GeschlechtPferdE
import at.mocode.shared.serializers.KotlinInstantSerializer import at.mocode.shared.serializers.KotlinInstantSerializer
import at.mocode.shared.serializers.UuidSerializer import at.mocode.shared.serializers.UuidSerializer
import com.benasher44.uuid.Uuid import com.benasher44.uuid.Uuid
@ -18,7 +18,7 @@ data class Pferd(
var name: String, var name: String,
var lebensnummer: String?, var lebensnummer: String?,
var feiPassNr: String?, var feiPassNr: String?,
var geschlecht: GeschlechtPferd?, var geschlecht: GeschlechtPferdE?,
var geburtsjahr: Int?, var geburtsjahr: Int?,
var rasse: String?, var rasse: String?,
var farbe: String?, var farbe: String?,