Register events modules in Gradle build and refactor VeranstaltungController: remove unused use cases, streamline request handling, and improve error responses.
This commit is contained in:
@@ -1,39 +1,32 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.spring)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.ktor)
|
||||
application
|
||||
// KORREKTUR 1: Dieses Plugin hinzufügen, um die Spring-BOM zu aktivieren.
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
alias(libs.plugins.ktor)
|
||||
application
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("at.mocode.events.api.ApplicationKt")
|
||||
mainClass.set("at.mocode.events.api.ApplicationKt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// KORREKTUR 2: Die Spring-Boot-BOM hier explizit als Plattform deklarieren.
|
||||
api(platform(libs.spring.boot.dependencies))
|
||||
// Bestehende Abhängigkeiten
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.events.eventsDomain)
|
||||
implementation(projects.events.eventsApplication)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
// Spring dependencies (jetzt mit korrekter Version aus der BOM)
|
||||
implementation(libs.spring.web)
|
||||
implementation(libs.springdoc.openapi.starter.common)
|
||||
// Ktor Server
|
||||
implementation(libs.ktor.server.core)
|
||||
implementation(libs.ktor.server.netty)
|
||||
implementation(libs.ktor.server.contentNegotiation)
|
||||
implementation(libs.ktor.server.serialization.kotlinx.json)
|
||||
implementation(libs.ktor.server.statusPages)
|
||||
implementation(libs.ktor.server.auth)
|
||||
implementation(libs.ktor.server.authJwt)
|
||||
api(platform(libs.spring.boot.dependencies))
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.backend.services.events.eventsDomain)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(libs.spring.web)
|
||||
implementation(libs.springdoc.openapi.starter.common)
|
||||
implementation(libs.ktor.server.core)
|
||||
implementation(libs.ktor.server.netty)
|
||||
implementation(libs.ktor.server.contentNegotiation)
|
||||
implementation(libs.ktor.server.serialization.kotlinx.json)
|
||||
implementation(libs.ktor.server.statusPages)
|
||||
implementation(libs.ktor.server.auth)
|
||||
implementation(libs.ktor.server.authJwt)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
// Ktor 3.x Test-Host statt veraltetes tests-Artefakt
|
||||
testImplementation(libs.ktor.server.testHost)
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.ktor.server.testHost)
|
||||
}
|
||||
|
||||
+67
-138
@@ -1,15 +1,11 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
package at.mocode.events.api.rest
|
||||
|
||||
import at.mocode.events.domain.model.Veranstaltung
|
||||
import at.mocode.core.domain.model.ApiResponse
|
||||
import at.mocode.core.domain.model.SparteE
|
||||
import at.mocode.events.application.usecase.CreateVeranstaltungUseCase
|
||||
import at.mocode.events.application.usecase.DeleteVeranstaltungUseCase
|
||||
import at.mocode.events.application.usecase.GetVeranstaltungUseCase
|
||||
import at.mocode.events.application.usecase.UpdateVeranstaltungUseCase
|
||||
import at.mocode.events.domain.repository.VeranstaltungRepository
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import at.mocode.core.utils.validation.ApiValidationUtils
|
||||
import kotlin.uuid.Uuid
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.request.*
|
||||
@@ -28,11 +24,6 @@ class VeranstaltungController(
|
||||
private val veranstaltungRepository: VeranstaltungRepository
|
||||
) {
|
||||
|
||||
private val createVeranstaltungUseCase = CreateVeranstaltungUseCase(veranstaltungRepository)
|
||||
private val getVeranstaltungUseCase = GetVeranstaltungUseCase(veranstaltungRepository)
|
||||
private val updateVeranstaltungUseCase = UpdateVeranstaltungUseCase(veranstaltungRepository)
|
||||
private val deleteVeranstaltungUseCase = DeleteVeranstaltungUseCase(veranstaltungRepository)
|
||||
|
||||
/**
|
||||
* Configures the event-related routes.
|
||||
*/
|
||||
@@ -42,31 +33,17 @@ class VeranstaltungController(
|
||||
// GET /api/events - Get all events with optional filtering
|
||||
get {
|
||||
try {
|
||||
// Validate query parameters
|
||||
val validationErrors = ApiValidationUtils.validateQueryParameters(
|
||||
limit = call.request.queryParameters["limit"],
|
||||
offset = call.request.queryParameters["offset"],
|
||||
startDate = call.request.queryParameters["startDate"],
|
||||
endDate = call.request.queryParameters["endDate"],
|
||||
search = call.request.queryParameters["search"]
|
||||
)
|
||||
|
||||
if (!ApiValidationUtils.isValid(validationErrors)) {
|
||||
call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>(ApiValidationUtils.createErrorMessage(validationErrors))
|
||||
)
|
||||
return@get
|
||||
}
|
||||
|
||||
val activeOnly = call.request.queryParameters["activeOnly"]?.toBoolean() ?: true
|
||||
val limit = call.request.queryParameters["limit"]?.toInt() ?: 100
|
||||
val offset = call.request.queryParameters["offset"]?.toInt() ?: 0
|
||||
|
||||
val organizerId = call.request.queryParameters["organizerId"]?.let {
|
||||
ApiValidationUtils.validateUuidString(it) ?: return@get call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("Invalid organizerId format")
|
||||
)
|
||||
try { Uuid.parse(it) } catch (e: Exception) {
|
||||
return@get call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("INVALID_ORGANIZER_ID", "Invalid organizerId format")
|
||||
)
|
||||
}
|
||||
}
|
||||
val searchTerm = call.request.queryParameters["search"]
|
||||
val publicOnly = call.request.queryParameters["publicOnly"]?.toBoolean() ?: false
|
||||
@@ -84,7 +61,7 @@ class VeranstaltungController(
|
||||
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success(events))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("Failed to retrieve events: ${e.message}"))
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("INTERNAL_ERROR", "Failed to retrieve events: ${e.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,18 +69,17 @@ class VeranstaltungController(
|
||||
get("/{id}") {
|
||||
try {
|
||||
val eventId = Uuid.parse(call.parameters["id"]!!)
|
||||
val request = GetVeranstaltungUseCase.GetVeranstaltungRequest(eventId)
|
||||
val response = getVeranstaltungUseCase.execute(request)
|
||||
val event = veranstaltungRepository.findById(eventId)
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success((response.data as GetVeranstaltungUseCase.GetVeranstaltungResponse).veranstaltung))
|
||||
if (event != null) {
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success(event))
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound, ApiResponse.error<Any>("Event not found"))
|
||||
call.respond(HttpStatusCode.NotFound, ApiResponse.error<Any>("NOT_FOUND", "Event not found"))
|
||||
}
|
||||
} catch (_: IllegalArgumentException) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("Invalid event ID format"))
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("INVALID_ID", "Invalid event ID format"))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("Failed to retrieve event: ${e.message}"))
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("INTERNAL_ERROR", "Failed to retrieve event: ${e.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +96,7 @@ class VeranstaltungController(
|
||||
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success(stats))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("Failed to retrieve event statistics: ${e.message}"))
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("INTERNAL_ERROR", "Failed to retrieve event statistics: ${e.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,26 +105,12 @@ class VeranstaltungController(
|
||||
try {
|
||||
val createRequest = call.receive<CreateEventRequest>()
|
||||
|
||||
// Validate input using shared validation utilities
|
||||
val validationErrors = ApiValidationUtils.validateEventRequest(
|
||||
name = createRequest.name,
|
||||
ort = createRequest.ort,
|
||||
startDatum = createRequest.startDatum,
|
||||
endDatum = createRequest.endDatum,
|
||||
maxTeilnehmer = createRequest.maxTeilnehmer
|
||||
)
|
||||
|
||||
if (!ApiValidationUtils.isValid(validationErrors)) {
|
||||
call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>(ApiValidationUtils.createErrorMessage(validationErrors))
|
||||
)
|
||||
return@post
|
||||
}
|
||||
|
||||
val useCaseRequest = CreateVeranstaltungUseCase.CreateVeranstaltungRequest(
|
||||
val veranstaltung = Veranstaltung(
|
||||
name = createRequest.name,
|
||||
untertitel = createRequest.untertitel,
|
||||
beschreibung = createRequest.beschreibung,
|
||||
logoUrl = createRequest.logoUrl,
|
||||
sponsoren = createRequest.sponsoren,
|
||||
startDatum = createRequest.startDatum,
|
||||
endDatum = createRequest.endDatum,
|
||||
ort = createRequest.ort,
|
||||
@@ -160,20 +122,16 @@ class VeranstaltungController(
|
||||
anmeldeschluss = createRequest.anmeldeschluss
|
||||
)
|
||||
|
||||
val response = createVeranstaltungUseCase.execute(useCaseRequest)
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
call.respond(HttpStatusCode.Created, ApiResponse.success((response.data as CreateVeranstaltungUseCase.CreateVeranstaltungResponse).veranstaltung))
|
||||
} else {
|
||||
val statusCode = when (response.error?.code) {
|
||||
"VALIDATION_ERROR" -> HttpStatusCode.BadRequest
|
||||
"DOMAIN_VALIDATION_ERROR" -> HttpStatusCode.BadRequest
|
||||
else -> HttpStatusCode.InternalServerError
|
||||
}
|
||||
call.respond(statusCode, ApiResponse.error<Any>(response.error?.message ?: "Failed to create event"))
|
||||
val errors = veranstaltung.validate()
|
||||
if (errors.isNotEmpty()) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("VALIDATION_ERROR", "Validation failed: ${errors.joinToString(", ")}"))
|
||||
return@post
|
||||
}
|
||||
|
||||
val savedEvent = veranstaltungRepository.save(veranstaltung)
|
||||
call.respond(HttpStatusCode.Created, ApiResponse.success(savedEvent))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("Invalid request data: ${e.message}"))
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("INVALID_REQUEST", "Invalid request data: ${e.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,27 +141,18 @@ class VeranstaltungController(
|
||||
val eventId = Uuid.parse(call.parameters["id"]!!)
|
||||
val updateRequest = call.receive<UpdateEventRequest>()
|
||||
|
||||
// Validate input using shared validation utilities
|
||||
val validationErrors = ApiValidationUtils.validateEventRequest(
|
||||
name = updateRequest.name,
|
||||
ort = updateRequest.ort,
|
||||
startDatum = updateRequest.startDatum,
|
||||
endDatum = updateRequest.endDatum,
|
||||
maxTeilnehmer = updateRequest.maxTeilnehmer
|
||||
)
|
||||
|
||||
if (!ApiValidationUtils.isValid(validationErrors)) {
|
||||
call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>(ApiValidationUtils.createErrorMessage(validationErrors))
|
||||
)
|
||||
val existingEvent = veranstaltungRepository.findById(eventId)
|
||||
if (existingEvent == null) {
|
||||
call.respond(HttpStatusCode.NotFound, ApiResponse.error<Any>("NOT_FOUND", "Event not found"))
|
||||
return@put
|
||||
}
|
||||
|
||||
val useCaseRequest = UpdateVeranstaltungUseCase.UpdateVeranstaltungRequest(
|
||||
veranstaltungId = eventId,
|
||||
val updatedVeranstaltung = existingEvent.copy(
|
||||
name = updateRequest.name,
|
||||
untertitel = updateRequest.untertitel,
|
||||
beschreibung = updateRequest.beschreibung,
|
||||
logoUrl = updateRequest.logoUrl,
|
||||
sponsoren = updateRequest.sponsoren,
|
||||
startDatum = updateRequest.startDatum,
|
||||
endDatum = updateRequest.endDatum,
|
||||
ort = updateRequest.ort,
|
||||
@@ -213,72 +162,46 @@ class VeranstaltungController(
|
||||
istOeffentlich = updateRequest.istOeffentlich,
|
||||
maxTeilnehmer = updateRequest.maxTeilnehmer,
|
||||
anmeldeschluss = updateRequest.anmeldeschluss
|
||||
)
|
||||
).withUpdatedTimestamp()
|
||||
|
||||
val response = updateVeranstaltungUseCase.execute(useCaseRequest)
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success((response.data as UpdateVeranstaltungUseCase.UpdateVeranstaltungResponse).veranstaltung))
|
||||
} else {
|
||||
val statusCode = when (response.error?.code) {
|
||||
"NOT_FOUND" -> HttpStatusCode.NotFound
|
||||
"VALIDATION_ERROR" -> HttpStatusCode.BadRequest
|
||||
"DOMAIN_VALIDATION_ERROR" -> HttpStatusCode.BadRequest
|
||||
else -> HttpStatusCode.InternalServerError
|
||||
}
|
||||
call.respond(statusCode, ApiResponse.error<Any>(response.error?.message ?: "Failed to update event"))
|
||||
val errors = updatedVeranstaltung.validate()
|
||||
if (errors.isNotEmpty()) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("VALIDATION_ERROR", "Validation failed: ${errors.joinToString(", ")}"))
|
||||
return@put
|
||||
}
|
||||
|
||||
val savedEvent = veranstaltungRepository.save(updatedVeranstaltung)
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success(savedEvent))
|
||||
} catch (_: IllegalArgumentException) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("Invalid event ID format"))
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("INVALID_ID", "Invalid event ID format"))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("Invalid request data: ${e.message}"))
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("INVALID_REQUEST", "Invalid request data: ${e.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/events/{id} - Delete event
|
||||
delete("/{id}") {
|
||||
try {
|
||||
val eventId = ApiValidationUtils.validateUuidString(call.parameters["id"])
|
||||
?: return@delete call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("Invalid event ID format")
|
||||
)
|
||||
|
||||
// Validate force parameter if provided
|
||||
val forceParam = call.request.queryParameters["force"]
|
||||
val forceDelete = if (forceParam != null) {
|
||||
try {
|
||||
forceParam.toBoolean()
|
||||
} catch (_: Exception) {
|
||||
return@delete call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("Invalid force parameter. Must be true or false")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val useCaseRequest = DeleteVeranstaltungUseCase.DeleteVeranstaltungRequest(
|
||||
veranstaltungId = eventId,
|
||||
forceDelete = forceDelete
|
||||
val eventIdString = call.parameters["id"] ?: return@delete call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("MISSING_ID", "Event ID is required")
|
||||
)
|
||||
|
||||
val response = deleteVeranstaltungUseCase.execute(useCaseRequest)
|
||||
|
||||
if (response.success) {
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success(response.data))
|
||||
} else {
|
||||
val statusCode = when (response.error?.code) {
|
||||
"NOT_FOUND" -> HttpStatusCode.NotFound
|
||||
"CANNOT_DELETE_ACTIVE_EVENT" -> HttpStatusCode.Conflict
|
||||
else -> HttpStatusCode.InternalServerError
|
||||
}
|
||||
call.respond(statusCode, ApiResponse.error<Any>(response.error?.message ?: "Failed to delete event"))
|
||||
val eventId = try { Uuid.parse(eventIdString) } catch (e: Exception) {
|
||||
return@delete call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("INVALID_ID", "Invalid event ID format")
|
||||
)
|
||||
}
|
||||
|
||||
val success = veranstaltungRepository.delete(eventId)
|
||||
if (success) {
|
||||
call.respond(HttpStatusCode.OK, ApiResponse.success("Event deleted successfully"))
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound, ApiResponse.error<Any>("NOT_FOUND", "Event not found"))
|
||||
}
|
||||
} catch (_: IllegalArgumentException) {
|
||||
call.respond(HttpStatusCode.BadRequest, ApiResponse.error<Any>("Invalid event ID format"))
|
||||
} catch (e: Exception) {
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("Failed to delete event: ${e.message}"))
|
||||
call.respond(HttpStatusCode.InternalServerError, ApiResponse.error<Any>("INTERNAL_ERROR", "Failed to delete event: ${e.message}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,7 +213,10 @@ class VeranstaltungController(
|
||||
@Serializable
|
||||
data class CreateEventRequest(
|
||||
val name: String,
|
||||
val untertitel: String? = null,
|
||||
val beschreibung: String? = null,
|
||||
val logoUrl: String? = null,
|
||||
val sponsoren: String? = null,
|
||||
val startDatum: LocalDate,
|
||||
val endDatum: LocalDate,
|
||||
val ort: String,
|
||||
@@ -309,7 +235,10 @@ class VeranstaltungController(
|
||||
@Serializable
|
||||
data class UpdateEventRequest(
|
||||
val name: String,
|
||||
val untertitel: String? = null,
|
||||
val beschreibung: String? = null,
|
||||
val logoUrl: String? = null,
|
||||
val sponsoren: String? = null,
|
||||
val startDatum: LocalDate,
|
||||
val endDatum: LocalDate,
|
||||
val ort: String,
|
||||
|
||||
Reference in New Issue
Block a user