Implements get photo information endpoint #4
@@ -69,8 +69,9 @@ class TeamController(private val teamService: TeamService, private val photoServ
|
|||||||
fun getPhotoInfo(@PathVariable huntId: HuntId,
|
fun getPhotoInfo(@PathVariable huntId: HuntId,
|
||||||
@PathVariable teamId: TeamId,
|
@PathVariable teamId: TeamId,
|
||||||
@PathVariable itemId: ItemId,
|
@PathVariable itemId: ItemId,
|
||||||
@PathVariable photoId: PhotoId): ResponseEntity<PhotoResponse> {
|
@PathVariable photoId: PhotoId,
|
||||||
TODO("Get photo information for the specified Team, Hunt, Item, and Photo. Join on the Hunter table to get the Hunter name. Also verify that the requesting user is either an admin or is on the same Hunt and Team as the Hunter who submitted the Photo")
|
authentication: Authentication): ResponseEntity<PhotoResponse> {
|
||||||
|
return ResponseEntity.ok(photoService.getPhotoInfo(huntId, teamId, itemId, photoId, authentication.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{teamId}/item/{itemId}/photo/{photoId}/file")
|
@GetMapping("/{teamId}/item/{itemId}/photo/{photoId}/file")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.halfbinary.scavengerhuntapi.error
|
package net.halfbinary.scavengerhuntapi.error
|
||||||
|
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
||||||
|
import net.halfbinary.scavengerhuntapi.error.exception.ForbiddenException
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.InvalidEmailException
|
import net.halfbinary.scavengerhuntapi.error.exception.InvalidEmailException
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.LoginFailedException
|
import net.halfbinary.scavengerhuntapi.error.exception.LoginFailedException
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
||||||
@@ -46,6 +47,12 @@ class ExceptionHandler {
|
|||||||
return e.message
|
return e.message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(ForbiddenException::class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
fun forbiddenException(e: ForbiddenException): String? {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(HttpMessageNotReadableException::class)
|
@ExceptionHandler(HttpMessageNotReadableException::class)
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
fun httpMessageNotReadableException(e: HttpMessageNotReadableException): Map<String, String?> {
|
fun httpMessageNotReadableException(e: HttpMessageNotReadableException): Map<String, String?> {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package net.halfbinary.scavengerhuntapi.error.exception
|
||||||
|
|
||||||
|
class ForbiddenException(override val message: String): RuntimeException(message)
|
||||||
@@ -15,6 +15,16 @@ fun Photo.toRecord() = PhotoRecord(
|
|||||||
statusChangeDateTime = statusChangeDateTime
|
statusChangeDateTime = statusChangeDateTime
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun PhotoRecord.toDomain() = Photo(
|
||||||
|
id = id,
|
||||||
|
itemId = itemId,
|
||||||
|
huntId = huntId,
|
||||||
|
hunterId = hunterId,
|
||||||
|
foundDateTime = foundDateTime,
|
||||||
|
status = status,
|
||||||
|
statusChangeDateTime = statusChangeDateTime
|
||||||
|
)
|
||||||
|
|
||||||
fun Photo.toResponse(hunter: Hunter) = PhotoResponse(
|
fun Photo.toResponse(hunter: Hunter) = PhotoResponse(
|
||||||
id = id,
|
id = id,
|
||||||
hunterName = hunter.name,
|
hunterName = hunter.name,
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
package net.halfbinary.scavengerhuntapi.repository
|
package net.halfbinary.scavengerhuntapi.repository
|
||||||
|
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||||
import net.halfbinary.scavengerhuntapi.model.PhotoId
|
import net.halfbinary.scavengerhuntapi.model.PhotoId
|
||||||
import net.halfbinary.scavengerhuntapi.model.record.PhotoRecord
|
import net.halfbinary.scavengerhuntapi.model.record.PhotoRecord
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.data.jpa.repository.Query
|
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface PhotoRepository : JpaRepository<PhotoRecord, PhotoId> {
|
interface PhotoRepository : JpaRepository<PhotoRecord, PhotoId> {
|
||||||
@Query("""
|
fun findByItemId(itemId: ItemId): List<PhotoRecord>
|
||||||
SELECT *
|
fun findByIdAndItemIdAndHuntId(id: PhotoId, itemId: ItemId, huntId: HuntId): PhotoRecord?
|
||||||
FROM photo p
|
|
||||||
WHERE
|
|
||||||
p.
|
|
||||||
""", nativeQuery = true)
|
|
||||||
fun findPhotosByItemId(itemId: ItemId): List<PhotoRecord>
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.halfbinary.scavengerhuntapi.service
|
package net.halfbinary.scavengerhuntapi.service
|
||||||
|
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.HunterId
|
||||||
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
|
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
|
||||||
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
|
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
|
||||||
import net.halfbinary.scavengerhuntapi.repository.HunterRepository
|
import net.halfbinary.scavengerhuntapi.repository.HunterRepository
|
||||||
@@ -12,4 +13,8 @@ class HunterService(private val hunterRepository: HunterRepository) {
|
|||||||
return hunterRepository.findByEmail(email)?.toDomain()
|
return hunterRepository.findByEmail(email)?.toDomain()
|
||||||
?: throw NotFoundException("No hunter with email $email found")
|
?: throw NotFoundException("No hunter with email $email found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getHunterById(hunterId: HunterId): Hunter {
|
||||||
|
return hunterRepository.findById(hunterId).orElseThrow { NotFoundException("No hunter with id $hunterId found") }.toDomain()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,18 @@ package net.halfbinary.scavengerhuntapi.service
|
|||||||
import net.coobird.thumbnailator.Thumbnails
|
import net.coobird.thumbnailator.Thumbnails
|
||||||
import net.coobird.thumbnailator.tasks.UnsupportedFormatException
|
import net.coobird.thumbnailator.tasks.UnsupportedFormatException
|
||||||
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
||||||
|
import net.halfbinary.scavengerhuntapi.error.exception.ForbiddenException
|
||||||
|
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
||||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.PhotoId
|
||||||
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
|
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.TeamId
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
|
||||||
import net.halfbinary.scavengerhuntapi.model.converter.toRecord
|
import net.halfbinary.scavengerhuntapi.model.converter.toRecord
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
|
||||||
import net.halfbinary.scavengerhuntapi.model.domain.Photo
|
import net.halfbinary.scavengerhuntapi.model.domain.Photo
|
||||||
|
import net.halfbinary.scavengerhuntapi.model.response.PhotoResponse
|
||||||
import net.halfbinary.scavengerhuntapi.repository.PhotoRepository
|
import net.halfbinary.scavengerhuntapi.repository.PhotoRepository
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
@@ -20,6 +27,7 @@ import java.time.LocalDateTime
|
|||||||
class PhotoService(
|
class PhotoService(
|
||||||
private val photoRepository: PhotoRepository,
|
private val photoRepository: PhotoRepository,
|
||||||
private val hunterService: HunterService,
|
private val hunterService: HunterService,
|
||||||
|
private val teamService: TeamService,
|
||||||
private val s3StorageService: S3StorageService,
|
private val s3StorageService: S3StorageService,
|
||||||
private val fileProbeService: FileProbeService
|
private val fileProbeService: FileProbeService
|
||||||
) {
|
) {
|
||||||
@@ -54,6 +62,20 @@ class PhotoService(
|
|||||||
s3StorageService.upload("${baseName}_small.jpg", resize(originalBytes, 200), MediaType.IMAGE_JPEG_VALUE)
|
s3StorageService.upload("${baseName}_small.jpg", resize(originalBytes, 200), MediaType.IMAGE_JPEG_VALUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
if (!requestingHunter.isAdmin) {
|
||||||
|
val team = teamService.getTeamForHunterInHunt(huntId, email)
|
||||||
|
if (team.id != teamId) throw ForbiddenException("Access denied")
|
||||||
|
}
|
||||||
|
|
||||||
|
val submitter = hunterService.getHunterById(photoRecord.hunterId)
|
||||||
|
return photoRecord.toDomain().toResponse(submitter)
|
||||||
|
}
|
||||||
|
|
||||||
private fun toJpeg(bytes: ByteArray): ByteArray {
|
private fun toJpeg(bytes: ByteArray): ByteArray {
|
||||||
val output = ByteArrayOutputStream()
|
val output = ByteArrayOutputStream()
|
||||||
Thumbnails.of(ByteArrayInputStream(bytes))
|
Thumbnails.of(ByteArrayInputStream(bytes))
|
||||||
|
|||||||
Reference in New Issue
Block a user