Compare commits
10 Commits
feature/te
...
1585b6eb7d
| Author | SHA1 | Date | |
|---|---|---|---|
| 1585b6eb7d | |||
| 1dd904055c | |||
| 5ca7a685dd | |||
| 863c824421 | |||
| 9324cf2eb0 | |||
| 30c66527b9 | |||
| 46132bb4fd | |||
| b2ba9ce676 | |||
| 4a1077833e | |||
| 1ed64cadd9 |
@@ -32,6 +32,9 @@ dependencies {
|
||||
val jakartaValidation = "3.1.1"
|
||||
val jsonWebToken = "0.13.0"
|
||||
val springdocUi = "3.0.3"
|
||||
val awsSdk = "2.26.0"
|
||||
val thumbnailator = "0.4.20"
|
||||
val tika = "3.3.0"
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
@@ -46,6 +49,10 @@ dependencies {
|
||||
implementation("io.jsonwebtoken:jjwt-impl:$jsonWebToken")
|
||||
implementation("io.jsonwebtoken:jjwt-jackson:$jsonWebToken")
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springdocUi")
|
||||
implementation(platform("software.amazon.awssdk:bom:$awsSdk"))
|
||||
implementation("software.amazon.awssdk:s3")
|
||||
implementation("net.coobird:thumbnailator:$thumbnailator")
|
||||
implementation("org.apache.tika:tika-core:$tika")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-actuator-test")
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.jsonwebtoken.Jwts
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.halfbinary.scavengerhuntapi.config
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
|
||||
import software.amazon.awssdk.regions.Region
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import software.amazon.awssdk.services.s3.S3Configuration
|
||||
import java.net.URI
|
||||
|
||||
@Configuration
|
||||
class S3Config(
|
||||
@Value("\${minio.endpoint}") private val endpoint: String,
|
||||
@Value("\${minio.access-key}") private val accessKey: String,
|
||||
@Value("\${minio.secret-key}") private val secretKey: String
|
||||
) {
|
||||
@Bean
|
||||
fun s3Client(): S3Client = S3Client.builder()
|
||||
.endpointOverride(URI.create(endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)))
|
||||
.region(Region.US_EAST_1)
|
||||
.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build())
|
||||
.build()
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.halfbinary.scavengerhuntapi.controller
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import jakarta.validation.Valid
|
||||
import net.halfbinary.scavengerhuntapi.config.JwtUtil
|
||||
import net.halfbinary.scavengerhuntapi.model.converter.toDomain
|
||||
@@ -13,28 +12,18 @@ import net.halfbinary.scavengerhuntapi.model.response.RefreshResponse
|
||||
import net.halfbinary.scavengerhuntapi.service.LoginService
|
||||
import net.halfbinary.scavengerhuntapi.service.RefreshTokenService
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import java.util.Collections
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
class AuthController(private val loginService: LoginService, private val jwtUtils: JwtUtil, private val refreshTokenService: RefreshTokenService) {
|
||||
@PostMapping("/login")
|
||||
fun login(@Valid @RequestBody body: LoginRequest, response: HttpServletResponse): ResponseEntity<LoginResponse> {
|
||||
fun login(@Valid @RequestBody body: LoginRequest): ResponseEntity<LoginResponse> {
|
||||
val result = loginService.login(body.toDomain())
|
||||
val hunterAuthorities =
|
||||
if (result.isAdmin) {
|
||||
SimpleGrantedAuthority("ROLE_ADMIN")
|
||||
} else {
|
||||
SimpleGrantedAuthority("ROLE_USER")
|
||||
}
|
||||
val user = User(result.email, result.password, Collections.singleton(hunterAuthorities))
|
||||
val accessToken = jwtUtils.generateToken(result.email)
|
||||
val refreshToken = refreshTokenService.generateRefreshToken(result.email)
|
||||
val loginResponse = LoginResponse(accessToken, refreshToken)
|
||||
@@ -47,7 +36,7 @@ class AuthController(private val loginService: LoginService, private val jwtUtil
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
fun logout(@RequestBody body: LogoutRequest, response: HttpServletResponse): ResponseEntity<String> {
|
||||
fun logout(@RequestBody body: LogoutRequest): ResponseEntity<String> {
|
||||
refreshTokenService.removeToken(body.refreshToken)
|
||||
return ResponseEntity.ok().build()
|
||||
}
|
||||
|
||||
@@ -11,16 +11,19 @@ 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
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("hunt")
|
||||
class HuntController(private val huntService: HuntService, private val hunterService: HunterService) {
|
||||
class HuntController(private val huntService: HuntService) {
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Gets the specified hunt information")
|
||||
@@ -29,24 +32,12 @@ class HuntController(private val huntService: HuntService, private val hunterSer
|
||||
}
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Tag(name = "Admin")
|
||||
@GetMapping()
|
||||
@GetMapping
|
||||
@Operation(summary = "Gets all Hunts")
|
||||
fun getAllHunts(@RequestParam status: HuntStatus?): ResponseEntity<List<HuntResponse>> {
|
||||
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>> {
|
||||
@@ -55,7 +46,7 @@ class HuntController(private val huntService: HuntService, private val hunterSer
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Tag(name = "Admin")
|
||||
@PostMapping()
|
||||
@PostMapping
|
||||
@Operation(summary = "Creates a new Hunt")
|
||||
fun createHunt(@Valid @RequestBody huntRequest: HuntCreateRequest): ResponseEntity<HuntResponse> {
|
||||
return ResponseEntity.ok(huntService.createHunt(huntRequest.toDomain()).toResponse())
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.halfbinary.scavengerhuntapi.controller
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamId
|
||||
import net.halfbinary.scavengerhuntapi.model.converter.toResponse
|
||||
import net.halfbinary.scavengerhuntapi.model.request.HuntStatus
|
||||
import net.halfbinary.scavengerhuntapi.model.response.HuntResponse
|
||||
import net.halfbinary.scavengerhuntapi.model.response.TeamResponse
|
||||
import net.halfbinary.scavengerhuntapi.service.HuntService
|
||||
import net.halfbinary.scavengerhuntapi.service.HunterService
|
||||
import net.halfbinary.scavengerhuntapi.service.TeamService
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/hunter")
|
||||
class HunterController(private val hunterService: HunterService,
|
||||
private val huntService: HuntService,
|
||||
private val teamService: TeamService) {
|
||||
|
||||
@GetMapping("/hunt/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() })
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/hunt/{huntId}/team/{teamId}")
|
||||
@Operation(summary = "Joins Hunter to specified Team for specified Hunt")
|
||||
fun joinTeamForHunt(@PathVariable huntId: HuntId, @PathVariable teamId: TeamId, authentication: Authentication) {
|
||||
teamService.joinTeam(teamId, authentication.name)
|
||||
}
|
||||
|
||||
@GetMapping("/hunt/{huntId}/team")
|
||||
@Operation(summary = "Gets the Team for the Hunter for the specified Hunt")
|
||||
fun getHunterHuntTeam(@PathVariable huntId: HuntId, authentication: Authentication): ResponseEntity<TeamResponse> {
|
||||
return ResponseEntity.ok(teamService.getTeamForHunterInHunt(huntId, authentication.name).toResponse())
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,23 @@
|
||||
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
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamHuntId
|
||||
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.ItemRequest
|
||||
import net.halfbinary.scavengerhuntapi.model.response.HuntResponse
|
||||
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.web.bind.annotation.*
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("hunt/{huntId}/item")
|
||||
@@ -24,18 +25,20 @@ class ItemController(private val huntService: HuntService) {
|
||||
|
||||
@GetMapping
|
||||
fun getItemsForHunt(@PathVariable huntId: HuntId): ResponseEntity<List<ItemResponse>> {
|
||||
TODO()
|
||||
return ResponseEntity.ok(huntService.getItemsForHunt(huntId).map { it.toResponse() })
|
||||
}
|
||||
|
||||
@GetMapping("/{itemId}")
|
||||
fun getItem(@PathVariable huntId: HuntId, @PathVariable itemId: ItemId): ResponseEntity<ItemResponse> {
|
||||
TODO()
|
||||
TODO("Get detailed information about the specified Item for the specified Hunt")
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Tag(name = "Admin")
|
||||
@PostMapping
|
||||
@Operation(summary = "Adds new Item to specified Hunt")
|
||||
fun addItemToHunt(@PathVariable huntId: HuntId, @Valid @RequestBody body: ItemRequest) {
|
||||
TODO()
|
||||
fun addItemToHunt(@PathVariable huntId: HuntId, @Valid @RequestBody body: ItemRequest): ResponseEntity<ItemResponse> {
|
||||
return ResponseEntity.ok(huntService.addItemToHunt(huntId, body.toDomain()).toResponse())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,13 +4,16 @@ import io.swagger.v3.oas.annotations.Operation
|
||||
import jakarta.validation.Valid
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
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.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.service.PhotoService
|
||||
import net.halfbinary.scavengerhuntapi.service.TeamService
|
||||
import org.springframework.core.io.InputStreamSource
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
@@ -18,11 +21,13 @@ import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
||||
@RestController
|
||||
@RequestMapping("hunt/{huntId}/team")
|
||||
class TeamController(private val teamService: TeamService) {
|
||||
class TeamController(private val teamService: TeamService, private val photoService: PhotoService) {
|
||||
@GetMapping
|
||||
@Operation(summary = "List all teams for the specified hunt")
|
||||
fun listHuntTeams(@PathVariable huntId: HuntId): ResponseEntity<List<TeamResponse>> {
|
||||
@@ -37,28 +42,54 @@ class TeamController(private val teamService: TeamService) {
|
||||
}
|
||||
|
||||
@GetMapping("/{teamId}")
|
||||
@Operation(summary = "Get team info for the specified hunt")
|
||||
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,
|
||||
@Operation(summary = "Get found/not found status and photo information about the Item for the specified Team, Hunt, and Item")
|
||||
fun getItemForTeam(@PathVariable huntId: HuntId,
|
||||
@PathVariable teamId: TeamId,
|
||||
@PathVariable itemId: ItemId): ResponseEntity<TeamItemResponse> {
|
||||
TODO()
|
||||
TODO("Get found/not found status about the Item for the specified Team, Hunt, and Item")
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/{teamId}/item/{itemId}/photo")
|
||||
fun getPhotosForTeam(@PathVariable huntId: HuntId,
|
||||
@Operation(summary = "Get list of photo information for the specified Team, Hunt, and Item")
|
||||
fun getItemPhotos(@PathVariable huntId: HuntId,
|
||||
@PathVariable teamId: TeamId,
|
||||
@PathVariable itemId: ItemId): ResponseEntity<PhotoResponse> {
|
||||
TODO()
|
||||
@PathVariable itemId: ItemId): ResponseEntity<List<PhotoResponse>> {
|
||||
TODO("Get list of photo information for the specified Team, Hunt, and Item")
|
||||
}
|
||||
|
||||
@GetMapping("/{teamId}/item/{itemId}/photo/{photoId}")
|
||||
@Operation(summary = "Get photo information for the specified Team, Hunt, Item, and Photo")
|
||||
fun getPhotoInfo(@PathVariable huntId: HuntId,
|
||||
@PathVariable teamId: TeamId,
|
||||
@PathVariable itemId: ItemId,
|
||||
@PathVariable photoId: PhotoId): ResponseEntity<PhotoResponse> {
|
||||
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")
|
||||
}
|
||||
|
||||
@GetMapping("/{teamId}/item/{itemId}/photo/{photoId}/file")
|
||||
@Operation(summary = "Get the binary image information for the specified Team, Hunt, Item, and Photo")
|
||||
fun getPhoto(@PathVariable huntId: HuntId,
|
||||
@PathVariable teamId: TeamId,
|
||||
@PathVariable itemId: ItemId,
|
||||
@PathVariable photoId: PhotoId): ResponseEntity<InputStreamSource> {
|
||||
TODO("Get the binary image information for the specified Team, Hunt, Item, and Photo")
|
||||
}
|
||||
|
||||
@PostMapping("/{teamId}/item/{itemId}/photo")
|
||||
@Operation(summary = "Save photo information and store the binary file")
|
||||
fun submitPhoto(@PathVariable huntId: HuntId,
|
||||
@PathVariable teamId: TeamId,
|
||||
@PathVariable itemId: ItemId,
|
||||
authentication: Authentication,
|
||||
@RequestParam file: MultipartFile) {
|
||||
photoService.submitPhoto(huntId, itemId, authentication.name, file)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.halfbinary.scavengerhuntapi.error
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
||||
import net.halfbinary.scavengerhuntapi.error.exception.InvalidEmailException
|
||||
import net.halfbinary.scavengerhuntapi.error.exception.LoginFailedException
|
||||
import net.halfbinary.scavengerhuntapi.error.exception.NotFoundException
|
||||
@@ -12,6 +13,8 @@ import org.springframework.web.bind.MethodArgumentNotValidException
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.ResponseStatus
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException
|
||||
import java.net.SocketTimeoutException
|
||||
|
||||
|
||||
@RestControllerAdvice
|
||||
@@ -68,6 +71,24 @@ class ExceptionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler(BadFileException::class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
fun badFileException(e: BadFileException): String? {
|
||||
return e.message
|
||||
}
|
||||
|
||||
@ExceptionHandler(MaxUploadSizeExceededException::class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
fun maxUploadSizeExceededException(e: MaxUploadSizeExceededException): String? {
|
||||
return e.message
|
||||
}
|
||||
|
||||
@ExceptionHandler(SocketTimeoutException::class)
|
||||
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
|
||||
fun socketTimeoutException(): String {
|
||||
return "Unable to connect. Try again later."
|
||||
}
|
||||
|
||||
private fun simpleMap(key: String, value: String?): Map<String, String?> {
|
||||
return mapOf(Pair(key, value))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package net.halfbinary.scavengerhuntapi.error.exception
|
||||
|
||||
class BadFileException(override val message: String): RuntimeException(message)
|
||||
@@ -1,3 +1,3 @@
|
||||
package net.halfbinary.scavengerhuntapi.error.exception
|
||||
|
||||
class LoginFailedException(): RuntimeException("The email and password combination is not correct.")
|
||||
class LoginFailedException : RuntimeException("The email and password combination is not correct.")
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.halfbinary.scavengerhuntapi.model
|
||||
|
||||
enum class PhotoStatus {
|
||||
SUBMITTED,
|
||||
APPROVED,
|
||||
REJECTED,
|
||||
REMOVED
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package net.halfbinary.scavengerhuntapi.model
|
||||
|
||||
import java.util.*
|
||||
|
||||
typealias FoundId = UUID
|
||||
typealias HuntId = UUID
|
||||
typealias HunterId = UUID
|
||||
typealias ItemId = UUID
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.converter
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.domain.HuntItem
|
||||
import net.halfbinary.scavengerhuntapi.model.record.HuntItemRecord
|
||||
|
||||
fun HuntItem.toRecord() = HuntItemRecord(id = id, huntId = huntId, itemId = itemId)
|
||||
|
||||
fun HuntItemRecord.toDomain() = HuntItem(id = id, huntId = huntId, itemId = itemId)
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.converter
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.domain.Item
|
||||
import net.halfbinary.scavengerhuntapi.model.record.ItemRecord
|
||||
import net.halfbinary.scavengerhuntapi.model.request.ItemRequest
|
||||
import net.halfbinary.scavengerhuntapi.model.response.ItemResponse
|
||||
|
||||
fun ItemRequest.toDomain() = Item(name = name, points = points)
|
||||
|
||||
fun Item.toRecord() = ItemRecord(id = id, name = name, points = points)
|
||||
|
||||
fun ItemRecord.toDomain() = Item(id = id, name = name, points = points)
|
||||
|
||||
fun Item.toResponse() = ItemResponse(id = id, name = name, points = points)
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.converter
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.domain.Hunter
|
||||
import net.halfbinary.scavengerhuntapi.model.domain.Photo
|
||||
import net.halfbinary.scavengerhuntapi.model.record.PhotoRecord
|
||||
import net.halfbinary.scavengerhuntapi.model.response.PhotoResponse
|
||||
|
||||
fun Photo.toRecord() = PhotoRecord(
|
||||
id = id,
|
||||
itemId = itemId,
|
||||
huntId = huntId,
|
||||
hunterId = hunterId,
|
||||
foundDateTime = foundDateTime,
|
||||
status = status,
|
||||
statusChangeDateTime = statusChangeDateTime
|
||||
)
|
||||
|
||||
fun Photo.toResponse(hunter: Hunter) = PhotoResponse(
|
||||
id = id,
|
||||
hunterName = hunter.name,
|
||||
photoUploadDateTime = foundDateTime,
|
||||
photoStatus = status,
|
||||
photoStatusChangeDateTime = statusChangeDateTime
|
||||
)
|
||||
@@ -1,11 +1,7 @@
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.domain
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import java.util.*
|
||||
|
||||
data class HuntItem(
|
||||
val id: UUID = UUID.randomUUID(),
|
||||
val huntId: HuntId,
|
||||
val itemId: ItemId
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.domain
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import java.util.*
|
||||
|
||||
data class Item(
|
||||
val id: ItemId = UUID.randomUUID(),
|
||||
val name: String,
|
||||
val points: Int
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.domain
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
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.util.UUID
|
||||
|
||||
data class Photo(
|
||||
val id: PhotoId = UUID.randomUUID(),
|
||||
val itemId: ItemId,
|
||||
val huntId: HuntId,
|
||||
val hunterId: HunterId,
|
||||
val foundDateTime: LocalDateTime,
|
||||
val status: PhotoStatus,
|
||||
val statusChangeDateTime: LocalDateTime
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.domain
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamId
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
data class Team(
|
||||
val id: TeamId = UUID.randomUUID(),
|
||||
|
||||
@@ -3,7 +3,6 @@ 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(),
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.record
|
||||
|
||||
import jakarta.persistence.Entity
|
||||
import jakarta.persistence.Id
|
||||
import jakarta.persistence.Table
|
||||
import net.halfbinary.scavengerhuntapi.model.*
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Represents a found Item for a Hunt by a Hunter
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "found")
|
||||
data class FoundRecord(
|
||||
@Id
|
||||
val id: FoundId,
|
||||
val itemId: ItemId,
|
||||
val huntId: HuntId,
|
||||
val hunterId: HunterId,
|
||||
val foundDateTime: LocalDateTime,
|
||||
val imageName: String,
|
||||
val status: FoundStatus
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.record
|
||||
|
||||
import jakarta.persistence.Entity
|
||||
import jakarta.persistence.Id
|
||||
import jakarta.persistence.Table
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
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
|
||||
|
||||
/**
|
||||
* Represents a found Item for a Hunt by a Hunter
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "photo")
|
||||
data class PhotoRecord(
|
||||
@Id
|
||||
val id: PhotoId,
|
||||
val itemId: ItemId,
|
||||
val huntId: HuntId,
|
||||
val hunterId: HunterId,
|
||||
val foundDateTime: LocalDateTime,
|
||||
val status: PhotoStatus,
|
||||
val statusChangeDateTime: LocalDateTime,
|
||||
)
|
||||
@@ -6,7 +6,6 @@ import jakarta.persistence.Table
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamHuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamId
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
@Table(name = "team_hunt")
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package net.halfbinary.scavengerhuntapi.model.response
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.PhotoId
|
||||
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class PhotoResponse(
|
||||
val id: PhotoId,
|
||||
val hunterName: String,
|
||||
val photoUploadDateTime: LocalDateTime
|
||||
val photoUploadDateTime: LocalDateTime,
|
||||
val photoStatus: PhotoStatus,
|
||||
val photoStatusChangeDateTime: LocalDateTime,
|
||||
)
|
||||
|
||||
@@ -2,10 +2,11 @@ package net.halfbinary.scavengerhuntapi.model.response
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.FoundStatus
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class TeamItemResponse(
|
||||
val id: ItemId,
|
||||
val itemName: String,
|
||||
val hunterName: String,
|
||||
val itemFoundStatus: FoundStatus
|
||||
val hunterName: String?,
|
||||
val itemFoundStatus: FoundStatus,
|
||||
val itemStatusChangeDateTime: LocalDateTime,
|
||||
)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.FoundId
|
||||
import net.halfbinary.scavengerhuntapi.model.record.FoundRecord
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface FoundRepository : JpaRepository<FoundRecord, FoundId>
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
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>
|
||||
@@ -2,7 +2,6 @@ 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
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.HunterId
|
||||
import net.halfbinary.scavengerhuntapi.model.record.HunterTeamRecord
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
@Repository
|
||||
interface HunterTeamRepository : JpaRepository<HunterTeamRecord, UUID>
|
||||
interface HunterTeamRepository : JpaRepository<HunterTeamRecord, UUID> {
|
||||
fun findByHunterId(hunterId: HunterId): List<HunterTeamRecord>
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import net.halfbinary.scavengerhuntapi.model.record.ItemRecord
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface ItemRepository : JpaRepository<ItemRecord, ItemId>
|
||||
interface ItemRepository : JpaRepository<ItemRecord, ItemId> {
|
||||
@Query("""
|
||||
SELECT i.*
|
||||
FROM item i
|
||||
INNER JOIN hunt_item hi ON i.id = hi.item_id
|
||||
WHERE hi.hunt_id = :huntId
|
||||
""", nativeQuery = true)
|
||||
fun findAllByHuntId(huntId: HuntId): List<ItemRecord>
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import net.halfbinary.scavengerhuntapi.model.PhotoId
|
||||
import net.halfbinary.scavengerhuntapi.model.record.PhotoRecord
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface PhotoRepository : JpaRepository<PhotoRecord, PhotoId> {
|
||||
@Query("""
|
||||
SELECT *
|
||||
FROM photo p
|
||||
WHERE
|
||||
p.
|
||||
""", nativeQuery = true)
|
||||
fun findPhotosByItemId(itemId: ItemId): List<PhotoRecord>
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.halfbinary.scavengerhuntapi.repository
|
||||
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.TeamId
|
||||
import net.halfbinary.scavengerhuntapi.model.record.TeamRecord
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.halfbinary.scavengerhuntapi.service
|
||||
|
||||
import org.apache.tika.Tika
|
||||
import org.apache.tika.config.TikaConfig
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
|
||||
@Service
|
||||
class FileProbeService {
|
||||
private val tika = Tika()
|
||||
|
||||
fun getFileType(fileBytes: ByteArray): String {
|
||||
return tika.detect(fileBytes)
|
||||
}
|
||||
|
||||
fun getFileExtension(fileType: String): String {
|
||||
return TikaConfig.getDefaultConfig().mimeRepository.forName(fileType).extension
|
||||
}
|
||||
fun isImageType(fileType: String): Boolean {
|
||||
return fileType.startsWith("image")
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,24 @@ import net.halfbinary.scavengerhuntapi.model.HunterId
|
||||
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.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
|
||||
|
||||
@Service
|
||||
class HuntService(private val huntRepository: HuntRepository) {
|
||||
class HuntService(
|
||||
private val huntRepository: HuntRepository,
|
||||
private val itemRepository: ItemRepository,
|
||||
private val huntItemRepository: HuntItemRepository
|
||||
) {
|
||||
fun getHunt(huntId: HuntId): Hunt {
|
||||
return huntRepository.findByIdOrNull(huntId)?.toDomain() ?: throw NotFoundException("No hunt with id ${huntId} found")
|
||||
return huntRepository.findByIdOrNull(huntId)?.toDomain() ?: throw NotFoundException("No hunt with id $huntId found")
|
||||
}
|
||||
|
||||
fun getAllHunts(status: HuntStatus?): List<Hunt> {
|
||||
@@ -55,4 +63,16 @@ class HuntService(private val huntRepository: HuntRepository) {
|
||||
fun createHunt(hunt: Hunt): Hunt {
|
||||
return huntRepository.save(hunt.toRecord()).toDomain()
|
||||
}
|
||||
|
||||
fun getItemsForHunt(huntId: HuntId): List<Item> {
|
||||
huntRepository.findByIdOrNull(huntId) ?: throw NotFoundException("No hunt with id $huntId found")
|
||||
return itemRepository.findAllByHuntId(huntId).map { it.toDomain() }
|
||||
}
|
||||
|
||||
fun addItemToHunt(huntId: HuntId, item: Item): Item {
|
||||
huntRepository.findByIdOrNull(huntId) ?: throw NotFoundException("No hunt with id $huntId found")
|
||||
val savedItem = itemRepository.save(item.toRecord()).toDomain()
|
||||
huntItemRepository.save(HuntItem(huntId = huntId, itemId = savedItem.id).toRecord())
|
||||
return savedItem
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.stereotype.Service
|
||||
import java.util.Collections
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Service
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package net.halfbinary.scavengerhuntapi.service
|
||||
|
||||
import net.coobird.thumbnailator.Thumbnails
|
||||
import net.coobird.thumbnailator.tasks.UnsupportedFormatException
|
||||
import net.halfbinary.scavengerhuntapi.error.exception.BadFileException
|
||||
import net.halfbinary.scavengerhuntapi.model.HuntId
|
||||
import net.halfbinary.scavengerhuntapi.model.ItemId
|
||||
import net.halfbinary.scavengerhuntapi.model.PhotoStatus
|
||||
import net.halfbinary.scavengerhuntapi.model.converter.toRecord
|
||||
import net.halfbinary.scavengerhuntapi.model.domain.Photo
|
||||
import net.halfbinary.scavengerhuntapi.repository.PhotoRepository
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Service
|
||||
class PhotoService(
|
||||
private val photoRepository: PhotoRepository,
|
||||
private val hunterService: HunterService,
|
||||
private val s3StorageService: S3StorageService,
|
||||
private val fileProbeService: FileProbeService
|
||||
) {
|
||||
fun submitPhoto(huntId: HuntId, itemId: ItemId, email: String, file: MultipartFile) {
|
||||
val originalBytes = file.bytes
|
||||
val fileType = fileProbeService.getFileType(originalBytes)
|
||||
|
||||
if(!fileProbeService.isImageType(fileType)) throw BadFileException("Not an image")
|
||||
|
||||
val originalAsJpeg = try {
|
||||
toJpeg(file.bytes)
|
||||
} catch (_: UnsupportedFormatException) {
|
||||
throw BadFileException("Image type is not supported")
|
||||
}
|
||||
|
||||
val hunter = hunterService.getHunterByEmail(email)
|
||||
val now = LocalDateTime.now()
|
||||
val photo = Photo(
|
||||
itemId = itemId,
|
||||
huntId = huntId,
|
||||
hunterId = hunter.id,
|
||||
foundDateTime = now,
|
||||
status = PhotoStatus.SUBMITTED,
|
||||
statusChangeDateTime = now
|
||||
)
|
||||
val savedRecord = photoRepository.save(photo.toRecord())
|
||||
val baseName = savedRecord.id.toString()
|
||||
|
||||
s3StorageService.upload("$baseName${fileProbeService.getFileExtension(fileType)}", originalBytes, fileType)
|
||||
s3StorageService.upload("${baseName}_large.jpg", originalAsJpeg, MediaType.IMAGE_JPEG_VALUE)
|
||||
s3StorageService.upload("${baseName}_medium.jpg", resize(originalBytes, 800), MediaType.IMAGE_JPEG_VALUE)
|
||||
s3StorageService.upload("${baseName}_small.jpg", resize(originalBytes, 200), MediaType.IMAGE_JPEG_VALUE)
|
||||
}
|
||||
|
||||
private fun toJpeg(bytes: ByteArray): ByteArray {
|
||||
val output = ByteArrayOutputStream()
|
||||
Thumbnails.of(ByteArrayInputStream(bytes))
|
||||
.scale(1.0)
|
||||
.outputFormat("jpg")
|
||||
.toOutputStream(output)
|
||||
return output.toByteArray()
|
||||
}
|
||||
|
||||
private fun resize(bytes: ByteArray, width: Int): ByteArray {
|
||||
val output = ByteArrayOutputStream()
|
||||
Thumbnails.of(ByteArrayInputStream(bytes))
|
||||
.width(width)
|
||||
.outputFormat("jpg")
|
||||
.toOutputStream(output)
|
||||
return output.toByteArray()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.halfbinary.scavengerhuntapi.service
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Service
|
||||
import software.amazon.awssdk.core.sync.RequestBody
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
||||
|
||||
@Service
|
||||
class S3StorageService(
|
||||
private val s3Client: S3Client,
|
||||
@Value("\${minio.bucket}") private val bucket: String
|
||||
) {
|
||||
fun upload(key: String, bytes: ByteArray, contentType: String) {
|
||||
s3Client.putObject(
|
||||
PutObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(key)
|
||||
.contentType(contentType)
|
||||
.contentLength(bytes.size.toLong())
|
||||
.build(),
|
||||
RequestBody.fromBytes(bytes)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,13 @@ import net.halfbinary.scavengerhuntapi.model.converter.toRecord
|
||||
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 java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
@Service
|
||||
class TeamService(
|
||||
@@ -44,6 +41,14 @@ class TeamService(
|
||||
.elementAt(0)
|
||||
}
|
||||
|
||||
fun getTeamForHunterInHunt(huntId: HuntId, email: String): Team {
|
||||
val hunter = hunterRepository.findByEmail(email) ?: throw NotFoundException("No hunter with email $email found")
|
||||
val hunterTeamIds = hunterTeamRepository.findByHunterId(hunter.id).map { it.teamId }.toSet()
|
||||
return getTeamsForHunt(huntId)
|
||||
.firstOrNull { it.id in hunterTeamIds }
|
||||
?: throw NotFoundException("No team found for hunter $email in hunt $huntId")
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
@@ -12,6 +12,14 @@ spring.datasource.password=${DB_PASSWORD}
|
||||
jwt.secret=${JWT_SECRET}
|
||||
jwt.expiration=300000
|
||||
|
||||
minio.endpoint=${MINIO_ENDPOINT}
|
||||
minio.access-key=${MINIO_ACCESS_KEY}
|
||||
minio.secret-key=${MINIO_SECRET_KEY}
|
||||
minio.bucket=${MINIO_BUCKET}
|
||||
|
||||
spring.servlet.multipart.max-file-size=25MB
|
||||
spring.servlet.multipart.max-request-size=25MB
|
||||
|
||||
springdoc.api-docs.enabled=true
|
||||
springdoc.api-docs.path=/docs/api-docs
|
||||
springdoc.swagger-ui.enabled=true
|
||||
|
||||
Reference in New Issue
Block a user