Adds user level hunt endpoints

This commit is contained in:
2026-05-12 10:08:34 -05:00
parent ab34f16a45
commit 46a78bfc08
4 changed files with 83 additions and 3 deletions

View File

@@ -1,5 +1,7 @@
package net.halfbinary.scavengerhuntapi.controller 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 jakarta.validation.Valid
import net.halfbinary.scavengerhuntapi.model.HuntId import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.HunterId import net.halfbinary.scavengerhuntapi.model.HunterId
@@ -9,32 +11,61 @@ import net.halfbinary.scavengerhuntapi.model.request.HuntCreateRequest
import net.halfbinary.scavengerhuntapi.model.request.HuntStatus import net.halfbinary.scavengerhuntapi.model.request.HuntStatus
import net.halfbinary.scavengerhuntapi.model.response.HuntResponse import net.halfbinary.scavengerhuntapi.model.response.HuntResponse
import net.halfbinary.scavengerhuntapi.service.HuntService import net.halfbinary.scavengerhuntapi.service.HuntService
import net.halfbinary.scavengerhuntapi.service.HunterService
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import java.time.LocalDateTime
@RestController @RestController
@RequestMapping("hunt") @RequestMapping("hunt")
class HuntController(private val huntService: HuntService) { class HuntController(private val huntService: HuntService, private val hunterService: HunterService) {
@GetMapping("/{id}") @GetMapping("/{id}")
@Operation(summary = "Gets the specified hunt information")
fun getHunt(@PathVariable("id") huntId: HuntId): ResponseEntity<HuntResponse> { fun getHunt(@PathVariable("id") huntId: HuntId): ResponseEntity<HuntResponse> {
return ResponseEntity.ok(huntService.getHunt(huntId).toResponse()) return ResponseEntity.ok(huntService.getHunt(huntId).toResponse())
} }
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@GetMapping() @GetMapping()
@Operation(summary = "Gets all Hunts")
fun getAllHunts(@RequestParam status: HuntStatus?): ResponseEntity<List<HuntResponse>> { fun getAllHunts(@RequestParam status: HuntStatus?): ResponseEntity<List<HuntResponse>> {
return ResponseEntity.ok(huntService.getAllHunts(status).map { it.toResponse() }) return ResponseEntity.ok(huntService.getAllHunts(status).map { it.toResponse() })
} }
@GetMapping("/ongoing")
@Operation(summary = "Gets list of all currently running Hunts (filtered by the calling hunter)")
fun getOngoingHunts(authentication: Authentication, @RequestParam status: HuntStatus?): ResponseEntity<List<HuntResponse>> {
val email = authentication.name
val isAdmin = hunterService.getHunterByEmail(email).isAdmin
return if(isAdmin) {
ResponseEntity.ok(huntService.getAllHunts(HuntStatus.ONGOING).map { it.toResponse() })
} else {
ResponseEntity.ok(huntService.getHuntsByEmail(email, status).map { it.toResponse() })
}
}
@GetMapping("/unstarted")
@Operation(summary = "Gets list of all upcoming Hunts")
fun getUnstartedHunts(): ResponseEntity<List<HuntResponse>> {
return ResponseEntity.ok(huntService.getAllHunts(HuntStatus.UNSTARTED).map { it.toResponse() })
}
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@PostMapping() @PostMapping()
@Operation(summary = "Creates a new Hunt")
fun createHunt(@Valid @RequestBody huntRequest: HuntCreateRequest): ResponseEntity<HuntResponse> { fun createHunt(@Valid @RequestBody huntRequest: HuntCreateRequest): ResponseEntity<HuntResponse> {
return ResponseEntity.ok(huntService.createHunt(huntRequest.toDomain()).toResponse()) return ResponseEntity.ok(huntService.createHunt(huntRequest.toDomain()).toResponse())
} }
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Admin")
@GetMapping("/hunter/{hunterId}") @GetMapping("/hunter/{hunterId}")
fun getHuntsByHunter(@PathVariable("hunterId") hunterId: HunterId): ResponseEntity<List<HuntResponse>> { @Operation(summary = "Lists all Hunts for specified Hunter")
fun getHuntsByHunter(@PathVariable hunterId: HunterId): ResponseEntity<List<HuntResponse>> {
return ResponseEntity.ok(huntService.getHuntsByHunter(hunterId).map { it.toResponse() }) return ResponseEntity.ok(huntService.getHuntsByHunter(hunterId).map { it.toResponse() })
} }

View File

@@ -2,6 +2,7 @@ package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HuntId import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.HunterId import net.halfbinary.scavengerhuntapi.model.HunterId
import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.record.HuntRecord import net.halfbinary.scavengerhuntapi.model.record.HuntRecord
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
@@ -23,6 +24,17 @@ interface HuntRepository : JpaRepository<HuntRecord, HuntId> {
""", nativeQuery = true) """, nativeQuery = true)
fun findAllOngoingByHunter(hunterId: HunterId): List<HuntRecord> fun findAllOngoingByHunter(hunterId: HunterId): List<HuntRecord>
@Query("""
SELECT h.*
FROM hunter u
INNER JOIN hunter_team ht ON u.id = ht.hunter_id
INNER JOIN team t ON ht.team_id = t.id
INNER JOIN team_hunt th ON t.id = th.team_id
INNER JOIN hunt h ON th.hunt_id = h.id
WHERE u.email = :email
""", nativeQuery = true)
fun findHuntsByEmail(email: String): List<HuntRecord>
@Query(""" @Query("""
SELECT h.* SELECT h.*
FROM hunt h FROM hunt h

View File

@@ -10,6 +10,7 @@ import net.halfbinary.scavengerhuntapi.model.request.HuntStatus
import net.halfbinary.scavengerhuntapi.repository.HuntRepository import net.halfbinary.scavengerhuntapi.repository.HuntRepository
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.LocalDateTime
@Service @Service
class HuntService(private val huntRepository: HuntRepository) { class HuntService(private val huntRepository: HuntRepository) {
@@ -30,6 +31,27 @@ class HuntService(private val huntRepository: HuntRepository) {
return huntRepository.findAllOngoingByHunter(hunterId).map { it.toDomain() } return huntRepository.findAllOngoingByHunter(hunterId).map { it.toDomain() }
} }
fun getHuntsByEmail(email: String, status: HuntStatus?): List<Hunt> {
val allHunts = huntRepository.findHuntsByEmail(email)
val filteredHunts = when (status) {
HuntStatus.ONGOING -> {
allHunts
.filter { !it.isTerminated && it.startDateTime < LocalDateTime.now() && it.endDateTime > LocalDateTime.now() }
.toList()
}
HuntStatus.CLOSED -> {
allHunts
.filter { it.isTerminated || it.endDateTime < LocalDateTime.now() }
}
HuntStatus.UNSTARTED -> {
allHunts
.filter { !it.isTerminated && it.startDateTime > LocalDateTime.now() }
}
else -> { allHunts }
}
return filteredHunts.map { it.toDomain() }
}
fun createHunt(hunt: Hunt): Hunt { fun createHunt(hunt: Hunt): Hunt {
return huntRepository.save(hunt.toRecord()).toDomain() return huntRepository.save(hunt.toRecord()).toDomain()
} }

View File

@@ -0,0 +1,15 @@
package net.halfbinary.scavengerhuntapi.service
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
import net.halfbinary.scavengerhuntapi.repository.HunterRepository
import org.springframework.stereotype.Service
@Service
class HunterService(private val hunterRepository: HunterRepository) {
fun getHunterByEmail(email: String): Hunter {
return hunterRepository.findByEmail(email)?.toDomain()
?: throw NotFoundException("No hunter with email $email found")
}
}