From 46a78bfc08a1779022ae654c58491e23a4d9ef79 Mon Sep 17 00:00:00 2001 From: aarbit Date: Tue, 12 May 2026 10:08:34 -0500 Subject: [PATCH] Adds user level hunt endpoints --- .../controller/HuntController.kt | 37 +++++++++++++++++-- .../repository/HuntRepository.kt | 12 ++++++ .../scavengerhuntapi/service/HuntService.kt | 22 +++++++++++ .../scavengerhuntapi/service/HunterService.kt | 15 ++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HunterService.kt diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/HuntController.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/HuntController.kt index 20a915c..dbd54de 100644 --- a/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/HuntController.kt +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/controller/HuntController.kt @@ -1,5 +1,7 @@ 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.HuntId 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.response.HuntResponse import net.halfbinary.scavengerhuntapi.service.HuntService +import net.halfbinary.scavengerhuntapi.service.HunterService import org.springframework.http.ResponseEntity import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.security.core.Authentication import org.springframework.web.bind.annotation.* +import java.time.LocalDateTime @RestController @RequestMapping("hunt") -class HuntController(private val huntService: HuntService) { +class HuntController(private val huntService: HuntService, private val hunterService: HunterService) { @GetMapping("/{id}") + @Operation(summary = "Gets the specified hunt information") fun getHunt(@PathVariable("id") huntId: HuntId): ResponseEntity { return ResponseEntity.ok(huntService.getHunt(huntId).toResponse()) } - @PreAuthorize("hasRole('ADMIN')") + @Tag(name = "Admin") @GetMapping() + @Operation(summary = "Gets all Hunts") fun getAllHunts(@RequestParam status: HuntStatus?): ResponseEntity> { 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> { + 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> { + return ResponseEntity.ok(huntService.getAllHunts(HuntStatus.UNSTARTED).map { it.toResponse() }) + } + + @PreAuthorize("hasRole('ADMIN')") + @Tag(name = "Admin") @PostMapping() + @Operation(summary = "Creates a new Hunt") fun createHunt(@Valid @RequestBody huntRequest: HuntCreateRequest): ResponseEntity { return ResponseEntity.ok(huntService.createHunt(huntRequest.toDomain()).toResponse()) } + @PreAuthorize("hasRole('ADMIN')") + @Tag(name = "Admin") @GetMapping("/hunter/{hunterId}") - fun getHuntsByHunter(@PathVariable("hunterId") hunterId: HunterId): ResponseEntity> { + @Operation(summary = "Lists all Hunts for specified Hunter") + fun getHuntsByHunter(@PathVariable hunterId: HunterId): ResponseEntity> { return ResponseEntity.ok(huntService.getHuntsByHunter(hunterId).map { it.toResponse() }) } diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/repository/HuntRepository.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/repository/HuntRepository.kt index a7c8c36..1ccb029 100644 --- a/src/main/kotlin/net/halfbinary/scavengerhuntapi/repository/HuntRepository.kt +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/repository/HuntRepository.kt @@ -2,6 +2,7 @@ package net.halfbinary.scavengerhuntapi.repository import net.halfbinary.scavengerhuntapi.model.HuntId import net.halfbinary.scavengerhuntapi.model.HunterId +import net.halfbinary.scavengerhuntapi.model.TeamId import net.halfbinary.scavengerhuntapi.model.record.HuntRecord import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query @@ -23,6 +24,17 @@ interface HuntRepository : JpaRepository { """, nativeQuery = true) fun findAllOngoingByHunter(hunterId: HunterId): List + @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 + @Query(""" SELECT h.* FROM hunt h diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HuntService.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HuntService.kt index b90534f..14655fa 100644 --- a/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HuntService.kt +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HuntService.kt @@ -10,6 +10,7 @@ import net.halfbinary.scavengerhuntapi.model.request.HuntStatus import net.halfbinary.scavengerhuntapi.repository.HuntRepository import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service +import java.time.LocalDateTime @Service class HuntService(private val huntRepository: HuntRepository) { @@ -30,6 +31,27 @@ class HuntService(private val huntRepository: HuntRepository) { return huntRepository.findAllOngoingByHunter(hunterId).map { it.toDomain() } } + fun getHuntsByEmail(email: String, status: HuntStatus?): List { + 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 { return huntRepository.save(hunt.toRecord()).toDomain() } diff --git a/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HunterService.kt b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HunterService.kt new file mode 100644 index 0000000..582b12b --- /dev/null +++ b/src/main/kotlin/net/halfbinary/scavengerhuntapi/service/HunterService.kt @@ -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") + } +} \ No newline at end of file