diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/AdminController.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/AdminController.kt index ebe7d6b..ce76f7b 100644 --- a/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/AdminController.kt +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/AdminController.kt @@ -2,21 +2,25 @@ package net.halfbinary.scavengerhuntapi.controller import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid import net.halfbinary.scavengerhuntapi.model.PhotoId +import net.halfbinary.scavengerhuntapi.model.request.ReviewPhotoRequest +import net.halfbinary.scavengerhuntapi.service.PhotoService import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("admin") -class AdminController { +class AdminController(private val photoService: PhotoService) { @PreAuthorize("hasRole('ADMIN')") @Tag(name = "Admin") - @PostMapping("/admin/photo/{photoId}") + @PatchMapping("/photo/{photoId}") @Operation(summary = "Sets a review status for the specified photo") - fun reviewPhoto(@PathVariable photoId: PhotoId) { - TODO("Set a review status for the specified photo, and update the photo record's status change timestamp") + fun reviewPhoto(@PathVariable photoId: PhotoId, @Valid @RequestBody request: ReviewPhotoRequest) { + photoService.updatePhotoStatus(photoId, request.status) } } \ No newline at end of file diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/model/request/ReviewPhotoRequest.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/model/request/ReviewPhotoRequest.kt new file mode 100644 index 0000000..9fce8a8 --- /dev/null +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/model/request/ReviewPhotoRequest.kt @@ -0,0 +1,9 @@ +package net.halfbinary.scavengerhuntapi.model.request + +import jakarta.validation.constraints.NotBlank +import net.halfbinary.scavengerhuntapi.model.PhotoStatus + +data class ReviewPhotoRequest( + @field:NotBlank(message = "Status must not be blank") + val status: PhotoStatus +) \ No newline at end of file diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/PhotoService.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/PhotoService.kt index e9d2518..f9ffb7c 100644 --- a/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/PhotoService.kt +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/PhotoService.kt @@ -27,6 +27,8 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.time.LocalDateTime +private const val PHOTO_NOT_FOUND = "Photo not found" + @Service class PhotoService( private val photoRepository: PhotoRepository, @@ -69,7 +71,7 @@ class PhotoService( fun getPhotoInfo(huntId: HuntId, teamId: TeamId, itemId: ItemId, photoId: PhotoId, email: String): PhotoResponse { val requestingHunter = hunterService.getHunterByEmail(email) val photoRecord = photoRepository.findByIdAndItemIdAndHuntId(photoId, itemId, huntId) - ?: throw NotFoundException("Photo not found") + ?: throw NotFoundException(PHOTO_NOT_FOUND) if (!requestingHunter.isAdmin) { val team = try { @@ -87,7 +89,7 @@ class PhotoService( fun getPhotoFile(photoId: PhotoId, email: String, version: ImageVersion = ImageVersion.LARGE): PhotoFile { val requestingHunter = hunterService.getHunterByEmail(email) val photoRecord = photoRepository.findByIdOrNull(photoId) - ?: throw NotFoundException("Photo not found") + ?: throw NotFoundException(PHOTO_NOT_FOUND) if (!requestingHunter.isAdmin) { val submitter = hunterService.getHunterById(photoRecord.hunterId) @@ -113,6 +115,12 @@ class PhotoService( return PhotoFile(InputStreamResource(stream), MediaType.parseMediaType(contentType)) } + fun updatePhotoStatus(photoId: PhotoId, status: PhotoStatus) { + val record = photoRepository.findByIdOrNull(photoId) + ?: throw NotFoundException(PHOTO_NOT_FOUND) + photoRepository.save(record.copy(status = status, statusChangeDateTime = LocalDateTime.now())) + } + private fun toJpeg(bytes: ByteArray): ByteArray { val output = ByteArrayOutputStream() Thumbnails.of(ByteArrayInputStream(bytes))