(fix) Umbau zu SCS
**Backend:** - Vervollständigen Sie alle Repository-Implementierungen - Implementieren Sie die Authentifizierung und Autorisierung - Fügen Sie Validierung für alle API-Endpunkte hinzu
This commit is contained in:
+81
-3
@@ -5,6 +5,8 @@ import at.mocode.events.application.usecase.*
|
||||
import at.mocode.events.domain.repository.VeranstaltungRepository
|
||||
import at.mocode.enums.SparteE
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import at.mocode.validation.ApiValidationUtils
|
||||
import at.mocode.validation.ValidationError
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuidFrom
|
||||
import io.ktor.http.*
|
||||
@@ -40,10 +42,32 @@ 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 { uuidFrom(it) }
|
||||
val organizerId = call.request.queryParameters["organizerId"]?.let {
|
||||
ApiValidationUtils.validateUuidString(it) ?: return@get call.respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
ApiResponse.error<Any>("Invalid organizerId format")
|
||||
)
|
||||
}
|
||||
val searchTerm = call.request.queryParameters["search"]
|
||||
val publicOnly = call.request.queryParameters["publicOnly"]?.toBoolean() ?: false
|
||||
val startDate = call.request.queryParameters["startDate"]?.let { LocalDate.parse(it) }
|
||||
@@ -104,6 +128,24 @@ class VeranstaltungController(
|
||||
post {
|
||||
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(
|
||||
name = createRequest.name,
|
||||
beschreibung = createRequest.beschreibung,
|
||||
@@ -140,6 +182,24 @@ class VeranstaltungController(
|
||||
try {
|
||||
val eventId = uuidFrom(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))
|
||||
)
|
||||
return@put
|
||||
}
|
||||
|
||||
val useCaseRequest = UpdateVeranstaltungUseCase.UpdateVeranstaltungRequest(
|
||||
veranstaltungId = eventId,
|
||||
name = updateRequest.name,
|
||||
@@ -178,8 +238,26 @@ class VeranstaltungController(
|
||||
// DELETE /api/events/{id} - Delete event
|
||||
delete("/{id}") {
|
||||
try {
|
||||
val eventId = uuidFrom(call.parameters["id"]!!)
|
||||
val forceDelete = call.request.queryParameters["force"]?.toBoolean() ?: false
|
||||
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 (e: 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
|
||||
|
||||
+24
-22
@@ -3,6 +3,8 @@ package at.mocode.events.infrastructure.repository
|
||||
import at.mocode.enums.SparteE
|
||||
import at.mocode.events.domain.model.Veranstaltung
|
||||
import at.mocode.events.domain.repository.VeranstaltungRepository
|
||||
import at.mocode.events.infrastructure.repository.VeranstaltungTable
|
||||
import at.mocode.shared.database.DatabaseFactory
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.LocalDate
|
||||
@@ -19,24 +21,24 @@ import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
*/
|
||||
class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): Veranstaltung? {
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.id eq id }
|
||||
override suspend fun findById(id: Uuid): Veranstaltung? = DatabaseFactory.dbQuery {
|
||||
VeranstaltungTable.selectAll().where { VeranstaltungTable.id eq id }
|
||||
.map { rowToVeranstaltung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<Veranstaltung> {
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.name like searchPattern }
|
||||
VeranstaltungTable.selectAll().where { VeranstaltungTable.name like searchPattern }
|
||||
.orderBy(VeranstaltungTable.startDatum, SortOrder.DESC)
|
||||
.limit(limit)
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): List<Veranstaltung> {
|
||||
override suspend fun findByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
|
||||
return if (activeOnly) {
|
||||
if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
@@ -44,13 +46,13 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByDateRange(startDate: LocalDate, endDate: LocalDate, activeOnly: Boolean): List<Veranstaltung> {
|
||||
override suspend fun findByDateRange(startDate: LocalDate, endDate: LocalDate, activeOnly: Boolean): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
val query = VeranstaltungTable.selectAll().where {
|
||||
(VeranstaltungTable.startDatum greaterEq startDate) and
|
||||
(VeranstaltungTable.endDatum lessEq endDate)
|
||||
}
|
||||
|
||||
return if (activeOnly) {
|
||||
if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
@@ -58,10 +60,10 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByStartDate(date: LocalDate, activeOnly: Boolean): List<Veranstaltung> {
|
||||
override suspend fun findByStartDate(date: LocalDate, activeOnly: Boolean): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.startDatum eq date }
|
||||
|
||||
return if (activeOnly) {
|
||||
if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
@@ -69,17 +71,17 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<Veranstaltung> {
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
.orderBy(VeranstaltungTable.startDatum, SortOrder.DESC)
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findPublicEvents(activeOnly: Boolean): List<Veranstaltung> {
|
||||
override suspend fun findPublicEvents(activeOnly: Boolean): List<Veranstaltung> = DatabaseFactory.dbQuery {
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.istOeffentlich eq true }
|
||||
|
||||
return if (activeOnly) {
|
||||
if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
@@ -87,7 +89,7 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(veranstaltung: Veranstaltung): Veranstaltung {
|
||||
override suspend fun save(veranstaltung: Veranstaltung): Veranstaltung = DatabaseFactory.dbQuery {
|
||||
val now = Clock.System.now()
|
||||
val updatedVeranstaltung = veranstaltung.copy(updatedAt = now)
|
||||
|
||||
@@ -96,7 +98,7 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
.where { VeranstaltungTable.id eq veranstaltung.veranstaltungId }
|
||||
.singleOrNull()
|
||||
|
||||
return if (existingRecord != null) {
|
||||
if (existingRecord != null) {
|
||||
// Update existing record
|
||||
VeranstaltungTable.update({ VeranstaltungTable.id eq veranstaltung.veranstaltungId }) {
|
||||
veranstaltungToStatement(it, updatedVeranstaltung)
|
||||
@@ -112,20 +114,20 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean {
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
val deletedRows = VeranstaltungTable.deleteWhere { VeranstaltungTable.id eq id }
|
||||
return deletedRows > 0
|
||||
deletedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
override suspend fun countActive(): Long = DatabaseFactory.dbQuery {
|
||||
VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): Long {
|
||||
override suspend fun countByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): Long = DatabaseFactory.dbQuery {
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
|
||||
return if (activeOnly) {
|
||||
if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
|
||||
Reference in New Issue
Block a user