Adds various team endpoints

This commit is contained in:
2026-05-12 10:10:55 -05:00
parent 46a78bfc08
commit fd754a7ee7
13 changed files with 182 additions and 11 deletions

View File

@@ -1,10 +1,18 @@
package net.halfbinary.scavengerhuntapi.controller package net.halfbinary.scavengerhuntapi.controller
import io.swagger.v3.oas.annotations.Operation
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.ItemId
import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
import net.halfbinary.scavengerhuntapi.model.request.TeamRequest import net.halfbinary.scavengerhuntapi.model.request.TeamRequest
import net.halfbinary.scavengerhuntapi.model.response.PhotoResponse
import net.halfbinary.scavengerhuntapi.model.response.TeamItemResponse
import net.halfbinary.scavengerhuntapi.model.response.TeamResponse import net.halfbinary.scavengerhuntapi.model.response.TeamResponse
import net.halfbinary.scavengerhuntapi.service.TeamService
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
@@ -13,15 +21,44 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
@RestController @RestController
@RequestMapping("hunt/{id}/team") @RequestMapping("hunt/{huntId}/team")
class TeamController { class TeamController(private val teamService: TeamService) {
@GetMapping @GetMapping
fun listHuntTeams(@PathVariable id: HuntId): ResponseEntity<List<TeamResponse>> { @Operation(summary = "List all teams for the specified hunt")
TODO() fun listHuntTeams(@PathVariable huntId: HuntId): ResponseEntity<List<TeamResponse>> {
return ResponseEntity.ok(teamService.getListOfTeamsForHunt(huntId).map { it.toResponse()})
} }
@PostMapping @PostMapping
fun createHuntTeam(@PathVariable id: HuntId, @Valid @RequestBody team: TeamRequest) { @Operation(summary = "Create a new team for the specified hunt")
fun createHuntTeam(@PathVariable huntId: HuntId, @Valid @RequestBody team: TeamRequest) {
val teamResponse = teamService.createTeam(team.name)
teamService.addTeamToHunt(huntId, teamResponse.id)
}
@GetMapping("/{teamId}")
fun getTeam(@PathVariable huntId: HuntId, @PathVariable teamId: TeamId): ResponseEntity<TeamResponse> {
return ResponseEntity.ok(teamService.getTeamFromHunt(huntId, teamId).toResponse())
}
@PostMapping("/{teamId}")
fun joinTeamForHunt(@PathVariable huntId: HuntId, @PathVariable teamId: TeamId, authentication: Authentication) {
teamService.joinTeam(teamId, authentication.name)
}
@GetMapping("/{teamId}/item/{itemId}")
fun getItemsForTeam(@PathVariable huntId: HuntId,
@PathVariable teamId: TeamId,
@PathVariable itemId: ItemId): ResponseEntity<TeamItemResponse> {
TODO() TODO()
} }
@GetMapping("/{teamId}/item/{itemId}/photo")
fun getPhotosForTeam(@PathVariable huntId: HuntId,
@PathVariable teamId: TeamId,
@PathVariable itemId: ItemId): ResponseEntity<PhotoResponse> {
TODO()
}
} }

View File

@@ -1,6 +1,7 @@
package net.halfbinary.scavengerhuntapi.model package net.halfbinary.scavengerhuntapi.model
enum class FoundStatus { enum class FoundStatus {
NOT_FOUND,
SUBMITTED, SUBMITTED,
APPROVED, APPROVED,
REJECTED, REJECTED,

View File

@@ -7,4 +7,6 @@ typealias HuntId = UUID
typealias HunterId = UUID typealias HunterId = UUID
typealias ItemId = UUID typealias ItemId = UUID
typealias TeamId = UUID typealias TeamId = UUID
typealias RefreshId = UUID typealias RefreshId = UUID
typealias TeamHuntId = UUID
typealias PhotoId = UUID

View File

@@ -0,0 +1,16 @@
package net.halfbinary.scavengerhuntapi.model.converter
import net.halfbinary.scavengerhuntapi.model.domain.Team
import net.halfbinary.scavengerhuntapi.model.domain.TeamHunt
import net.halfbinary.scavengerhuntapi.model.record.TeamHuntRecord
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
import net.halfbinary.scavengerhuntapi.model.request.TeamRequest
import net.halfbinary.scavengerhuntapi.model.response.TeamResponse
fun TeamHunt.toRecord(): TeamHuntRecord {
return TeamHuntRecord(id, teamId, huntId)
}
fun TeamHuntRecord.toDomain(): TeamHunt {
return TeamHunt(id, teamId, huntId)
}

View File

@@ -0,0 +1,12 @@
package net.halfbinary.scavengerhuntapi.model.domain
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamHuntId
import net.halfbinary.scavengerhuntapi.model.TeamId
import java.util.UUID
data class TeamHunt(
val id: TeamHuntId = TeamHuntId.randomUUID(),
val teamId: TeamId,
val huntId: HuntId
)

View File

@@ -4,6 +4,7 @@ import jakarta.persistence.Entity
import jakarta.persistence.Id import jakarta.persistence.Id
import jakarta.persistence.Table import jakarta.persistence.Table
import net.halfbinary.scavengerhuntapi.model.HuntId import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamHuntId
import net.halfbinary.scavengerhuntapi.model.TeamId import net.halfbinary.scavengerhuntapi.model.TeamId
import java.util.* import java.util.*
@@ -11,7 +12,7 @@ import java.util.*
@Table(name = "team_hunt") @Table(name = "team_hunt")
data class TeamHuntRecord( data class TeamHuntRecord(
@Id @Id
val id: UUID, val id: TeamHuntId,
val teamId: TeamId, val teamId: TeamId,
val huntId: HuntId val huntId: HuntId
) )

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.model.request
import jakarta.validation.constraints.NotBlank
import net.halfbinary.scavengerhuntapi.model.TeamId
data class JoinTeamRequest(
@field:NotBlank
val teamId: TeamId
)

View File

@@ -0,0 +1,10 @@
package net.halfbinary.scavengerhuntapi.model.response
import net.halfbinary.scavengerhuntapi.model.PhotoId
import java.time.LocalDateTime
data class PhotoResponse(
val id: PhotoId,
val hunterName: String,
val photoUploadDateTime: LocalDateTime
)

View File

@@ -0,0 +1,11 @@
package net.halfbinary.scavengerhuntapi.model.response
import net.halfbinary.scavengerhuntapi.model.FoundStatus
import net.halfbinary.scavengerhuntapi.model.ItemId
data class TeamItemResponse(
val id: ItemId,
val itemName: String,
val hunterName: String,
val itemFoundStatus: FoundStatus
)

View File

@@ -0,0 +1,9 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.record.HunterTeamRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import java.util.UUID
@Repository
interface HunterTeamRepository : JpaRepository<HunterTeamRecord, UUID>

View File

@@ -0,0 +1,28 @@
package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.record.HuntRecord
import net.halfbinary.scavengerhuntapi.model.record.TeamHuntRecord
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.util.*
@Repository
interface TeamHuntRepository : JpaRepository<TeamHuntRecord, UUID> {
@Query("""
SELECT h.* FROM hunt h
INNER JOIN team_hunt th ON h.id = th.hunt_id
WHERE th.team_id = :teamId
""", nativeQuery = true)
fun findHuntsByTeamId(teamId: TeamId): List<HuntRecord>
@Query("""
SELECT t.* FROM team t
INNER JOIN team_hunt th ON t.id = th.team_id
WHERE th.hunt_id = :huntId
""", nativeQuery = true)
fun findTeamsByHuntId(huntId: HuntId): List<TeamRecord>
}

View File

@@ -1,5 +1,6 @@
package net.halfbinary.scavengerhuntapi.repository package net.halfbinary.scavengerhuntapi.repository
import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamId import net.halfbinary.scavengerhuntapi.model.TeamId
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository

View File

@@ -1,21 +1,55 @@
package net.halfbinary.scavengerhuntapi.service package net.halfbinary.scavengerhuntapi.service
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
import net.halfbinary.scavengerhuntapi.model.HuntId import net.halfbinary.scavengerhuntapi.model.HuntId
import net.halfbinary.scavengerhuntapi.model.TeamId 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.Team import net.halfbinary.scavengerhuntapi.model.domain.Team
import net.halfbinary.scavengerhuntapi.model.domain.TeamHunt
import net.halfbinary.scavengerhuntapi.model.record.HunterTeamRecord
import net.halfbinary.scavengerhuntapi.model.record.TeamHuntRecord
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
import net.halfbinary.scavengerhuntapi.model.request.TeamRequest
import net.halfbinary.scavengerhuntapi.model.response.TeamResponse
import net.halfbinary.scavengerhuntapi.repository.HunterRepository
import net.halfbinary.scavengerhuntapi.repository.HunterTeamRepository
import net.halfbinary.scavengerhuntapi.repository.TeamHuntRepository
import net.halfbinary.scavengerhuntapi.repository.TeamRepository
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.util.UUID
@Service @Service
class TeamService { class TeamService(
private val teamRepository: TeamRepository,
private val teamHuntRepository: TeamHuntRepository,
private val hunterRepository: HunterRepository,
private val hunterTeamRepository: HunterTeamRepository,
) {
fun getListOfTeamsForHunt(huntId: HuntId): List<Team> { fun getListOfTeamsForHunt(huntId: HuntId): List<Team> {
TODO() return getTeamsForHunt(huntId)
} }
fun createTeam(name: String): Team { fun createTeam(name: String): Team {
TODO() return teamRepository.save(TeamRequest(name).toDomain().toRecord()).toDomain()
} }
fun addTeamToHunt(huntId: HuntId, teamId: TeamId) { fun addTeamToHunt(huntId: HuntId, teamId: TeamId) {
TODO() teamHuntRepository.save(TeamHunt(teamId = teamId, huntId = huntId).toRecord()).toDomain()
}
fun getTeamFromHunt(huntId: HuntId, teamId: TeamId): Team {
return getTeamsForHunt(huntId)
.filter { it.id == teamId }
.elementAt(0)
}
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))
}
private fun getTeamsForHunt(huntId: HuntId): List<Team> {
return teamHuntRepository.findTeamsByHuntId(huntId).map { it.toDomain() }
} }
} }