Skip to content

Commit

Permalink
Merge pull request #23 from branch drive-offers-communicator-adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
stevensolleder authored Dec 18, 2024
2 parents 8aca0f1 + d4dc8f8 commit fd8dc45
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -79,6 +83,7 @@ private class DriveOffersCommunicator(
allowedDrivingStyles,
allowedGenders,
actingUser.blockedUsers,
actingUser.carpools,
Sort.by(
when (sortingDirection) {
SortingDirection.Ascending -> Sort.Direction.ASC
Expand All @@ -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) }

Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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,
Expand All @@ -216,17 +236,24 @@ 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)

return ResponseEntity.noContent().build()
}

@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<Void> {
val actingUser: User = usersRepository.findById(UUIDType.fromString(userToken.id)).getOrNull() ?: throw ForbiddenError(ErrorDP("User with id ${userToken.id} does not exist in resource server."))
Expand All @@ -244,15 +271,15 @@ 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)

return ResponseEntity.noContent().build()
}

@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<Void> {
val actingUser: User = usersRepository.findById(UUIDType.fromString(userToken.id)).getOrNull() ?: throw ForbiddenError(ErrorDP("User with id ${userToken.id} does not exist in resource server."))
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -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<IdDP>
{
Expand All @@ -204,29 +202,29 @@ 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) }

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(
user,
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) }

Expand All @@ -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<Void>
{
Expand All @@ -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)) }
Expand All @@ -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<Void>
{
Expand All @@ -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!!)) }
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 @@ -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)
) : DriveOfferCreationDP(carIndex, freeSeats, route, plannedDeparture)
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ 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,
car: CarDP,
freeSeats: Int,
route: RouteDP,
passengers: List<UserStopDP>,
plannedDepartureTime: String?,
plannedDeparture: String?,
@field:Valid val carpool: PartialCarpoolDP,
): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDepartureTime)
): DetailedDriveOfferDP(containsFavoriteDriver, id, driver, car, freeSeats, route, passengers, plannedDeparture)
Loading

0 comments on commit fd8dc45

Please sign in to comment.