Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

實作取得房間資訊 #124

Merged
merged 1 commit into from
Jul 29, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
依據實作房主IsReady always True修正
依據jwt subject is user identity修正
根據第三次 code review 修正
根據第二次 code review 修正
根據第一次 code review 修正
實作取得房間資訊
Ted committed Jul 29, 2023
commit 5d31a08e83669432ac1d0fb7bbb4e3674c5b8b5e
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package tw.waterballsa.gaas.application.usecases

import tw.waterballsa.gaas.application.eventbus.EventBus
import tw.waterballsa.gaas.application.repositories.RoomRepository
import tw.waterballsa.gaas.application.repositories.UserRepository
import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.domain.Room.Player
import tw.waterballsa.gaas.exceptions.PlatformException
import tw.waterballsa.gaas.exceptions.enums.PlatformError.PLAYER_NOT_IN_ROOM_ERROR
import javax.inject.Named

@Named
class GetRoomUsecase(
roomRepository: RoomRepository,
userRepository: UserRepository,
private val eventBus: EventBus,
) : AbstractRoomUseCase(roomRepository, userRepository) {
fun execute(request: Request, presenter: Presenter) {
with(request) {
val room = findRoomById(roomId)
val player = findPlayerByIdentity(userIdentity)
room.validatePlayerInRoom(player.id)
presenter.present(room)
}
}

private fun Room.validatePlayerInRoom(playerId: Player.Id) {
if (!hasPlayer(playerId)) {
throw PlatformException(PLAYER_NOT_IN_ROOM_ERROR, "Player(${playerId.value}) is not in the room(${roomId!!.value}).")
}
}

data class Request(
val roomId: String,
val userIdentity: String
)

interface Presenter {
fun present(room: Room)
}
}
3 changes: 3 additions & 0 deletions domain/src/main/kotlin/tw/waterballsa/gaas/domain/Room.kt
Original file line number Diff line number Diff line change
@@ -39,6 +39,9 @@ class Room(
}
}

fun hasPlayer(playerId: Player.Id): Boolean =
players.any { it.id == playerId }

fun kickPlayer(hostId: Player.Id, playerId: Player.Id) {
validateRoomHost(hostId)
val player =
Original file line number Diff line number Diff line change
@@ -26,4 +26,5 @@ enum class PlatformError(
PLAYER_NOT_HOST("P002"),
PLAYER_JOIN_ROOM_ERROR("P003"),
PLAYER_CREATE_ROOM_ERROR("P004"),
PLAYER_NOT_IN_ROOM_ERROR("P005"),
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,9 @@ import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.events.CreatedRoomEvent
import tw.waterballsa.gaas.events.DomainEvent
import tw.waterballsa.gaas.spring.controllers.RoomController.CreateRoomViewModel
import tw.waterballsa.gaas.spring.controllers.presenter.GetRoomPresenter
import tw.waterballsa.gaas.spring.controllers.presenter.GetRoomsPresenter
import tw.waterballsa.gaas.spring.controllers.viewmodel.GetRoomViewModel
import tw.waterballsa.gaas.spring.controllers.viewmodel.GetRoomsViewModel
import tw.waterballsa.gaas.spring.controllers.viewmodel.PlatformViewModel
import tw.waterballsa.gaas.spring.extensions.getEvent
@@ -28,7 +30,8 @@ class RoomController(
private val closeRoomsUseCase: CloseRoomUsecase,
private val changePlayerReadinessUsecase: ChangePlayerReadinessUsecase,
private val kickPlayerUseCase: KickPlayerUsecase,
private val leaveRoomUsecase: LeaveRoomUsecase
private val leaveRoomUsecase: LeaveRoomUsecase,
private val getRoomUsecase: GetRoomUsecase
) {
@PostMapping
fun createRoom(
@@ -118,6 +121,17 @@ class RoomController(
leaveRoomUsecase.execute(LeaveRoomUsecase.Request(roomId, jwt.subject))
}

@GetMapping("/{roomId}")
fun getRoom(
@AuthenticationPrincipal jwt: Jwt,
@PathVariable roomId: String,
): GetRoomViewModel {
val request = GetRoomUsecase.Request(roomId, jwt.subject)
val presenter = GetRoomPresenter()
getRoomUsecase.execute(request, presenter)
return presenter.viewModel
}

class CreateRoomRequest(
private val name: String,
private val gameId: String,
@@ -201,6 +215,7 @@ class RoomController(
offset = offset
)
}

}

private fun GameRegistration.toView(): CreateRoomViewModel.Game =
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tw.waterballsa.gaas.spring.controllers.presenter

import tw.waterballsa.gaas.application.usecases.GetRoomUsecase
import tw.waterballsa.gaas.domain.GameRegistration
import tw.waterballsa.gaas.domain.Room
import tw.waterballsa.gaas.spring.controllers.viewmodel.GetRoomViewModel

class GetRoomPresenter : GetRoomUsecase.Presenter {
lateinit var viewModel: GetRoomViewModel
private set

override fun present(room: Room) {
viewModel = room.toViewModel()
}

}

private fun Room.toViewModel(): GetRoomViewModel =
GetRoomViewModel(
id = roomId!!.value,
name = name,
game = game.toViewModel(),
host = host.toViewModel(),
players = players.map { it.toViewModel() },
maxPlayers = maxPlayers,
minPlayers = minPlayers,
currentPlayers = players.size,
isLocked = isLocked,
status = status.toString()
)

private fun GameRegistration.toViewModel(): GetRoomViewModel.Game =
GetRoomViewModel.Game(id!!.value, displayName)

private fun Room.Player.toViewModel(): GetRoomViewModel.Player =
GetRoomViewModel.Player(id.value, nickname, readiness)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package tw.waterballsa.gaas.spring.controllers.viewmodel

data class GetRoomViewModel(
val id: String,
val name: String,
val game: Game,
val host: Player,
val players: List<Player>,
val maxPlayers: Int,
val minPlayers: Int,
val currentPlayers: Int,
val isLocked: Boolean,
val status: String
) {
data class Game(val id: String, val name: String)

data class Player(val id: String, val nickname: String, val isReady: Boolean)
}
Original file line number Diff line number Diff line change
@@ -327,7 +327,7 @@ class RoomControllerTest @Autowired constructor(
"winner0033", "google-oauth2|200000000000000000000"
).toRoomPlayer()

givenHostAndPlayersAreInTheRoom(host, playerB, playerC)
givenHostAndPlayersJoinedTheRoom(host, playerB, playerC)
.whenUserLeaveTheRoom(userA)
.thenPlayerShouldBeNotInRoomAndHostIsChanged(host)
}
@@ -360,6 +360,33 @@ class RoomControllerTest @Autowired constructor(
.thenShouldFail("Player(${userB.id!!.value}) has joined another room.")
}

@Test
fun giveHostAndPlayerBJoinedRoomC_WhenHostGetRoomC_ThenShouldGetRoomCSuccessfully() {
val userA = testUser
val host = userA.toRoomPlayer()
val playerB = createUser(
"2", "test2@mail.com",
"winner1122", "google-oauth2|100000000000000000000"
).toRoomPlayer()

givenHostAndPlayersJoinedTheRoom(host, playerB)
.whenUserGetTheRoom(userA)
.thenGetRoomSuccessfully()
}

@Test
fun giveUserANotJoinedRoomB_WhenUserAGetRoomB_ThenShouldFail() {
val userA = testUser
val host = createUser(
"2", "test2@mail.com",
"winner1122", "google-oauth2|100000000000000000000"
).toRoomPlayer()

givenHostAndPlayersJoinedTheRoom(host)
.whenUserGetTheRoom(userA)
.thenShouldFail("Player(${userA.id!!.value}) is not in the room(${testRoom.roomId!!.value}).")
}

private fun TestGetRoomsRequest.whenUserAVisitLobby(joinUser: User): ResultActions =
mockMvc.perform(
get("/rooms")
@@ -429,7 +456,7 @@ class RoomControllerTest @Autowired constructor(
return testRoom
}

private fun givenHostAndPlayersAreInTheRoom(host: Player, vararg players: Player): Room {
private fun givenHostAndPlayersJoinedTheRoom(host: Player, vararg players: Player): Room {
val combinedPlayers = (listOf(host) + players).toMutableList()
testRoom = createRoom(host, combinedPlayers)
return testRoom
@@ -451,6 +478,14 @@ class RoomControllerTest @Autowired constructor(
return leaveRoom(leaveUser)
}

private fun Room.whenUserGetTheRoom(user: User) = getRoom(user)

private fun getRoom(user: User): ResultActions =
mockMvc.perform(
get("/rooms/${testRoom.roomId!!.value}")
.withJwt(user.toJwt())
)

private fun ResultActions.thenCreateRoomSuccessfully() {
val roomView = getBody(CreateRoomViewModel::class.java)
val room = roomRepository.findById(roomView.id)!!
@@ -500,6 +535,33 @@ class RoomControllerTest @Autowired constructor(

private fun createUser(user: User): User = userRepository.createUser(user)

private fun ResultActions.thenGetRoomSuccessfully() {
val room = roomRepository.findById(testRoom.roomId!!)!!
room.let {
andExpect(status().isOk)
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.id").value(it.roomId!!.value))
.andExpect(jsonPath("$.name").value(it.name))
.andExpect(jsonPath("$.game.id").value(it.game.id!!.value))
.andExpect(jsonPath("$.game.name").value(it.game.displayName))
.andExpect(jsonPath("$.host.id").value(it.host.id!!.value))
.andExpect(jsonPath("$.host.nickname").value(it.host.nickname))
.andExpect(jsonPath("$.host.isReady").value(it.host.readiness))
.andExpect(jsonPath("$.isLocked").value(!it.password.isNullOrEmpty()))
.andExpect(jsonPath("$.status").value(it.status.toString()))
.andExpect(jsonPath("$.currentPlayers").value(2))
.andExpect(jsonPath("$.minPlayers").value(it.minPlayers))
.andExpect(jsonPath("$.maxPlayers").value(it.maxPlayers))
.andExpect(jsonPath("$.players").isArray())

it.players.forEachIndexed { index, player ->
andExpect(jsonPath("$.players[$index].id").value(player.id!!.value))
.andExpect(jsonPath("$.players[$index].nickname").value(player.nickname))
.andExpect(jsonPath("$.players[$index].isReady").value(player.readiness))
}
}
}

private fun registerGame(): GameRegistration = gameRegistrationRepository.registerGame(
GameRegistration(
uniqueName = "Mahjong-python",
@@ -585,9 +647,6 @@ class RoomControllerTest @Autowired constructor(
private fun User.toRoomPlayer(): Player =
Player(Player.Id(id!!.value), nickname)

private fun Room.hasPlayer(playerId: Player.Id): Boolean =
players.any { it.id == playerId }

private fun Room.isHost(playerId: Player.Id): Boolean =
host.id == playerId
}