diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveOffersCommunicator.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveOffersCommunicator.kt index 1c609f5..821ae28 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveOffersCommunicator.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveOffersCommunicator.kt @@ -4,13 +4,16 @@ import de.uniflitzer.backend.applicationservices.authentication.UserToken import de.uniflitzer.backend.applicationservices.communicators.version1.datapackages.* import de.uniflitzer.backend.applicationservices.communicators.version1.documentationinformationadder.apiresponses.* import de.uniflitzer.backend.applicationservices.communicators.version1.errors.ForbiddenError +import de.uniflitzer.backend.applicationservices.communicators.version1.errors.InternalServerError import de.uniflitzer.backend.applicationservices.communicators.version1.errors.NotFoundError +import de.uniflitzer.backend.applicationservices.communicators.version1.errors.UnprocessableContentError import de.uniflitzer.backend.applicationservices.communicators.version1.valuechecker.UUID import de.uniflitzer.backend.applicationservices.geography.GeographyService import de.uniflitzer.backend.model.* import de.uniflitzer.backend.model.errors.MissingActionError import de.uniflitzer.backend.model.errors.NotAvailableError import de.uniflitzer.backend.model.errors.RepeatedActionError +import de.uniflitzer.backend.repositories.CarpoolsRepository import de.uniflitzer.backend.repositories.DriveOffersRepository import de.uniflitzer.backend.repositories.ImagesRepository import de.uniflitzer.backend.repositories.UsersRepository @@ -44,7 +47,8 @@ private class DriveOffersCommunicator( @field:Autowired private val driveOffersRepository: DriveOffersRepository, @field:Autowired private val usersRepository: UsersRepository, @field:Autowired private val geographyService: GeographyService, - @field:Autowired private val imagesRepository: ImagesRepository + @field:Autowired private val imagesRepository: ImagesRepository, + @field:Autowired private val carpoolsRepository: CarpoolsRepository, ) { @Operation(description = "Get all drive offers.") @CommonApiResponses @OkApiResponse @@ -79,6 +83,7 @@ private class DriveOffersCommunicator( allowedDrivingStyles, allowedGenders, actingUser.blockedUsers, + actingUser.carpools, Sort.by( when (sortingDirection) { SortingDirection.Ascending -> Sort.Direction.ASC @@ -87,7 +92,6 @@ private class DriveOffersCommunicator( DriveOffer::plannedDeparture.name ) ) - //TODO: CarpoolDriveOffers .filter { it.route.isCoordinateOnRoute(startCoordinate, tolerance) && it.route.isCoordinateOnRoute(destinationCoordinate, tolerance) } .filter { it.route.areCoordinatesInCorrectDirection(startCoordinate, destinationCoordinate) } @@ -146,7 +150,7 @@ private class DriveOffersCommunicator( val selectedCarForDriverOffer: Car = actingUser.cars.getOrNull(driveOfferCreation.carIndex) ?: throw NotFoundError(ErrorDP("The car with the index ${driveOfferCreation.carIndex} could not be found.")) val newDriveOffer: DriveOffer = when (driveOfferCreation) { - is PublicDriveOfferCreationDP -> + is PublicDriveOfferCreationDP -> { PublicDriveOffer( actingUser, selectedCarForDriverOffer, @@ -155,9 +159,24 @@ private class DriveOffersCommunicator( geographyService.createPosition(driveOfferCreation.route.start.toCoordinate()), geographyService.createPosition(driveOfferCreation.route.destination.toCoordinate()) ), - driveOfferCreation.plannedDepartureTime?.let { ZonedDateTime.parse(it) } + driveOfferCreation.plannedDeparture?.let { ZonedDateTime.parse(it) } ) - is CarpoolDriveOfferCreationDP -> TODO() + } + is CarpoolDriveOfferCreationDP -> { + val targetedCarpool: Carpool = carpoolsRepository.findById(UUIDType.fromString(driveOfferCreation.carpoolId)).getOrNull() ?: throw NotFoundError(ErrorDP("The carpool with the id ${driveOfferCreation.carpoolId} could not be found.")) + + CarpoolDriveOffer( + actingUser, + selectedCarForDriverOffer, + Seats(driveOfferCreation.freeSeats.toUInt()), + geographyService.createRoute( + geographyService.createPosition(driveOfferCreation.route.start.toCoordinate()), + geographyService.createPosition(driveOfferCreation.route.destination.toCoordinate()) + ), + driveOfferCreation.plannedDeparture?.let { ZonedDateTime.parse(it) }, + targetedCarpool + ) + } } selectedCarForDriverOffer.image?.let { actingUserCarImage -> newDriveOffer.car.image = imagesRepository.copy(actingUserCarImage) } driveOffersRepository.save(newDriveOffer) @@ -187,7 +206,7 @@ private class DriveOffersCommunicator( val driveOfferInEditing: DriveOffer = driveOffersRepository.findById(UUIDType.fromString(id)).getOrNull() ?: throw NotFoundError(ErrorDP("The drive offer with the id $id could not be found.")) if (driveOfferInEditing.driver.id.toString() != userToken.id) throw ForbiddenError(ErrorDP("The user with the id $id is not the driver of the drive offer with the id $id.")) - driveOfferInEditing.plannedDeparture = ZonedDateTime.parse(driveOfferUpdate.plannedDepartureTime) + driveOfferInEditing.plannedDeparture = ZonedDateTime.parse(driveOfferUpdate.plannedDeparture) driveOffersRepository.save(driveOfferInEditing) return ResponseEntity.noContent().build() @@ -200,10 +219,11 @@ private class DriveOffersCommunicator( val actingUser: User = usersRepository.findById(UUIDType.fromString(userToken.id)).getOrNull() ?: throw ForbiddenError(ErrorDP("User with id ${userToken.id} does not exist in resource server.")) val driveOfferInEditing: DriveOffer = driveOffersRepository.findById(UUIDType.fromString(id)).getOrNull() ?: throw NotFoundError(ErrorDP("The drive offer with the id $id could not be found.")) if(driveOfferInEditing.driver.id == actingUser.id) throw ForbiddenError(ErrorDP("The requesting user cannot be the driver of the same drive offer.")) - if(actingUser in driveOfferInEditing.driver.blockedUsers) throw ForbiddenError(ErrorDP("The requesting user ${actingUser.id} is blocked by the driver and cannot request a seat in this drive offer.")) when (driveOfferInEditing) { is PublicDriveOffer -> { + if(actingUser in driveOfferInEditing.driver.blockedUsers) throw ForbiddenError(ErrorDP("The requesting user ${actingUser.id} is blocked by the driver and cannot request a seat in this drive offer.")) + try { driveOfferInEditing.addRequestFromUser( actingUser, @@ -216,9 +236,16 @@ private class DriveOffersCommunicator( } catch (_: RepeatedActionError) { throw ForbiddenError(ErrorDP("The user with the id ${actingUser.id} has already requested a seat in the drive offer with the id $id.")) } - } - is CarpoolDriveOffer -> TODO() + is CarpoolDriveOffer -> { + driveOfferInEditing.addPassenger( + UserStop( + actingUser, + geographyService.createPosition(userStopCreation.start.toCoordinate()), + geographyService.createPosition(userStopCreation.destination.toCoordinate()) + ) + ) + } } driveOffersRepository.save(driveOfferInEditing) @@ -226,7 +253,7 @@ private class DriveOffersCommunicator( } @Operation(description = "Accept a requesting user for a specific drive offer.") - @CommonApiResponses @NoContentApiResponse @NotFoundApiResponse + @CommonApiResponses @UnprocessableContentApiResponse @NoContentApiResponse @NotFoundApiResponse @PostMapping("{driveOfferId}/requesting-users/{requestingUserId}/acceptances") fun acceptRequestingUser(@PathVariable @UUID driveOfferId: String, @PathVariable @UUID requestingUserId: String, userToken: UserToken):ResponseEntity { val actingUser: User = usersRepository.findById(UUIDType.fromString(userToken.id)).getOrNull() ?: throw ForbiddenError(ErrorDP("User with id ${userToken.id} does not exist in resource server.")) @@ -244,7 +271,7 @@ private class DriveOffersCommunicator( throw ForbiddenError(ErrorDP("No free seats left in the drive offer with the id $driveOfferId.")) } } - is CarpoolDriveOffer -> TODO() + is CarpoolDriveOffer -> throw UnprocessableContentError(ErrorDP("The drive offer with the ID $driveOfferId is a carpool drive offer, so requests are automatically accepted.")) } driveOffersRepository.save(driveOfferInEditing) @@ -252,7 +279,7 @@ private class DriveOffersCommunicator( } @Operation(description = "Reject a requesting user for a specific drive offer") - @CommonApiResponses @NoContentApiResponse @NotFoundApiResponse + @CommonApiResponses @UnprocessableContentApiResponse @NoContentApiResponse @NotFoundApiResponse @PostMapping("{driveOfferId}/requesting-users/{requestingUserId}/rejections") fun rejectRequestingUser(@PathVariable @UUID driveOfferId: String, @PathVariable @UUID requestingUserId: String, userToken: UserToken):ResponseEntity { val actingUser: User = usersRepository.findById(UUIDType.fromString(userToken.id)).getOrNull() ?: throw ForbiddenError(ErrorDP("User with id ${userToken.id} does not exist in resource server.")) @@ -268,7 +295,7 @@ private class DriveOffersCommunicator( throw NotFoundError(ErrorDP("The requesting user with the id $requestingUserId could not be found in the drive offer with the id $driveOfferId.")) } } - is CarpoolDriveOffer -> TODO() + is CarpoolDriveOffer -> throw UnprocessableContentError(ErrorDP("The drive offer with the ID $driveOfferId is a carpool drive offer, so requests are automatically accepted.")) } driveOffersRepository.save(driveOfferInEditing) diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveRequestsCommunicator.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveRequestsCommunicator.kt index c5262ad..4f57f75 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveRequestsCommunicator.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/DriveRequestsCommunicator.kt @@ -3,9 +3,7 @@ package de.uniflitzer.backend.applicationservices.communicators.version1 import de.uniflitzer.backend.applicationservices.authentication.UserToken import de.uniflitzer.backend.applicationservices.communicators.version1.datapackages.* import de.uniflitzer.backend.applicationservices.communicators.version1.documentationinformationadder.apiresponses.* -import de.uniflitzer.backend.applicationservices.communicators.version1.errors.ForbiddenError -import de.uniflitzer.backend.applicationservices.communicators.version1.errors.InternalServerError -import de.uniflitzer.backend.applicationservices.communicators.version1.errors.NotFoundError +import de.uniflitzer.backend.applicationservices.communicators.version1.errors.* import de.uniflitzer.backend.applicationservices.communicators.version1.valuechecker.UUID import de.uniflitzer.backend.applicationservices.geography.GeographyService import de.uniflitzer.backend.model.* @@ -181,7 +179,7 @@ private class DriveRequestsCommunicator( } @Operation(description = "Create a new drive offer for a specific drive request. The drive request is either deleted if it's a CarpoolDriveRequest or its drive offers list is updated if it's a PublicDriveRequest.") - @CommonApiResponses @CreatedApiResponse @NotFoundApiResponse + @CommonApiResponses @UnprocessableContentApiResponse @CreatedApiResponse @NotFoundApiResponse @PostMapping("{id}/drive-offers") fun createDriveOfferForDriveRequest(@PathVariable @UUID id:String, @RequestBody @Valid driveOfferCreation: DriveOfferCreationDP, userToken: UserToken): ResponseEntity { @@ -204,7 +202,7 @@ private class DriveRequestsCommunicator( car, Seats(driveOfferCreation.freeSeats.toUInt()), geographyService.createRoute(geographyService.createPosition(driveOfferCreation.route.start.toCoordinate()), geographyService.createPosition(driveOfferCreation.route.destination.toCoordinate())), - driveOfferCreation.plannedDepartureTime?.let { ZonedDateTime.parse(it) }, + driveOfferCreation.plannedDeparture?.let { ZonedDateTime.parse(it) }, carpoolsRepository.findById(UUIDType.fromString(driveOfferCreation.carpoolId)).getOrNull() ?: throw NotFoundError(ErrorDP("Carpool with id ${driveOfferCreation.carpoolId} not found.")) ) car.image?.let { driveOffer.car.image = imagesRepository.copy(it) } @@ -212,13 +210,13 @@ private class DriveRequestsCommunicator( driveOffersRepository.saveAndFlush(driveOffer) driveRequestsRepository.delete(driveRequest) } - is PublicDriveOfferCreationDP -> { throw ForbiddenError(ErrorDP("PublicDriveOffer creation is not allowed for CarpoolDriveRequests.")) } + is PublicDriveOfferCreationDP -> { throw UnprocessableContentError(ErrorDP("PublicDriveOffer creation is not allowed for CarpoolDriveRequests.")) } } } is PublicDriveRequest -> { when (driveOfferCreation) { - is CarpoolDriveOfferCreationDP -> { throw ForbiddenError(ErrorDP("CarpoolDriveOffer creation is not allowed for PublicDriveRequests.")) } + is CarpoolDriveOfferCreationDP -> { throw UnprocessableContentError(ErrorDP("CarpoolDriveOffer creation is not allowed for PublicDriveRequests.")) } is PublicDriveOfferCreationDP -> { val car:Car = try{ user.getCarByIndex(driveOfferCreation.carIndex) } catch(error:NotAvailableError){ throw NotFoundError(ErrorDP(error.message!!)) } driveOffer = PublicDriveOffer( @@ -226,7 +224,7 @@ private class DriveRequestsCommunicator( car, Seats(driveOfferCreation.freeSeats.toUInt()), geographyService.createRoute(geographyService.createPosition(driveOfferCreation.route.start.toCoordinate()), geographyService.createPosition(driveOfferCreation.route.destination.toCoordinate())), - driveOfferCreation.plannedDepartureTime?.let { ZonedDateTime.parse(it) } + driveOfferCreation.plannedDeparture?.let { ZonedDateTime.parse(it) } ) car.image?.let { driveOffer.car.image = imagesRepository.copy(it) } @@ -243,7 +241,7 @@ private class DriveRequestsCommunicator( } @Operation(description = "This endpoint is only allowed to use on a PublicRequestRequest. Reject a specific drive offer for a specific drive request. Neither the drive request nor the drive offer is deleted so other users can still see them.") - @CommonApiResponses @NoContentApiResponse @NotFoundApiResponse + @CommonApiResponses @UnprocessableContentApiResponse @NoContentApiResponse @NotFoundApiResponse @PostMapping("{driveRequestId}/drive-offers/{driveOfferId}/rejections") fun rejectDriveOffer(@PathVariable @UUID driveRequestId:String, @PathVariable @UUID driveOfferId:String, userToken: UserToken): ResponseEntity { @@ -254,7 +252,7 @@ private class DriveRequestsCommunicator( when(driveRequest) { - is CarpoolDriveRequest -> { throw ForbiddenError(ErrorDP("DriveOffers for CarpoolDriveRequests are automatically accepted.")) } + is CarpoolDriveRequest -> { throw UnprocessableContentError(ErrorDP("DriveOffers for CarpoolDriveRequests are automatically accepted.")) } is PublicDriveRequest -> { try { driveRequest.rejectDriveOffer(UUIDType.fromString(driveOfferId)) } @@ -268,7 +266,7 @@ private class DriveRequestsCommunicator( } @Operation(description = "This endpoint is only allowed to use on a PublicRequestRequest. Accept a specific drive offer for a specific drive request. The requesting user of the drive request is automatically accepted as a passenger and the drive request is deleted.") - @CommonApiResponses @NoContentApiResponse @NotFoundApiResponse + @CommonApiResponses @UnprocessableContentApiResponse @NoContentApiResponse @NotFoundApiResponse @PostMapping("{driveRequestId}/drive-offers/{driveOfferId}/acceptances") fun acceptDriveOffer(@PathVariable @UUID driveRequestId:String, @PathVariable @UUID driveOfferId:String, userToken: UserToken): ResponseEntity { @@ -278,7 +276,7 @@ private class DriveRequestsCommunicator( if(driveRequest.requestingUser.id != UUIDType.fromString(userToken.id)) throw ForbiddenError(ErrorDP("UserToken id does not match the requesting user id of the drive request.")) when(driveRequest) { - is CarpoolDriveRequest -> { throw ForbiddenError(ErrorDP("DriveOffers for CarpoolDriveRequests are automatically accepted.")) } + is CarpoolDriveRequest -> { throw UnprocessableContentError(ErrorDP("DriveOffers for CarpoolDriveRequests are automatically accepted.")) } is PublicDriveRequest -> { try { driveRequest.acceptDriveOffer(UUIDType.fromString(driveOfferId)) } catch (entityNotFoundError: EntityNotFoundError) { throw NotFoundError(ErrorDP(entityNotFoundError.message!!)) } diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/ErrorsCommunicator.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/ErrorsCommunicator.kt index 859d48d..5774342 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/ErrorsCommunicator.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/ErrorsCommunicator.kt @@ -11,6 +11,7 @@ import de.uniflitzer.backend.applicationservices.communicators.version1.errors.F import de.uniflitzer.backend.applicationservices.communicators.version1.errors.InternalServerError import de.uniflitzer.backend.applicationservices.communicators.version1.errors.NotFoundError import de.uniflitzer.backend.applicationservices.communicators.version1.errors.StompError +import de.uniflitzer.backend.applicationservices.communicators.version1.errors.UnprocessableContentError import jakarta.servlet.http.HttpServletRequest import jakarta.validation.ConstraintViolationException import org.slf4j.LoggerFactory @@ -99,6 +100,11 @@ private class ErrorsCommunicator( return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).contentType(MediaType.APPLICATION_JSON).body(ErrorDP("Method not supported on this endpoint.")) } + @ExceptionHandler(UnprocessableContentError::class) + fun handleMethodNotSupportedErrors(error: UnprocessableContentError): ResponseEntity { + return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).contentType(MediaType.APPLICATION_JSON).body(error.errorDP) + } + @ExceptionHandler( InternalServerError::class, Exception::class diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/CarpoolDriveOfferCreationDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/CarpoolDriveOfferCreationDP.kt index c83c894..c567d70 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/CarpoolDriveOfferCreationDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/CarpoolDriveOfferCreationDP.kt @@ -6,6 +6,6 @@ class CarpoolDriveOfferCreationDP private constructor( carIndex: Int, freeSeats: Int, route: RouteCreationDP, - plannedDepartureTime: String?, + plannedDeparture: String?, @field:UUID val carpoolId: String -) : DriveOfferCreationDP(carIndex, freeSeats, route, plannedDepartureTime) \ No newline at end of file +) : DriveOfferCreationDP(carIndex, freeSeats, route, plannedDeparture) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedCarpoolDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedCarpoolDriveOfferDP.kt index 121ce84..49111a0 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedCarpoolDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedCarpoolDriveOfferDP.kt @@ -2,7 +2,7 @@ package de.uniflitzer.backend.applicationservices.communicators.version1.datapac import jakarta.validation.Valid -class DetailedCarpoolDriveOfferDP private constructor( +class DetailedCarpoolDriveOfferDP constructor( containsFavoriteDriver: Boolean, id: String, driver: PartialUserDP, @@ -10,6 +10,6 @@ class DetailedCarpoolDriveOfferDP private constructor( freeSeats: Int, route: RouteDP, passengers: List, - plannedDepartureTime: String?, + plannedDeparture: String?, @field:Valid val carpool: PartialCarpoolDP, -): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDepartureTime) \ No newline at end of file +): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDeparture) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedDriveOfferDP.kt index 287d2e7..86ef895 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedDriveOfferDP.kt @@ -28,7 +28,7 @@ sealed class DetailedDriveOfferDP( @field:Min(1) @field:Max(8) val freeSeats: Int, @field:Valid val route: RouteDP, @field:Valid val passengers: List, - @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) open val plannedDepartureTime: String? + @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) open val plannedDeparture: String? ) { companion object { fun fromDriveOffer(driveOffer: DriveOffer, containsFavoriteDriver: Boolean): DetailedDriveOfferDP { @@ -45,7 +45,18 @@ sealed class DetailedDriveOfferDP( driveOffer.plannedDeparture?.toString(), driveOffer.requestingUsers.map { UserStopDP.fromUserStop(it) } ) - is CarpoolDriveOffer -> TODO() + is CarpoolDriveOffer -> + DetailedCarpoolDriveOfferDP( + containsFavoriteDriver, + driveOffer.id.toString(), + PartialUserDP.fromUser(driveOffer.driver), + CarDP.fromCar(driveOffer.car), + driveOffer.freeSeats.value.toInt(), + RouteDP.fromRoute(driveOffer.route), + driveOffer.passengers.map { UserStopDP.fromUserStop(it) }, + driveOffer.plannedDeparture?.toString(), + PartialCarpoolDP.fromCarpool(driveOffer.carpool) + ) else -> throw IllegalArgumentException("Unknown DriveOffer type") } } diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedPublicDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedPublicDriveOfferDP.kt index d9ce379..ac97a0b 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedPublicDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DetailedPublicDriveOfferDP.kt @@ -10,6 +10,6 @@ class DetailedPublicDriveOfferDP constructor( freeSeats: Int, route: RouteDP, passengers: List, - plannedDepartureTime: String?, + plannedDeparture: String?, @field:Valid val requestingUsers: List? -): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDepartureTime) \ No newline at end of file +): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDeparture) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriveOfferCreationDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriveOfferCreationDP.kt index a6eb74a..bb82082 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriveOfferCreationDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriveOfferCreationDP.kt @@ -20,5 +20,5 @@ sealed class DriveOfferCreationDP( @field:Min(0) val carIndex: Int, @field:Min(1) @field:Max(8) val freeSeats: Int, @field:Valid val route: RouteCreationDP, - @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDepartureTime: String?, + @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDeparture: String?, ) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriverOfferUpdateDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriverOfferUpdateDP.kt index cb73f0f..a90f5f3 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriverOfferUpdateDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/DriverOfferUpdateDP.kt @@ -6,5 +6,5 @@ import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.Pattern data class DriverOfferUpdateDP private constructor( - @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDepartureTime: String, + @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDeparture: String, ) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialCarpoolDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialCarpoolDriveOfferDP.kt index afb1c51..14dd00b 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialCarpoolDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialCarpoolDriveOfferDP.kt @@ -10,9 +10,9 @@ class PartialCarpoolDriveOfferDP ( freeSeats: Int, route: RouteDP, passengersCount: Int, - plannedDepartureTime: String?, + plannedDeparture: String?, @field:UUID val carpoolId: String, -): PartialDriveOfferDP(containsFavoriteDriver, id, driver, freeSeats, route, passengersCount, plannedDepartureTime) { +): PartialDriveOfferDP(containsFavoriteDriver, id, driver, freeSeats, route, passengersCount, plannedDeparture) { companion object { fun fromCarpoolDriveOffer(carpoolDriveOffer: CarpoolDriveOffer, containsFavoriteDriver: Boolean): PartialCarpoolDriveOfferDP = PartialCarpoolDriveOfferDP( diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialDriveOfferDP.kt index 6d33224..eb33f71 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialDriveOfferDP.kt @@ -28,7 +28,7 @@ sealed class PartialDriveOfferDP( @field:Min(1) @field:Max(8) val freeSeats: Int, @field:Valid val route: RouteDP, @field:Min(1) @field:Max(8) val passengersCount: Int, - @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDepartureTime: String? + @field:Pattern(regexp = DateTimeFormat) @field:Schema(example = DateTimeFormatExample) val plannedDeparture: String? ) { companion object { fun fromDriveOffer(driveOffer: DriveOffer, containsFavoriteDriver: Boolean): PartialDriveOfferDP { @@ -44,7 +44,17 @@ sealed class PartialDriveOfferDP( driveOffer.plannedDeparture?.toString(), driveOffer.requestingUsers.map { it.user.id.toString() } ) - is CarpoolDriveOffer -> TODO() + is CarpoolDriveOffer -> + PartialCarpoolDriveOfferDP( + containsFavoriteDriver, + driveOffer.id.toString(), + PartialUserDP.fromUser(driveOffer.driver), + driveOffer.freeSeats.value.toInt(), + RouteDP.fromRoute(driveOffer.route), + driveOffer.passengers.size, + driveOffer.plannedDeparture?.toString(), + driveOffer.carpool.id.toString() + ) else -> throw IllegalArgumentException("Unknown DriveOffer type") } } diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialPublicDriveOfferDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialPublicDriveOfferDP.kt index 1e46869..455df2f 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialPublicDriveOfferDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PartialPublicDriveOfferDP.kt @@ -10,9 +10,9 @@ class PartialPublicDriveOfferDP constructor( freeSeats: Int, route: RouteDP, passengersCount: Int, - plannedDepartureTime: String?, + plannedDeparture: String?, @field:Size(min = 0) val requestingUserIds: List? -): PartialDriveOfferDP(containsFavoriteDriver, id, driver, freeSeats, route, passengersCount, plannedDepartureTime) { +): PartialDriveOfferDP(containsFavoriteDriver, id, driver, freeSeats, route, passengersCount, plannedDeparture) { companion object { fun fromPublicDriveOffer(publicDriveOffer: PublicDriveOffer, containsFavoriteDriver: Boolean): PartialPublicDriveOfferDP { return PartialPublicDriveOfferDP( diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PublicDriveOfferCreationDP.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PublicDriveOfferCreationDP.kt index 833dae2..a77be6c 100644 --- a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PublicDriveOfferCreationDP.kt +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/datapackages/PublicDriveOfferCreationDP.kt @@ -4,5 +4,5 @@ class PublicDriveOfferCreationDP private constructor( carIndex: Int, freeSeats: Int, route: RouteCreationDP, - plannedDepartureTime: String?, -) : DriveOfferCreationDP(carIndex, freeSeats, route, plannedDepartureTime) \ No newline at end of file + plannedDeparture: String?, +) : DriveOfferCreationDP(carIndex, freeSeats, route, plannedDeparture) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/documentationinformationadder/apiresponses/UnprocessableContentApiResponse.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/documentationinformationadder/apiresponses/UnprocessableContentApiResponse.kt new file mode 100644 index 0000000..c371568 --- /dev/null +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/documentationinformationadder/apiresponses/UnprocessableContentApiResponse.kt @@ -0,0 +1,13 @@ +package de.uniflitzer.backend.applicationservices.communicators.version1.documentationinformationadder.apiresponses + +import de.uniflitzer.backend.applicationservices.communicators.version1.datapackages.ErrorDP +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse + +@Target(AnnotationTarget.FUNCTION) +@ApiResponse( + responseCode = "422", + content = [Content(schema = Schema(implementation = ErrorDP::class))] +) +annotation class UnprocessableContentApiResponse \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/errors/UnprocessableContentError.kt b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/errors/UnprocessableContentError.kt new file mode 100644 index 0000000..19c7c26 --- /dev/null +++ b/src/main/kotlin/de/uniflitzer/backend/applicationservices/communicators/version1/errors/UnprocessableContentError.kt @@ -0,0 +1,5 @@ +package de.uniflitzer.backend.applicationservices.communicators.version1.errors + +import de.uniflitzer.backend.applicationservices.communicators.version1.datapackages.ErrorDP + +class UnprocessableContentError(val errorDP: ErrorDP): HttpClientError(422, "Unprocessable Content", errorDP.toString()) \ No newline at end of file diff --git a/src/main/kotlin/de/uniflitzer/backend/model/DriveOffer.kt b/src/main/kotlin/de/uniflitzer/backend/model/DriveOffer.kt index 984fb54..dd0ae0b 100644 --- a/src/main/kotlin/de/uniflitzer/backend/model/DriveOffer.kt +++ b/src/main/kotlin/de/uniflitzer/backend/model/DriveOffer.kt @@ -1,6 +1,8 @@ package de.uniflitzer.backend.model +import de.uniflitzer.backend.model.errors.MissingActionError import de.uniflitzer.backend.model.errors.NotAvailableError +import de.uniflitzer.backend.model.errors.RepeatedActionError import jakarta.persistence.* import java.util.UUID import java.time.ZonedDateTime @@ -24,7 +26,7 @@ class DriveOffer(driver: User, car: Car, freeSeats: Seats, route: Route, planned var route: Route = route @field:ElementCollection - protected var _passengers: MutableList = mutableListOf() + private var _passengers: MutableList = mutableListOf() val passengers: List get() = _passengers var plannedDeparture: ZonedDateTime? = plannedDeparture @@ -38,6 +40,10 @@ class DriveOffer(driver: User, car: Car, freeSeats: Seats, route: Route, planned } fun addPassenger(userStop: UserStop) { + if (userStop.user in _passengers.map { it.user }) throw RepeatedActionError("User is already a passenger of this drive offer") + if (userStop.user == driver) throw MissingActionError("Driver cannot be a passenger of a drive offer") + if (freeSeats.value == _passengers.count().toUInt()) throw NotAvailableError("No more seats available for this drive offer") + _passengers.add(userStop) } diff --git a/src/main/kotlin/de/uniflitzer/backend/model/PublicDriveOffer.kt b/src/main/kotlin/de/uniflitzer/backend/model/PublicDriveOffer.kt index 8eb2893..ec932fd 100644 --- a/src/main/kotlin/de/uniflitzer/backend/model/PublicDriveOffer.kt +++ b/src/main/kotlin/de/uniflitzer/backend/model/PublicDriveOffer.kt @@ -20,14 +20,13 @@ class PublicDriveOffer(driver: User, car: Car, freeSeats: Seats, route: Route, p if (passengers.size.toUInt() >= freeSeats.value) throw NotAvailableError("No free seats left") if(requestingUsers.any { it.user.id == user.id }) throw RepeatedActionError("User already requested a seat") - _passengers.add(UserStop(user, start, destination)) + super.addPassenger(UserStop(user, start, destination)) } @Throws(MissingActionError::class, NotAvailableError::class) fun acceptRequestFromUser(userId: UUID) { val userStop = requestingUsers.find { it.user.id == userId } ?: throw MissingActionError("User did not request a seat") - if (passengers.size.toUInt() >= freeSeats.value) throw NotAvailableError("No free seats left") - _passengers.add(userStop) + super.addPassenger(userStop) _requestingUsers.remove(userStop) } diff --git a/src/main/kotlin/de/uniflitzer/backend/repositories/DriveOffersRepository.kt b/src/main/kotlin/de/uniflitzer/backend/repositories/DriveOffersRepository.kt index c4f6640..6da6c43 100644 --- a/src/main/kotlin/de/uniflitzer/backend/repositories/DriveOffersRepository.kt +++ b/src/main/kotlin/de/uniflitzer/backend/repositories/DriveOffersRepository.kt @@ -1,6 +1,7 @@ package de.uniflitzer.backend.repositories import de.uniflitzer.backend.model.Animal +import de.uniflitzer.backend.model.Carpool import de.uniflitzer.backend.model.Coordinate import de.uniflitzer.backend.model.DriveOffer import de.uniflitzer.backend.model.DrivingStyle @@ -34,6 +35,7 @@ interface DriveOffersRepository: JpaRepository { AND (:allowedDrivingStyles IS NULL OR driveOffer.driver.drivingStyle IN :allowedDrivingStyles) AND (:allowedGenders IS NULL OR driveOffer.driver.gender IN :allowedGenders) AND driveOffer.driver NOT IN :blockedUsers + AND (TYPE(driveOffer) <> CarpoolDriveOffer OR driveOffer IN (SELECT carpoolDriveOffer FROM CarpoolDriveOffer carpoolDriveOffer WHERE carpoolDriveOffer.carpool IN :allowedCarpools)) """ ) fun findAll( @@ -42,6 +44,7 @@ interface DriveOffersRepository: JpaRepository { @Param("allowedDrivingStyles") allowedDrivingStyles: List?, @Param("allowedGenders") allowedGenders: List?, @Param("blockedUsers") blockedUsers: List, + @Param("allowedCarpools") allowedCarpools: List, sort: Sort ): List } \ No newline at end of file