Make amenities global and super-admin managed
All checks were successful
build-and-deploy / build-deploy (push) Successful in 27s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 27s
This commit is contained in:
@@ -1,11 +1,9 @@
|
|||||||
package com.android.trisolarisserver.controller
|
package com.android.trisolarisserver.controller
|
||||||
|
|
||||||
import com.android.trisolarisserver.component.PropertyAccess
|
|
||||||
import com.android.trisolarisserver.controller.dto.AmenityResponse
|
import com.android.trisolarisserver.controller.dto.AmenityResponse
|
||||||
import com.android.trisolarisserver.controller.dto.AmenityUpsertRequest
|
import com.android.trisolarisserver.controller.dto.AmenityUpsertRequest
|
||||||
import com.android.trisolarisserver.models.property.Role
|
|
||||||
import com.android.trisolarisserver.models.room.RoomAmenity
|
import com.android.trisolarisserver.models.room.RoomAmenity
|
||||||
import com.android.trisolarisserver.repo.PropertyRepo
|
import com.android.trisolarisserver.repo.AppUserRepo
|
||||||
import com.android.trisolarisserver.repo.RoomAmenityRepo
|
import com.android.trisolarisserver.repo.RoomAmenityRepo
|
||||||
import com.android.trisolarisserver.security.MyPrincipal
|
import com.android.trisolarisserver.security.MyPrincipal
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
@@ -23,43 +21,32 @@ import org.springframework.web.server.ResponseStatusException
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/properties/{propertyId}/amenities")
|
@RequestMapping("/amenities")
|
||||||
class RoomAmenities(
|
class RoomAmenities(
|
||||||
private val propertyAccess: PropertyAccess,
|
|
||||||
private val roomAmenityRepo: RoomAmenityRepo,
|
private val roomAmenityRepo: RoomAmenityRepo,
|
||||||
private val propertyRepo: PropertyRepo
|
private val appUserRepo: AppUserRepo
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
fun listAmenities(
|
fun listAmenities(
|
||||||
@PathVariable propertyId: UUID,
|
|
||||||
@AuthenticationPrincipal principal: MyPrincipal?
|
@AuthenticationPrincipal principal: MyPrincipal?
|
||||||
): List<AmenityResponse> {
|
): List<AmenityResponse> {
|
||||||
requirePrincipal(principal)
|
requirePrincipal(principal)
|
||||||
propertyAccess.requireMember(propertyId, principal!!.userId)
|
return roomAmenityRepo.findAllByOrderByName().map { it.toResponse() }
|
||||||
return roomAmenityRepo.findByPropertyIdOrderByName(propertyId).map { it.toResponse() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@ResponseStatus(HttpStatus.CREATED)
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
fun createAmenity(
|
fun createAmenity(
|
||||||
@PathVariable propertyId: UUID,
|
|
||||||
@AuthenticationPrincipal principal: MyPrincipal?,
|
@AuthenticationPrincipal principal: MyPrincipal?,
|
||||||
@RequestBody request: AmenityUpsertRequest
|
@RequestBody request: AmenityUpsertRequest
|
||||||
): AmenityResponse {
|
): AmenityResponse {
|
||||||
requirePrincipal(principal)
|
requireSuperAdmin(principal)
|
||||||
propertyAccess.requireMember(propertyId, principal!!.userId)
|
|
||||||
propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER)
|
|
||||||
|
|
||||||
if (roomAmenityRepo.existsByPropertyIdAndName(propertyId, request.name)) {
|
if (roomAmenityRepo.existsByName(request.name)) {
|
||||||
throw ResponseStatusException(HttpStatus.CONFLICT, "Amenity already exists for property")
|
throw ResponseStatusException(HttpStatus.CONFLICT, "Amenity already exists")
|
||||||
}
|
|
||||||
|
|
||||||
val property = propertyRepo.findById(propertyId).orElseThrow {
|
|
||||||
ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
|
||||||
}
|
}
|
||||||
val amenity = RoomAmenity(
|
val amenity = RoomAmenity(
|
||||||
property = property,
|
|
||||||
name = request.name,
|
name = request.name,
|
||||||
category = request.category,
|
category = request.category,
|
||||||
iconKey = request.iconKey,
|
iconKey = request.iconKey,
|
||||||
@@ -70,20 +57,17 @@ class RoomAmenities(
|
|||||||
|
|
||||||
@PutMapping("/{amenityId}")
|
@PutMapping("/{amenityId}")
|
||||||
fun updateAmenity(
|
fun updateAmenity(
|
||||||
@PathVariable propertyId: UUID,
|
|
||||||
@PathVariable amenityId: UUID,
|
@PathVariable amenityId: UUID,
|
||||||
@AuthenticationPrincipal principal: MyPrincipal?,
|
@AuthenticationPrincipal principal: MyPrincipal?,
|
||||||
@RequestBody request: AmenityUpsertRequest
|
@RequestBody request: AmenityUpsertRequest
|
||||||
): AmenityResponse {
|
): AmenityResponse {
|
||||||
requirePrincipal(principal)
|
requireSuperAdmin(principal)
|
||||||
propertyAccess.requireMember(propertyId, principal!!.userId)
|
|
||||||
propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER)
|
|
||||||
|
|
||||||
val amenity = roomAmenityRepo.findByIdAndPropertyId(amenityId, propertyId)
|
val amenity = roomAmenityRepo.findById(amenityId).orElse(null)
|
||||||
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
||||||
|
|
||||||
if (roomAmenityRepo.existsByPropertyIdAndNameAndIdNot(propertyId, request.name, amenityId)) {
|
if (roomAmenityRepo.existsByNameAndIdNot(request.name, amenityId)) {
|
||||||
throw ResponseStatusException(HttpStatus.CONFLICT, "Amenity already exists for property")
|
throw ResponseStatusException(HttpStatus.CONFLICT, "Amenity already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
amenity.name = request.name
|
amenity.name = request.name
|
||||||
@@ -96,15 +80,12 @@ class RoomAmenities(
|
|||||||
@DeleteMapping("/{amenityId}")
|
@DeleteMapping("/{amenityId}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
fun deleteAmenity(
|
fun deleteAmenity(
|
||||||
@PathVariable propertyId: UUID,
|
|
||||||
@PathVariable amenityId: UUID,
|
@PathVariable amenityId: UUID,
|
||||||
@AuthenticationPrincipal principal: MyPrincipal?
|
@AuthenticationPrincipal principal: MyPrincipal?
|
||||||
) {
|
) {
|
||||||
requirePrincipal(principal)
|
requireSuperAdmin(principal)
|
||||||
propertyAccess.requireMember(propertyId, principal!!.userId)
|
|
||||||
propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER)
|
|
||||||
|
|
||||||
val amenity = roomAmenityRepo.findByIdAndPropertyId(amenityId, propertyId)
|
val amenity = roomAmenityRepo.findById(amenityId).orElse(null)
|
||||||
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
||||||
|
|
||||||
roomAmenityRepo.delete(amenity)
|
roomAmenityRepo.delete(amenity)
|
||||||
@@ -115,14 +96,24 @@ class RoomAmenities(
|
|||||||
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
|
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun requireSuperAdmin(principal: MyPrincipal?) {
|
||||||
|
if (principal == null) {
|
||||||
|
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
|
||||||
|
}
|
||||||
|
val user = appUserRepo.findById(principal.userId).orElseThrow {
|
||||||
|
ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found")
|
||||||
|
}
|
||||||
|
if (!user.superAdmin) {
|
||||||
|
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Super admin only")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RoomAmenity.toResponse(): AmenityResponse {
|
private fun RoomAmenity.toResponse(): AmenityResponse {
|
||||||
val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Amenity id missing")
|
val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Amenity id missing")
|
||||||
val propertyId = property.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Property id missing")
|
|
||||||
return AmenityResponse(
|
return AmenityResponse(
|
||||||
id = id,
|
id = id,
|
||||||
propertyId = propertyId,
|
|
||||||
name = name,
|
name = name,
|
||||||
category = category,
|
category = category,
|
||||||
iconKey = iconKey,
|
iconKey = iconKey,
|
||||||
|
|||||||
@@ -74,16 +74,16 @@ class RoomTypes(
|
|||||||
otaAliases = request.otaAliases?.toMutableSet() ?: mutableSetOf()
|
otaAliases = request.otaAliases?.toMutableSet() ?: mutableSetOf()
|
||||||
)
|
)
|
||||||
if (request.amenityIds != null) {
|
if (request.amenityIds != null) {
|
||||||
roomType.amenities = resolveAmenities(propertyId, request.amenityIds)
|
roomType.amenities = resolveAmenities(request.amenityIds)
|
||||||
}
|
}
|
||||||
return roomTypeRepo.save(roomType).toResponse()
|
return roomTypeRepo.save(roomType).toResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveAmenities(propertyId: UUID, ids: Set<UUID>): MutableSet<RoomAmenity> {
|
private fun resolveAmenities(ids: Set<UUID>): MutableSet<RoomAmenity> {
|
||||||
if (ids.isEmpty()) {
|
if (ids.isEmpty()) {
|
||||||
return mutableSetOf()
|
return mutableSetOf()
|
||||||
}
|
}
|
||||||
val amenities = roomAmenityRepo.findByPropertyIdAndIdIn(propertyId, ids)
|
val amenities = roomAmenityRepo.findByIdIn(ids)
|
||||||
if (amenities.size != ids.size) {
|
if (amenities.size != ids.size) {
|
||||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Amenity not found")
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ class RoomTypes(
|
|||||||
roomType.otaAliases = request.otaAliases.toMutableSet()
|
roomType.otaAliases = request.otaAliases.toMutableSet()
|
||||||
}
|
}
|
||||||
if (request.amenityIds != null) {
|
if (request.amenityIds != null) {
|
||||||
roomType.amenities = resolveAmenities(propertyId, request.amenityIds)
|
roomType.amenities = resolveAmenities(request.amenityIds)
|
||||||
}
|
}
|
||||||
return roomTypeRepo.save(roomType).toResponse()
|
return roomTypeRepo.save(roomType).toResponse()
|
||||||
}
|
}
|
||||||
@@ -169,10 +169,8 @@ private fun RoomType.toResponse(): RoomTypeResponse {
|
|||||||
|
|
||||||
private fun RoomAmenity.toResponse(): com.android.trisolarisserver.controller.dto.AmenityResponse {
|
private fun RoomAmenity.toResponse(): com.android.trisolarisserver.controller.dto.AmenityResponse {
|
||||||
val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Amenity id missing")
|
val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Amenity id missing")
|
||||||
val propertyId = property.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Property id missing")
|
|
||||||
return com.android.trisolarisserver.controller.dto.AmenityResponse(
|
return com.android.trisolarisserver.controller.dto.AmenityResponse(
|
||||||
id = id,
|
id = id,
|
||||||
propertyId = propertyId,
|
|
||||||
name = name,
|
name = name,
|
||||||
category = category,
|
category = category,
|
||||||
iconKey = iconKey,
|
iconKey = iconKey,
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ data class AmenityUpsertRequest(
|
|||||||
|
|
||||||
data class AmenityResponse(
|
data class AmenityResponse(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
val propertyId: UUID,
|
|
||||||
val name: String,
|
val name: String,
|
||||||
val category: String?,
|
val category: String?,
|
||||||
val iconKey: String?,
|
val iconKey: String?,
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package com.android.trisolarisserver.models.room
|
package com.android.trisolarisserver.models.room
|
||||||
|
|
||||||
import com.android.trisolarisserver.models.property.Property
|
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
import jakarta.persistence.FetchType
|
|
||||||
import jakarta.persistence.GeneratedValue
|
import jakarta.persistence.GeneratedValue
|
||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.JoinColumn
|
|
||||||
import jakarta.persistence.ManyToOne
|
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import jakarta.persistence.UniqueConstraint
|
import jakarta.persistence.UniqueConstraint
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
@@ -16,7 +12,7 @@ import java.util.UUID
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "room_amenity",
|
name = "room_amenity",
|
||||||
uniqueConstraints = [UniqueConstraint(columnNames = ["property_id", "name"])]
|
uniqueConstraints = [UniqueConstraint(columnNames = ["name"])]
|
||||||
)
|
)
|
||||||
class RoomAmenity(
|
class RoomAmenity(
|
||||||
@Id
|
@Id
|
||||||
@@ -24,10 +20,6 @@ class RoomAmenity(
|
|||||||
@Column(columnDefinition = "uuid")
|
@Column(columnDefinition = "uuid")
|
||||||
val id: UUID? = null,
|
val id: UUID? = null,
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
|
||||||
@JoinColumn(name = "property_id", nullable = false)
|
|
||||||
var property: Property,
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
interface RoomAmenityRepo : JpaRepository<RoomAmenity, UUID> {
|
interface RoomAmenityRepo : JpaRepository<RoomAmenity, UUID> {
|
||||||
fun findByIdAndPropertyId(id: UUID, propertyId: UUID): RoomAmenity?
|
fun findAllByOrderByName(): List<RoomAmenity>
|
||||||
fun findByPropertyIdOrderByName(propertyId: UUID): List<RoomAmenity>
|
fun findByIdIn(ids: Set<UUID>): List<RoomAmenity>
|
||||||
fun findByPropertyIdAndIdIn(propertyId: UUID, ids: Set<UUID>): List<RoomAmenity>
|
fun existsByName(name: String): Boolean
|
||||||
fun existsByPropertyIdAndName(propertyId: UUID, name: String): Boolean
|
fun existsByNameAndIdNot(name: String, id: UUID): Boolean
|
||||||
fun existsByPropertyIdAndNameAndIdNot(propertyId: UUID, name: String, id: UUID): Boolean
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user