6 Commits

21 changed files with 194 additions and 49 deletions

View File

@@ -9,11 +9,13 @@ import net.halfbinary.scavengerhuntapi.model.converter.toDomain
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
import net.halfbinary.scavengerhuntapi.model.request.HuntCreateRequest
import net.halfbinary.scavengerhuntapi.model.request.HuntStatus
import net.halfbinary.scavengerhuntapi.model.request.HuntUpdateRequest
import net.halfbinary.scavengerhuntapi.model.response.HuntResponse
import net.halfbinary.scavengerhuntapi.service.HuntService
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
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
@@ -44,6 +46,12 @@ class HuntController(private val huntService: HuntService) {
return ResponseEntity.ok(huntService.getAllHunts(HuntStatus.UNSTARTED).map { it.toResponse() })
}
@GetMapping("/ongoing")
@Operation(summary = "Gets list of all ongoing Hunts")
fun getOngoingHunts(): ResponseEntity<List<HuntResponse>> {
return ResponseEntity.ok(huntService.getAllHunts(HuntStatus.ONGOING).map { it.toResponse() })
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@PostMapping
@@ -52,6 +60,14 @@ class HuntController(private val huntService: HuntService) {
return ResponseEntity.ok(huntService.createHunt(huntRequest.toDomain()).toResponse())
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@PatchMapping("/{id}")
@Operation(summary = "Updates details of the specified Hunt")
fun updateHunt(@PathVariable("id") huntId: HuntId, @RequestBody body: HuntUpdateRequest): ResponseEntity<HuntResponse> {
return ResponseEntity.ok(huntService.updateHunt(huntId, body).toResponse())
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@GetMapping("/hunter/{hunterId}")

View File

@@ -8,11 +8,15 @@ import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
import net.halfbinary.scavengerhuntapi.model.request.ItemRequest
import net.halfbinary.scavengerhuntapi.model.request.ItemUpdateRequest
import net.halfbinary.scavengerhuntapi.model.response.ItemResponse
import net.halfbinary.scavengerhuntapi.service.HuntService
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
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
@@ -24,8 +28,8 @@ import org.springframework.web.bind.annotation.RestController
class ItemController(private val huntService: HuntService) {
@GetMapping
fun getItemsForHunt(@PathVariable huntId: HuntId): ResponseEntity<List<ItemResponse>> {
return ResponseEntity.ok(huntService.getItemsForHunt(huntId).map { it.toResponse() })
fun getItemsForHunt(@PathVariable huntId: HuntId, authentication: Authentication): ResponseEntity<List<ItemResponse>> {
return ResponseEntity.ok(huntService.getItemsForHunt(huntId, authentication.name).map { it.toResponse() })
}
@GetMapping("/{itemId}")
@@ -41,4 +45,21 @@ class ItemController(private val huntService: HuntService) {
return ResponseEntity.ok(huntService.addItemToHunt(huntId, body.toDomain()).toResponse())
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@PatchMapping("/{itemId}")
@Operation(summary = "Updates name and/or points for the specified Item in the specified Hunt")
fun updateItem(@PathVariable huntId: HuntId, @PathVariable itemId: ItemId, @RequestBody body: ItemUpdateRequest): ResponseEntity<ItemResponse> {
return ResponseEntity.ok(huntService.updateItem(huntId, itemId, body).toResponse())
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@DeleteMapping("/{itemId}")
@Operation(summary = "Deletes the specified Item from the specified Hunt")
fun deleteItem(@PathVariable huntId: HuntId, @PathVariable itemId: ItemId): ResponseEntity<Unit> {
huntService.deleteItem(huntId, itemId)
return ResponseEntity.noContent().build()
}
}

View File

@@ -7,7 +7,9 @@ import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.PhotoId
import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
import net.halfbinary.scavengerhuntapi.model.converter.toSummaryResponse
import net.halfbinary.scavengerhuntapi.model.request.TeamRequest
import net.halfbinary.scavengerhuntapi.model.response.HunterSummaryResponse
import net.halfbinary.scavengerhuntapi.model.response.PhotoResponse
import net.halfbinary.scavengerhuntapi.model.response.TeamItemResponse
import net.halfbinary.scavengerhuntapi.model.response.TeamResponse
@@ -47,6 +49,12 @@ class TeamController(private val teamService: TeamService, private val photoServ
return ResponseEntity.ok(teamService.getTeamFromHunt(huntId, teamId).toResponse())
}
@GetMapping("/{teamId}/hunter")
@Operation(summary = "Get all Hunters for the specified Team in the specified Hunt")
fun getHuntersForTeam(@PathVariable huntId: HuntId, @PathVariable teamId: TeamId): ResponseEntity<List<HunterSummaryResponse>> {
return ResponseEntity.ok(teamService.getHuntersForTeam(huntId, teamId).map { it.toSummaryResponse() })
}
@GetMapping("/{teamId}/item/{itemId}")
@Operation(summary = "Get found/not found status about the Item for the specified Team, Hunt, and Item")
fun getItemForTeam(@PathVariable huntId: HuntId,

View File

@@ -3,6 +3,7 @@ package net.halfbinary.scavengerhuntapi.model.converter
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
import net.halfbinary.scavengerhuntapi.model.record.HunterRecord
import net.halfbinary.scavengerhuntapi.model.request.HunterSignupRequest
import net.halfbinary.scavengerhuntapi.model.response.HunterSummaryResponse
fun HunterSignupRequest.toDomain(): Hunter {
return Hunter(
@@ -20,3 +21,7 @@ fun Hunter.toRecord(): HunterRecord {
fun HunterRecord.toDomain(): Hunter {
return Hunter(id, email, name, password, isAdmin)
}
fun Hunter.toSummaryResponse(): HunterSummaryResponse {
return HunterSummaryResponse(id, name)
}

View File

@@ -1,13 +1,16 @@
package net.halfbinary.scavengerhuntapi.model.domain
import net.halfbinary.scavengerhuntapi.model.HuntId
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.util.*
data class Hunt(
val id: HuntId = UUID.randomUUID(),
val title: String,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val startDateTime: OffsetDateTime,
val endDateTime: OffsetDateTime,
val isTerminated: Boolean
)
) {
val isOngoing: Boolean
get() = !isTerminated && startDateTime < OffsetDateTime.now() && endDateTime > OffsetDateTime.now()
}

View File

@@ -5,7 +5,7 @@ import net.halfbinary.scavengerhuntapi.model.HunterId
import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.PhotoId
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.util.*
data class Photo(
@@ -13,7 +13,7 @@ data class Photo(
val itemId: ItemId,
val huntId: HuntId,
val hunterId: HunterId,
val foundDateTime: LocalDateTime,
val foundDateTime: OffsetDateTime,
val status: PhotoStatus,
val statusChangeDateTime: LocalDateTime
val statusChangeDateTime: OffsetDateTime
)

View File

@@ -4,7 +4,7 @@ import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId
import java.time.LocalDateTime
import java.time.OffsetDateTime
/**
* Represents a scavenger hunt event
@@ -16,7 +16,7 @@ data class HuntRecord(
@Id
val id: HuntId,
val title: String,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val startDateTime: OffsetDateTime,
val endDateTime: OffsetDateTime,
val isTerminated: Boolean
)

View File

@@ -8,7 +8,7 @@ import net.halfbinary.scavengerhuntapi.model.HunterId
import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.PhotoId
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
import java.time.LocalDateTime
import java.time.OffsetDateTime
/**
* Represents a found Item for a Hunt by a Hunter
@@ -21,7 +21,7 @@ data class PhotoRecord(
val itemId: ItemId,
val huntId: HuntId,
val hunterId: HunterId,
val foundDateTime: LocalDateTime,
val foundDateTime: OffsetDateTime,
val status: PhotoStatus,
val statusChangeDateTime: LocalDateTime,
val statusChangeDateTime: OffsetDateTime,
)

View File

@@ -4,7 +4,7 @@ import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.RefreshId
import java.time.LocalDateTime
import java.time.OffsetDateTime
@Entity
@Table(name = "refresh_token")
@@ -12,5 +12,5 @@ data class RefreshTokenRecord(
@Id
val token: RefreshId,
val email: String,
val expiryDateTime: LocalDateTime
val expiryDateTime: OffsetDateTime
)

View File

@@ -2,13 +2,13 @@ package net.halfbinary.scavengerhuntapi.model.request
import jakarta.validation.constraints.Future
import jakarta.validation.constraints.NotBlank
import java.time.LocalDateTime
import java.time.OffsetDateTime
data class HuntCreateRequest(
@field:NotBlank(message = "Hunt title is required")
val title: String,
@field:Future
val startDateTime: LocalDateTime,
val startDateTime: OffsetDateTime,
@field:Future
val endDateTime: LocalDateTime,
val endDateTime: OffsetDateTime,
)

View File

@@ -0,0 +1,10 @@
package net.halfbinary.scavengerhuntapi.model.request
import java.time.OffsetDateTime
data class HuntUpdateRequest(
val title: String?,
val startDateTime: OffsetDateTime?,
val endDateTime: OffsetDateTime?,
val isTerminated: Boolean?
)

View File

@@ -0,0 +1,6 @@
package net.halfbinary.scavengerhuntapi.model.request
data class ItemUpdateRequest(
val name: String?,
val points: Int?
)

View File

@@ -1,9 +1,9 @@
package net.halfbinary.scavengerhuntapi.model.request
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotNull
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
data class ReviewPhotoRequest(
@field:NotBlank(message = "Status must not be blank")
@field:NotNull(message = "Status must not be null")
val status: PhotoStatus
)

View File

@@ -1,12 +1,12 @@
package net.halfbinary.scavengerhuntapi.model.response
import net.halfbinary.scavengerhuntapi.model.HuntId
import java.time.LocalDateTime
import java.time.OffsetDateTime
data class HuntResponse(
val id: HuntId,
val title: String,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val startDateTime: OffsetDateTime,
val endDateTime: OffsetDateTime,
val isTerminated: Boolean
)

View File

@@ -0,0 +1,8 @@
package net.halfbinary.scavengerhuntapi.model.response
import net.halfbinary.scavengerhuntapi.model.HunterId
data class HunterSummaryResponse(
val id: HunterId,
val name: String
)

View File

@@ -2,12 +2,12 @@ package net.halfbinary.scavengerhuntapi.model.response
import net.halfbinary.scavengerhuntapi.model.PhotoId
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
import java.time.LocalDateTime
import java.time.OffsetDateTime
data class PhotoResponse(
val id: PhotoId,
val hunterName: String,
val photoUploadDateTime: LocalDateTime,
val photoUploadDateTime: OffsetDateTime,
val photoStatus: PhotoStatus,
val photoStatusChangeDateTime: LocalDateTime,
val photoStatusChangeDateTime: OffsetDateTime,
)

View File

@@ -1,9 +1,13 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.record.HuntItemRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import java.util.*
@Repository
interface HuntItemRepository : JpaRepository<HuntItemRecord, UUID>
interface HuntItemRepository : JpaRepository<HuntItemRecord, UUID> {
fun findByHuntIdAndItemId(huntId: HuntId, itemId: ItemId): HuntItemRecord?
}

View File

@@ -1,26 +1,31 @@
package net.halfbinary.scavengerhuntapi.service
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.HunterId
import net.halfbinary.scavengerhuntapi.model.ItemId
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
import net.halfbinary.scavengerhuntapi.model.converter.toRecord
import net.halfbinary.scavengerhuntapi.model.domain.Hunt
import net.halfbinary.scavengerhuntapi.model.domain.HuntItem
import net.halfbinary.scavengerhuntapi.model.domain.Item
import net.halfbinary.scavengerhuntapi.model.request.HuntStatus
import net.halfbinary.scavengerhuntapi.model.request.HuntUpdateRequest
import net.halfbinary.scavengerhuntapi.model.request.ItemUpdateRequest
import net.halfbinary.scavengerhuntapi.repository.HuntItemRepository
import net.halfbinary.scavengerhuntapi.repository.HuntRepository
import net.halfbinary.scavengerhuntapi.repository.ItemRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.time.OffsetDateTime
@Service
class HuntService(
private val huntRepository: HuntRepository,
private val itemRepository: ItemRepository,
private val huntItemRepository: HuntItemRepository
private val huntItemRepository: HuntItemRepository,
private val hunterService: HunterService
) {
fun getHunt(huntId: HuntId): Hunt {
return huntRepository.findByIdOrNull(huntId)?.toDomain() ?: throw NotFoundException("No hunt with id $huntId found")
@@ -44,16 +49,16 @@ class HuntService(
val filteredHunts = when (status) {
HuntStatus.ONGOING -> {
allHunts
.filter { !it.isTerminated && it.startDateTime < LocalDateTime.now() && it.endDateTime > LocalDateTime.now() }
.filter { !it.isTerminated && it.startDateTime < OffsetDateTime.now() && it.endDateTime > OffsetDateTime.now() }
.toList()
}
HuntStatus.CLOSED -> {
allHunts
.filter { it.isTerminated || it.endDateTime < LocalDateTime.now() }
.filter { it.isTerminated || it.endDateTime < OffsetDateTime.now() }
}
HuntStatus.UNSTARTED -> {
allHunts
.filter { !it.isTerminated && it.startDateTime > LocalDateTime.now() }
.filter { !it.isTerminated && it.startDateTime > OffsetDateTime.now() }
}
else -> { allHunts }
}
@@ -64,8 +69,22 @@ class HuntService(
return huntRepository.save(hunt.toRecord()).toDomain()
}
fun getItemsForHunt(huntId: HuntId): List<Item> {
huntRepository.findByIdOrNull(huntId) ?: throw NotFoundException("No hunt with id $huntId found")
fun updateHunt(huntId: HuntId, request: HuntUpdateRequest): Hunt {
val existing = huntRepository.findByIdOrNull(huntId)
?: throw NotFoundException("No hunt with id $huntId found")
val updated = existing.copy(
title = request.title ?: existing.title,
startDateTime = request.startDateTime ?: existing.startDateTime,
endDateTime = request.endDateTime ?: existing.endDateTime,
isTerminated = request.isTerminated ?: existing.isTerminated
)
return huntRepository.save(updated).toDomain()
}
fun getItemsForHunt(huntId: HuntId, email: String): List<Item> {
val hunt = huntRepository.findByIdOrNull(huntId)?.toDomain() ?: throw NotFoundException("No hunt with id $huntId found")
val hunter = hunterService.getHunterByEmail(email)
if (!hunter.isAdmin && !hunt.isOngoing) throw ForbiddenException()
return itemRepository.findAllByHuntId(huntId).map { it.toDomain() }
}
@@ -75,4 +94,23 @@ class HuntService(
huntItemRepository.save(HuntItem(huntId = huntId, itemId = savedItem.id).toRecord())
return savedItem
}
fun updateItem(huntId: HuntId, itemId: ItemId, request: ItemUpdateRequest): Item {
huntItemRepository.findByHuntIdAndItemId(huntId, itemId)
?: throw NotFoundException("No item with id $itemId found in hunt $huntId")
val existing = itemRepository.findByIdOrNull(itemId)
?: throw NotFoundException("No item with id $itemId found")
val updated = existing.copy(
name = request.name ?: existing.name,
points = request.points ?: existing.points
)
return itemRepository.save(updated).toDomain()
}
fun deleteItem(huntId: HuntId, itemId: ItemId) {
val huntItem = huntItemRepository.findByHuntIdAndItemId(huntId, itemId)
?: throw NotFoundException("No item with id $itemId found in hunt $huntId")
huntItemRepository.delete(huntItem)
itemRepository.deleteById(itemId)
}
}

View File

@@ -27,7 +27,7 @@ import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.time.LocalDateTime
import java.time.OffsetDateTime
private const val PHOTO_NOT_FOUND = "Photo not found"
@@ -36,10 +36,15 @@ class PhotoService(
private val photoRepository: PhotoRepository,
private val hunterService: HunterService,
private val teamService: TeamService,
private val huntService: HuntService,
private val s3StorageService: S3StorageService,
private val fileProbeService: FileProbeService
) {
fun submitPhoto(huntId: HuntId, itemId: ItemId, email: String, file: MultipartFile) {
val hunter = hunterService.getHunterByEmail(email)
val hunt = huntService.getHunt(huntId)
if (!hunter.isAdmin && !hunt.isOngoing) throw ForbiddenException()
val originalBytes = file.bytes
val fileType = fileProbeService.getFileType(originalBytes)
@@ -51,8 +56,7 @@ class PhotoService(
throw BadFileException("Image type is not supported")
}
val hunter = hunterService.getHunterByEmail(email)
val now = LocalDateTime.now()
val now = OffsetDateTime.now()
val photo = Photo(
itemId = itemId,
huntId = huntId,
@@ -76,6 +80,8 @@ class PhotoService(
?: throw NotFoundException(PHOTO_NOT_FOUND)
if (!requestingHunter.isAdmin) {
val hunt = huntService.getHunt(huntId)
if (!hunt.isOngoing) throw ForbiddenException()
val team = try {
teamService.getTeamForHunterInHunt(huntId, email)
} catch (_: NotFoundException) {
@@ -121,6 +127,8 @@ class PhotoService(
val requestingHunter = hunterService.getHunterByEmail(email)
if (!requestingHunter.isAdmin) {
val hunt = huntService.getHunt(huntId)
if (!hunt.isOngoing) throw ForbiddenException()
val team = try {
teamService.getTeamForHunterInHunt(huntId, email)
} catch (_: NotFoundException) {
@@ -142,25 +150,36 @@ class PhotoService(
}
fun removePhoto(huntId: HuntId, teamId: TeamId, itemId: ItemId, photoId: PhotoId, email: String) {
val requestingHunter = hunterService.getHunterByEmail(email)
if (!requestingHunter.isAdmin) {
val hunt = huntService.getHunt(huntId)
if (!hunt.isOngoing) throw ForbiddenException()
}
val photoRecord = photoRepository.findByIdAndItemIdAndHuntId(photoId, itemId, huntId)
?: throw NotFoundException(PHOTO_NOT_FOUND)
if (!requestingHunter.isAdmin) {
val team = try {
teamService.getTeamForHunterInHunt(huntId, email)
} catch (_: NotFoundException) {
throw ForbiddenException()
}
if (team.id != teamId) throw ForbiddenException()
}
if (photoRecord.status == PhotoStatus.APPROVED) throw ConflictException("Cannot remove an approved photo")
photoRepository.save(photoRecord.copy(status = PhotoStatus.REMOVED, statusChangeDateTime = LocalDateTime.now()))
photoRepository.save(photoRecord.copy(status = PhotoStatus.REMOVED, statusChangeDateTime = OffsetDateTime.now()))
}
fun getItemPhotos(huntId: HuntId, teamId: TeamId, itemId: ItemId, email: String): List<PhotoResponse> {
val requestingHunter = hunterService.getHunterByEmail(email)
if (!requestingHunter.isAdmin) {
val hunt = huntService.getHunt(huntId)
if (!hunt.isOngoing) throw ForbiddenException()
val team = try {
teamService.getTeamForHunterInHunt(huntId, email)
} catch (_: NotFoundException) {
@@ -178,7 +197,7 @@ class PhotoService(
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()))
photoRepository.save(record.copy(status = status, statusChangeDateTime = OffsetDateTime.now()))
}
private fun toJpeg(bytes: ByteArray): ByteArray {

View File

@@ -10,7 +10,7 @@ import net.halfbinary.scavengerhuntapi.repository.RefreshTokenRepository
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.temporal.ChronoUnit
@Service
@@ -33,11 +33,11 @@ class RefreshTokenService(private val refreshTokenRepository: RefreshTokenReposi
}
fun generateRefreshToken(email: String): RefreshId {
return refreshTokenRepository.save(RefreshTokenRecord(RefreshId.randomUUID(), email, LocalDateTime.now().plus(1, ChronoUnit.MONTHS))).token
return refreshTokenRepository.save(RefreshTokenRecord(RefreshId.randomUUID(), email, OffsetDateTime.now().plus(1, ChronoUnit.MONTHS))).token
}
fun isTokenExpired(token: RefreshTokenRecord): Boolean {
return token.expiryDateTime.isBefore(LocalDateTime.now())
return token.expiryDateTime.isBefore(OffsetDateTime.now())
}
fun getToken(token: RefreshId): RefreshTokenRecord? {

View File

@@ -6,6 +6,7 @@ import net.halfbinary.scavengerhuntapi.model.HunterId
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.domain.Hunter
import net.halfbinary.scavengerhuntapi.model.domain.Team
import net.halfbinary.scavengerhuntapi.model.domain.TeamHunt
import net.halfbinary.scavengerhuntapi.model.record.HunterTeamRecord
@@ -54,6 +55,12 @@ class TeamService(
return hunterTeamRepository.findByTeamId(teamId).map { it.hunterId }.toSet()
}
fun getHuntersForTeam(huntId: HuntId, teamId: TeamId): List<Hunter> {
getTeamFromHunt(huntId, teamId)
val hunterIds = getHunterIdsForTeam(teamId)
return hunterRepository.findAllById(hunterIds).map { it.toDomain() }
}
fun joinTeam(teamId: TeamId, email: String) {
val hunter = hunterRepository.findByEmail(email) ?: throw NotFoundException("No hunter with email $email found")
hunterTeamRepository.save(HunterTeamRecord(UUID.randomUUID(), hunter.id, teamId))