Skip to content

Commit

Permalink
Implement carpool functionality in DriveOffersCommunicator
Browse files Browse the repository at this point in the history
  • Loading branch information
stevensolleder committed Dec 17, 2024
1 parent 8aca0f1 commit 386594d
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import de.uniflitzer.backend.applicationservices.communicators.version1.datapack
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.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
Expand Down Expand Up @@ -44,7 +46,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
Expand Down Expand Up @@ -79,6 +82,7 @@ private class DriveOffersCommunicator(
allowedDrivingStyles,
allowedGenders,
actingUser.blockedUsers,
actingUser.carpools,
Sort.by(
when (sortingDirection) {
SortingDirection.Ascending -> Sort.Direction.ASC
Expand All @@ -87,7 +91,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) }

Expand Down Expand Up @@ -146,7 +149,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,
Expand All @@ -157,7 +160,22 @@ private class DriveOffersCommunicator(
),
driveOfferCreation.plannedDepartureTime?.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.plannedDepartureTime?.let { ZonedDateTime.parse(it) },
targetedCarpool
)
}
}
selectedCarForDriverOffer.image?.let { actingUserCarImage -> newDriveOffer.car.image = imagesRepository.copy(actingUserCarImage) }
driveOffersRepository.save(newDriveOffer)
Expand Down Expand Up @@ -200,10 +218,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,
Expand All @@ -216,9 +235,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)

Expand All @@ -244,7 +270,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)

Expand All @@ -268,7 +294,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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<ErrorDP> {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).contentType(MediaType.APPLICATION_JSON).body(error.errorDP)
}

@ExceptionHandler(
InternalServerError::class,
Exception::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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())
8 changes: 7 additions & 1 deletion src/main/kotlin/de/uniflitzer/backend/model/DriveOffer.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<UserStop> = mutableListOf()
private var _passengers: MutableList<UserStop> = mutableListOf()
val passengers: List<UserStop> get() = _passengers

var plannedDeparture: ZonedDateTime? = plannedDeparture
Expand All @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -34,6 +35,7 @@ interface DriveOffersRepository: JpaRepository<DriveOffer, UUID> {
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(
Expand All @@ -42,6 +44,7 @@ interface DriveOffersRepository: JpaRepository<DriveOffer, UUID> {
@Param("allowedDrivingStyles") allowedDrivingStyles: List<DrivingStyle>?,
@Param("allowedGenders") allowedGenders: List<Gender>?,
@Param("blockedUsers") blockedUsers: List<User>,
@Param("allowedCarpools") allowedCarpools: List<Carpool>,
sort: Sort
): List<DriveOffer>
}

0 comments on commit 386594d

Please sign in to comment.