Skip to content

Commit

Permalink
回傳前端Exception Message格式化 (#126)
Browse files Browse the repository at this point in the history
* feat except message format
  • Loading branch information
m1a2st authored Jul 25, 2023
1 parent 065f026 commit d60d3bb
Show file tree
Hide file tree
Showing 17 changed files with 130 additions and 55 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import tw.waterballsa.gaas.application.repositories.UserRepository
import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.domain.User
import tw.waterballsa.gaas.exceptions.NotFoundException.Companion.notFound
import tw.waterballsa.gaas.exceptions.enums.PlatformError.ROOM_NOT_FOUND

abstract class AbstractRoomUseCase(
protected val roomRepository: RoomRepository,
Expand All @@ -14,14 +15,14 @@ abstract class AbstractRoomUseCase(
protected fun findPlayerByIdentity(userIdentity: String): Room.Player =
userRepository.findByIdentity(userIdentity)
?.toRoomPlayer()
?: throw notFound(User::class).message()
?: throw notFound(ROOM_NOT_FOUND, User::class).message()

protected fun findRoomById(roomId: String, messageWithId: Boolean = true): Room {
val id = Room.Id(roomId)
val notFoundException = if (messageWithId) {
notFound(Room::class).id(id)
notFound(ROOM_NOT_FOUND, Room::class).id(id)
} else {
notFound(Room::class).message()
notFound(ROOM_NOT_FOUND, Room::class).message()
}
return roomRepository.findById(id)
?: throw notFoundException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package tw.waterballsa.gaas.application.usecases

import tw.waterballsa.gaas.application.repositories.RoomRepository
import tw.waterballsa.gaas.application.repositories.UserRepository
import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.exceptions.PlatformException
import javax.inject.Named

@Named
Expand All @@ -20,12 +18,6 @@ class CloseRoomUsecase(
}
}

private fun Room.validateRoomHost(userId: Room.Player.Id) {
if (host.id != userId) {
throw PlatformException("Player($userId) is not the host")
}
}

data class Request(
val roomId: String,
val userIdentity: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import tw.waterballsa.gaas.domain.User
import tw.waterballsa.gaas.events.CreatedRoomEvent
import tw.waterballsa.gaas.exceptions.NotFoundException.Companion.notFound
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.GAME_NOT_FOUND
import tw.waterballsa.gaas.exceptions.enums.PlatformError.PLAYER_JOIN_ROOM_ERROR
import javax.inject.Named

@Named
Expand All @@ -33,7 +35,7 @@ class CreateRoomUsecase(

private fun Player.ensureHostWouldNotCreatedRoomAgain() {
if (roomRepository.existsByHostId(User.Id(id.value))) {
throw PlatformException("A user can only create one room at a time.")
throw PlatformException(PLAYER_JOIN_ROOM_ERROR, "A user can only create one room at a time.")
}
}

Expand All @@ -44,7 +46,7 @@ class CreateRoomUsecase(

private fun findGameRegistrationById(gameId: String) =
gameRegistrationRepository.findById(GameRegistration.Id(gameId))
?: throw notFound(GameRegistration::class).id(gameId)
?: throw notFound(GAME_NOT_FOUND, GameRegistration::class).id(gameId)

data class Request(
val name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tw.waterballsa.gaas.application.usecases
import tw.waterballsa.gaas.application.repositories.UserRepository
import tw.waterballsa.gaas.domain.User
import tw.waterballsa.gaas.exceptions.NotFoundException.Companion.notFound
import tw.waterballsa.gaas.exceptions.enums.PlatformError.USER_NOT_FOUND
import javax.inject.Named

@Named
Expand All @@ -12,7 +13,7 @@ class GetUserUseCase(
fun execute(request: Request, presenter: Presenter) {
with(request) {
val user = userRepository.findByIdentity(userIdentity)
?: throw notFound(User::class).identifyBy("userIdentity", userIdentity)
?: throw notFound(USER_NOT_FOUND, User::class).identifyBy("userIdentity", userIdentity)
presenter.present(user)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import tw.waterballsa.gaas.application.repositories.UserRepository
import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.domain.User
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.PLAYER_JOIN_ROOM_ERROR
import tw.waterballsa.gaas.exceptions.enums.PlatformError.ROOM_FULL
import tw.waterballsa.gaas.exceptions.enums.PlatformError.ROOM_PASSWORD_INCORRECT
import javax.inject.Named

@Named
Expand All @@ -29,19 +32,22 @@ class JoinRoomUsecase(
private fun validatePlayerJoinedRoom(player: Room.Player) {
val hasJoined = roomRepository.hasPlayerJoinedRoom(User.Id(player.id.value))
if (hasJoined) {
throw PlatformException("Player(${player.id.value}) has joined another room.")
throw PlatformException(PLAYER_JOIN_ROOM_ERROR, "Player(${player.id.value}) has joined another room.")
}
}

private fun Room.validateRoomPassword(password: String?) {
if (isLocked && !isPasswordCorrect(password)) {
throw PlatformException("wrong password")
throw PlatformException(ROOM_PASSWORD_INCORRECT, "wrong password")
}
}

private fun Room.validateFullRoom() {
if (isFull()) {
throw PlatformException("The room ($roomId) is full. Please select another room or try again later.")
throw PlatformException(
ROOM_FULL,
"The room ($roomId) is full. Please select another room or try again later.",
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package tw.waterballsa.gaas.application.usecases

import tw.waterballsa.gaas.application.eventbus.EventBus
import tw.waterballsa.gaas.application.exceptions.GameAlreadyExistsException
import tw.waterballsa.gaas.application.repositories.GameRegistrationRepository
import tw.waterballsa.gaas.domain.GameRegistration
import tw.waterballsa.gaas.events.RegisteredGameEvent
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.GAME_EXISTS
import javax.inject.Named

@Named
Expand All @@ -16,7 +17,11 @@ class RegisterGameUsecase(
gameRegistrationRepository.run {
val uniqueName = request.uniqueName
val gameRegistration = when {
existsByUniqueName(uniqueName) -> throw GameAlreadyExistsException(uniqueName)
existsByUniqueName(uniqueName) -> throw PlatformException(
GAME_EXISTS,
"$uniqueName already exists",
)

else -> registerGame(request.toGameRegistration())
}
gameRegistration.toRegisteredGameEvent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import tw.waterballsa.gaas.domain.User
import tw.waterballsa.gaas.events.UserUpdatedEvent
import tw.waterballsa.gaas.exceptions.NotFoundException.Companion.notFound
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.USER_NAME_DUPLICATED
import tw.waterballsa.gaas.exceptions.enums.PlatformError.USER_NOT_FOUND
import javax.inject.Named

@Named
Expand All @@ -28,13 +30,13 @@ class UpdateUserUseCase(

private fun validateNicknameDuplicated(nickname: String) {
if (userRepository.existsUserByNickname(nickname)) {
throw PlatformException("invalid nickname: duplicated")
throw PlatformException(USER_NAME_DUPLICATED, "invalid nickname: duplicated")
}
}

private fun findUserByIdentity(userIdentity: String) =
userRepository.findByIdentity(userIdentity)
?: throw notFound(User::class).identifyBy("userIdentity", userIdentity)
?: throw notFound(USER_NOT_FOUND, User::class).identifyBy("userIdentity", userIdentity)

data class Request(val userIdentity: String, val nickname: String)
}
Expand Down
18 changes: 13 additions & 5 deletions domain/src/main/kotlin/tw/waterballsa/gaas/domain/Room.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tw.waterballsa.gaas.domain

import tw.waterballsa.gaas.domain.Room.Status.WAITING
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.PLAYER_NOT_FOUND
import tw.waterballsa.gaas.exceptions.enums.PlatformError.PLAYER_NOT_HOST

class Room(
var roomId: Id? = null,
Expand All @@ -28,7 +30,8 @@ class Room(
fun isFull(): Boolean = players.size >= maxPlayers

fun changePlayerReadiness(playerId: Player.Id, readiness: Boolean) {
val player = findPlayer(playerId) ?: throw PlatformException("Player not joined")
val player =
findPlayer(playerId) ?: throw PlatformException(PLAYER_NOT_FOUND, "Player not joined")
if (readiness) {
player.ready()
} else {
Expand All @@ -37,13 +40,18 @@ class Room(
}

fun kickPlayer(hostId: Player.Id, playerId: Player.Id) {
if (hostId != host.id) {
throw throw throw PlatformException("This Player is not host")
}
val player = findPlayer(playerId) ?: throw PlatformException("Player not joined")
validateRoomHost(hostId)
val player =
findPlayer(playerId) ?: throw PlatformException(PLAYER_NOT_FOUND, "Player not joined")
players.remove(player)
}

fun validateRoomHost(userId: Player.Id) {
if (host.id != userId) {
throw PlatformException(PLAYER_NOT_HOST, "Player(${userId.value}) is not the host")
}
}

fun leaveRoom(playerId: Player.Id) {
players.removeIf { it.id == playerId }
if (playerId == host.id) {
Expand Down
5 changes: 3 additions & 2 deletions domain/src/main/kotlin/tw/waterballsa/gaas/domain/User.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tw.waterballsa.gaas.domain

import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.USER_INPUT_INVALID

class User(
val id: Id? = null,
Expand All @@ -23,11 +24,11 @@ class User(
val nicknameByteSize = nickname.toByteArray().size

if (nicknameByteSize < NICKNAME_MINIMUM_BYTE_SIZE) {
throw PlatformException("invalid nickname: too short")
throw PlatformException(USER_INPUT_INVALID, "invalid nickname: too short")
}

if (nicknameByteSize > NICKNAME_MAXIMUM_BYTE_SIZE) {
throw PlatformException("invalid nickname: too long")
throw PlatformException(USER_INPUT_INVALID, "invalid nickname: too long")
}

this.nickname = nickname
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
package tw.waterballsa.gaas.exceptions

import tw.waterballsa.gaas.exceptions.enums.PlatformError
import java.util.*
import kotlin.reflect.KClass

class NotFoundException private constructor(message: String) : PlatformException(message) {
private constructor(id: Any, identifierName: String, resourceName: String) : this(
"Resource (${resourceName.capitalize()}) not found ($identifierName = $id).",
class NotFoundException private constructor(
platformError: PlatformError,
message: String,
) : PlatformException(platformError, message) {

private constructor(platformError: PlatformError, id: Any, identifierName: String, resourceName: String) : this(
platformError = platformError,
message = "Resource (${resourceName.capitalize()}) not found ($identifierName = $id).",
)

private constructor(id: Any, resourceName: String) : this(id, "id", resourceName)
private constructor(platformError: PlatformError, id: Any, resourceName: String) :
this(platformError, id, "id", resourceName)

companion object {
fun <T : Any> notFound(resourceType: KClass<T>): NotFoundExceptionBuilder = notFound(resourceType.simpleName!!)
fun <T : Any> notFound(platformError: PlatformError, resourceType: KClass<T>): NotFoundExceptionBuilder =
notFound(platformError, resourceType.simpleName!!)

fun notFound(resourceName: String): NotFoundExceptionBuilder = NotFoundExceptionBuilder(resourceName)
fun notFound(platformError: PlatformError, resourceName: String): NotFoundExceptionBuilder =
NotFoundExceptionBuilder(platformError, resourceName)

class NotFoundExceptionBuilder(private val resourceName: String) {
class NotFoundExceptionBuilder(
private val platformError: PlatformError,
private val resourceName: String,
) {
fun identifyBy(identifierName: String, id: Any): NotFoundException =
NotFoundException(identifierName, resourceName)
NotFoundException(platformError, identifierName, resourceName)

fun id(id: Any): NotFoundException = NotFoundException(id, resourceName)
fun id(id: Any): NotFoundException = NotFoundException(platformError, id, resourceName)

fun message(): NotFoundException = NotFoundException("${resourceName.capitalize()} not found")
fun message(): NotFoundException =
NotFoundException(platformError, "${resourceName.capitalize()} not found")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
package tw.waterballsa.gaas.exceptions

open class PlatformException(message: String) : RuntimeException(message)
import tw.waterballsa.gaas.exceptions.enums.PlatformError

open class PlatformException(
val platformError: PlatformError,
message: String,
) : RuntimeException(message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tw.waterballsa.gaas.exceptions.enums

enum class PlatformError(
val code: String,
) {

CLIENT_INVALID("C001"),
CLIENT_AUTHORIZATION_INVALID("C002"),

JWT_ERROR("J001"),
JWT_NOT_FOUND("J002"),

GAME_NOT_FOUND("G001"),
GAME_EXISTS("G002"),
GAME_CREATE_ERROR("G003"),

USER_NOT_FOUND("U001"),
USER_INPUT_INVALID("U002"),
USER_NAME_DUPLICATED("U003"),

ROOM_NOT_FOUND("R001"),
ROOM_PASSWORD_INCORRECT("R002"),
ROOM_FULL("R003"),

PLAYER_NOT_FOUND("P001"),
PLAYER_NOT_HOST("P002"),
PLAYER_JOIN_ROOM_ERROR("P003"),
PLAYER_CREATE_ROOM_ERROR("P004"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ import tw.waterballsa.gaas.spring.controllers.viewmodel.PlatformViewModel

@RestControllerAdvice
class PlatformExceptionHandler {

@ResponseStatus(NOT_FOUND)
@ExceptionHandler(NotFoundException::class)
fun notFound(exception: NotFoundException): PlatformViewModel = PlatformViewModel(exception.message!!)
fun notFound(exception: NotFoundException): PlatformViewModel =
exception.toPlatformViewModel()

@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(PlatformException::class)
fun badRequest(exception: PlatformException): PlatformViewModel = PlatformViewModel(exception.message!!)

fun badRequest(exception: PlatformException): PlatformViewModel =
exception.toPlatformViewModel()
}

private fun <T : PlatformException> T.toPlatformViewModel(): PlatformViewModel =
PlatformViewModel(errorCode = platformError.code, message = message!!)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import org.springframework.web.context.request.NativeWebRequest
import tw.waterballsa.gaas.application.usecases.CreateUserUseCase
import tw.waterballsa.gaas.exceptions.NotFoundException.Companion.notFound
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.JWT_ERROR
import tw.waterballsa.gaas.exceptions.enums.PlatformError.JWT_NOT_FOUND
import tw.waterballsa.gaas.spring.configs.securities.RefreshAccessTokenHandler

@RestController
Expand All @@ -36,7 +38,7 @@ class OAuth2Controller(
): AuthenticateToken{
return refreshTokenHandler.refreshAccessToken(request, payload.token)
?.let { AuthenticateToken(it) }
?: throw notFound("AccessToken").message()
?: throw notFound(JWT_NOT_FOUND,"AccessToken").message()
}

@GetMapping("/login")
Expand All @@ -53,7 +55,7 @@ data class AuthenticateToken(

val OidcUser.identityProviderId: String
get() = subject
?: throw PlatformException("subject should exist.")
?: throw PlatformException(JWT_ERROR,"subject should exist.")

private fun OidcUser.toRequest(): CreateUserUseCase.Request =
CreateUserUseCase.Request(email ?: throw PlatformException("email should exist."), identityProviderId)
CreateUserUseCase.Request(email ?: throw PlatformException(JWT_ERROR,"email should exist."), identityProviderId)
Loading

0 comments on commit d60d3bb

Please sign in to comment.