diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java new file mode 100644 index 00000000..0d574785 --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/repositories/GameRepository.java @@ -0,0 +1,12 @@ +package tw.waterballsa.gaas.citadels.app.repositories; + +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; + +import java.util.Optional; + +public interface GameRepository { + + CitadelsGame createGame(CitadelsGame citadelsGame); + + Optional findGameById(String gameId); +} diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java new file mode 100644 index 00000000..5d79883f --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/GetGameUseCase.java @@ -0,0 +1,38 @@ +package tw.waterballsa.gaas.citadels.app.usecases; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; +import tw.waterballsa.gaas.citadels.exceptions.NotFoundException; + +import javax.inject.Named; +import java.util.Optional; + +@Named +@RequiredArgsConstructor +public class GetGameUseCase { + + private final GameRepository gameRepository; + + public void execute(String gameId, Presenter presenter) { + CitadelsGame citadelsGame = findGameById(gameId); + presenter.setGame(citadelsGame); + } + + private CitadelsGame findGameById(String gameId) { + Optional game = gameRepository.findGameById(gameId); + return game.orElseThrow(() -> new NotFoundException("CAN NOT FIND GAME, ID=" + gameId)); + } + + @Data + @AllArgsConstructor + public static class Request { + private String gameId; + } + + public interface Presenter { + void setGame(CitadelsGame citadelsGame); + } +} diff --git a/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java new file mode 100644 index 00000000..5038a409 --- /dev/null +++ b/packages/backend/app/src/main/java/tw/waterballsa/gaas/citadels/app/usecases/StartGameUseCase.java @@ -0,0 +1,104 @@ +package tw.waterballsa.gaas.citadels.app.usecases; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.domain.BuildingCard; +import tw.waterballsa.gaas.citadels.domain.RoleCard; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; +import tw.waterballsa.gaas.citadels.domain.Player; + +import javax.inject.Named; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.*; + + +@Named +@RequiredArgsConstructor +public class StartGameUseCase { + + private final GameRepository gameRepository; + + public void execute(Request request, Presenter presenter) { + CitadelsGame citadelsGame = createGame(request); + presenter.setGame(gameRepository.createGame(citadelsGame)); + } + + private CitadelsGame createGame(Request request) { + List players = getPlayers(request.getPlayers()); + List roleCards = getRoleCards(); + List buildingCards = getBuildingCards(); + CitadelsGame citadelsGame = new CitadelsGame(players, roleCards, buildingCards); + citadelsGame.start(); + return citadelsGame; + } + + private List getRoleCards() { + return Arrays.asList(new RoleCard(1, "刺客"), + new RoleCard(2, "小偷"), + new RoleCard(3, "魔術師"), + new RoleCard(4, "國王"), + new RoleCard(5, "住持"), + new RoleCard(6, "商人"), + new RoleCard(7, "建築師"), + new RoleCard(8, "領主")); + } + + private List getPlayers(List playerIds) { + return playerIds.stream() + .map(user -> new Player(user.getId(), user.getName(), user.getImageName())) + .collect(toList()); + } + + public interface Presenter { + void setGame(CitadelsGame citadelsGame); + } + + @Data + @Builder + @AllArgsConstructor + public static class Request { + public String roomId; + public String roomName; + public String holderId; + public List players; + } + + @Data + @AllArgsConstructor + public static class UserRequest { + public String id; + public String name; + public String imageName; + } + + private List getBuildingCards() { + List cards = new ArrayList<>(); + Stream.of( + getBuildingCards(BuildingCard.Color.YELLOW, 12), + getBuildingCards(BuildingCard.Color.BLUE, 11), + getBuildingCards(BuildingCard.Color.GREEN, 11), + getBuildingCards(BuildingCard.Color.RED, 11), + getBuildingCards(BuildingCard.Color.PURPLE, 30) + ) + .flatMap(Collection::stream) + .forEach(cards::add); + + return cards; + } + + private List getBuildingCards(BuildingCard.Color color, int count) { + return IntStream.range(0, count) + .mapToObj(i -> new BuildingCard("card name", 2, color)) + .collect(toList()); + } + +} diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildingCard.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildingCard.java new file mode 100644 index 00000000..3507aa0e --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/BuildingCard.java @@ -0,0 +1,27 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class BuildingCard { + private String name; + private int coins; + private Color color; + + public enum Color { + PURPLE("特別地區"), + YELLOW("貴族地區"), + RED("軍事地區"), + BLUE("商業地區"), + GREEN("宗教地區"); + private final String area; + + Color(String area) { + this.area = area; + } + } + +} + diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CitadelsGame.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CitadelsGame.java new file mode 100644 index 00000000..612290de --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/CitadelsGame.java @@ -0,0 +1,70 @@ +package tw.waterballsa.gaas.citadels.domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static java.util.UUID.randomUUID; + +public class CitadelsGame { + private final String id; + private final List players; + private final List roleCards; + private final List buildingCards; + public static final Integer DEFAULT_COINS = 2; + public static final Integer DEFAULT_CARD_QUANTITY = 2; + + public CitadelsGame(List players, List roleCards, List buildingCards) { + this.id = randomUUID().toString(); + this.players = players; + this.roleCards = roleCards; + this.buildingCards = buildingCards; + } + + public CitadelsGame(String id, List players, List roleCards, List buildingCards) { + this.id = id; + this.players = players; + this.roleCards = roleCards; + this.buildingCards = buildingCards; + } + + public String getId() { + return id; + } + + public List getPlayers() { + return List.copyOf(players); + } + + public List getBuildingCards() { + return List.copyOf(buildingCards); + } + + public List getRoleCards() { + return List.copyOf(roleCards); + } + + public void randomlyAwardCrownToOnePlayer() { + Collections.shuffle(players); + Player kingPlayer = players.get(0); + kingPlayer.acquireCrown(); + } + + public void start() { + randomlyAwardCrownToOnePlayer(); + distributingCardsAndCoinsToEachPlayer(); + } + + private void distributingCardsAndCoinsToEachPlayer() { + players.forEach(player -> { + player.plusCards(getTwoCards()); + player.plusCoins(2); + }); + + } + + public List getTwoCards() { + return Arrays.asList(new BuildingCard("test", 3, BuildingCard.Color.BLUE), new BuildingCard("test", 2, BuildingCard.Color.BLUE)); + } + +} diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java new file mode 100644 index 00000000..0ba28e9c --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/Player.java @@ -0,0 +1,39 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import java.util.*; + + +@Getter +@AllArgsConstructor +public class Player { + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildingCards; + private RoleCard roleCard; + private Boolean hasCrown; + static final int DEFAULT_COIN = 0; + + public Player(String id, String name, String image) { + this(id, name, image, DEFAULT_COIN, new ArrayList<>(), null, null); + } + + public void removeCrown() { + hasCrown = false; + } + + public void acquireCrown() { + hasCrown = true; + } + + public void plusCoins(Integer coins) { + this.coins = this.coins + coins; + } + + public void plusCards(List buildingCardList) { + buildingCards.addAll(buildingCardList); + } +} \ No newline at end of file diff --git a/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/RoleCard.java b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/RoleCard.java new file mode 100644 index 00000000..b4b4d346 --- /dev/null +++ b/packages/backend/domain/src/main/java/tw/waterballsa/gaas/citadels/domain/RoleCard.java @@ -0,0 +1,11 @@ +package tw.waterballsa.gaas.citadels.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class RoleCard { + private int sequence; + private String name; +} diff --git a/packages/backend/spring/pom.xml b/packages/backend/spring/pom.xml index 5c4af75e..7b96d4e2 100644 --- a/packages/backend/spring/pom.xml +++ b/packages/backend/spring/pom.xml @@ -13,7 +13,7 @@ citadels-backend 0.0.1-SNAPSHOT citadels-backend - gaas citadels game web + gaas citadels citadelsGame web 17 diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java index 73781b48..b30274e9 100644 --- a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomController.java @@ -21,6 +21,7 @@ @AllArgsConstructor @RequestMapping(value = "/api/citadels") public class CreateRoomController { + private final CreateRoomUseCase createRoomUseCase; @PostMapping("/room") diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java new file mode 100644 index 00000000..83080a1d --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/GameController.java @@ -0,0 +1,99 @@ +package tw.waterballsa.gaas.citadels.spring.controllers; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import tw.waterballsa.gaas.citadels.app.usecases.GetGameUseCase; +import tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GetGameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.ResponseEntity.status; +import static tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase.Presenter; +import static tw.waterballsa.gaas.citadels.app.usecases.StartGameUseCase.Request; +import static tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView.toViewModel; + +@RestController +@AllArgsConstructor +@RequestMapping(value = "/api/citadels") +public class GameController { + + private final StartGameUseCase startGameUseCase; + + private final GetGameUseCase getGameUseCase; + + @PostMapping("/games") + public ResponseEntity startGame(@RequestBody StartGameRequest request) { + StartGamePresenter presenter = new StartGamePresenter(); + startGameUseCase.execute(request.toRequest(), presenter); + return status(OK).body(presenter.getStartGameView()); + } + + @GetMapping("/games/{gameId}") + public ResponseEntity getGame(@PathVariable String gameId) { + GetCitadelsGamePresenter presenter = new GetCitadelsGamePresenter(); + getGameUseCase.execute(gameId, presenter); + return status(OK).body(presenter.getGameView()); + } + + class GetCitadelsGamePresenter implements GetGameUseCase.Presenter { + private CitadelsGame citadelsGame; + + public GetGameView getGameView() { + return GetGameView.toViewModel(citadelsGame); + } + + @Override + public void setGame(CitadelsGame citadelsGame) { + this.citadelsGame = citadelsGame; + } + } + + + class StartGamePresenter implements Presenter { + private CitadelsGame citadelsGame; + + @Override + public void setGame(CitadelsGame citadelsGame) { + this.citadelsGame = citadelsGame; + } + + public StartGameView getStartGameView() { + return toViewModel(citadelsGame); + } + } + + @AllArgsConstructor + @Value + static class StartGameRequest { + @NotNull(message = "The roomId must not be null.") + String roomId; + @NotNull(message = "The roomRoom must not be null.") + String roomName; + @NotNull(message = "The holderId must not be null.") + String holderId; + List players; + + public Request toRequest() { + List userRequests = new ArrayList<>(); + players.forEach(player -> userRequests.add(new StartGameUseCase.UserRequest(player.getId(), player.name, player.getImageName()))); + return new Request(roomId, roomName, holderId, userRequests); + } + } + + @Data + @AllArgsConstructor + public static class UserRequest { + public String id; + public String name; + public String imageName; + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildingCardView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildingCardView.java new file mode 100644 index 00000000..92368e8f --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/BuildingCardView.java @@ -0,0 +1,25 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.BuildingCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class BuildingCardView { + private String name; + private int coins; + private BuildingCard.Color color; + + public static List toViewModels(List buildingCards) { + return buildingCards.stream().map(BuildingCardView::toViewModel).collect(Collectors.toList()); + } + + public static BuildingCardView toViewModel(BuildingCard buildingCard) { + return new BuildingCardView(buildingCard.getName(), buildingCard.getCoins(), buildingCard.getColor()); + } + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java new file mode 100644 index 00000000..f9c8fa38 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GameView.java @@ -0,0 +1,22 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import tw.waterballsa.gaas.citadels.domain.*; + +import java.util.List; + +import static tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.PlayerView.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GameView { + private String id; + private List playerViews; + + public static GameView toViewModel(CitadelsGame citadelsGame) { + return new GameView(citadelsGame.getId(), toViewModels(citadelsGame.getPlayers())); + } +} \ No newline at end of file diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java new file mode 100644 index 00000000..61b667a1 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/GetGameView.java @@ -0,0 +1,20 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class GetGameView { + private String createTime; + private String status; + private String msg; + private GameView gameView; + + public static GetGameView toViewModel(CitadelsGame citadelsGame) { + return new GetGameView(LocalDateTime.now().toString(), "OK", "", GameView.toViewModel(citadelsGame)); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java new file mode 100644 index 00000000..43d134a0 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/PlayerView.java @@ -0,0 +1,31 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.Player; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class PlayerView { + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildingCardViews; + private RoleCardView roleCardView; + private Boolean hasCrown; + + public static List toViewModels(List players) { + return players.stream().map(PlayerView::toViewModel).collect(Collectors.toList()); + } + + + public static PlayerView toViewModel(Player player) { + List buildingCardViews = BuildingCardView.toViewModels(player.getBuildingCards()); + RoleCardView roleCardView = RoleCardView.toViewModel(player.getRoleCard()); + return new PlayerView(player.getId(),player.getName(),player.getImageName(),player.getCoins(),buildingCardViews,roleCardView,player.getHasCrown()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoleCardView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoleCardView.java new file mode 100644 index 00000000..c2ae64c7 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoleCardView.java @@ -0,0 +1,23 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.RoleCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +public class RoleCardView { + private int sequence; + private String name; + + public static RoleCardView toViewModel(RoleCard roleCard) { + return (roleCard != null) ? new RoleCardView(roleCard.getSequence(), roleCard.getName()) : null; + } + + public static List toViewModels(List roleCards) { + return roleCards.stream().map(RoleCardView::toViewModel).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoomView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoomView.java index 530d3f32..2d30ee1c 100644 --- a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoomView.java +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/RoomView.java @@ -24,6 +24,7 @@ public class RoomView { public static RoomView toViewModel(Room room) { User holder = room.findHolder(); List usersView = UserView.toViewModel(room.getUsers()); - return new RoomView(room.getCreateTime().toString(), room.getId(), room.getName(), holder.getId(), holder.getName(), usersView, room.getStatus(), room.getUsers().size()); + return new RoomView(room.getCreateTime().toString(), room.getId(), room.getName(), + holder.getId(), holder.getName(), usersView, room.getStatus(), room.getUsers().size()); } } \ No newline at end of file diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java new file mode 100644 index 00000000..d402d92c --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/controllers/viewmodel/StartGameView.java @@ -0,0 +1,19 @@ +package tw.waterballsa.gaas.citadels.spring.controllers.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class StartGameView { + private String createTime; + private String status; + private String msg; + private String gameId; + + public static StartGameView toViewModel(CitadelsGame citadelsGame) { + return new StartGameView(LocalDateTime.now().toString(), "OK", "", citadelsGame.getId()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java new file mode 100644 index 00000000..f32c7e43 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/GameRepositoryImpl.java @@ -0,0 +1,28 @@ +package tw.waterballsa.gaas.citadels.spring.repositories; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import tw.waterballsa.gaas.citadels.app.repositories.GameRepository; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; +import tw.waterballsa.gaas.citadels.spring.repositories.dao.GameDAO; +import tw.waterballsa.gaas.citadels.spring.repositories.data.GameData; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class GameRepositoryImpl implements GameRepository { + + private final GameDAO gameDAO; + + @Override + public CitadelsGame createGame(CitadelsGame citadelsGame) { + GameData data = GameData.toData(citadelsGame); + return gameDAO.save(data).toDomain(); + } + + @Override + public Optional findGameById(String gameId) { + return gameDAO.findById(gameId).map(GameData::toDomain); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java new file mode 100644 index 00000000..67452636 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/dao/GameDAO.java @@ -0,0 +1,10 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.dao; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; +import tw.waterballsa.gaas.citadels.spring.repositories.data.GameData; + +@Repository +public interface GameDAO extends MongoRepository { + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildingCardData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildingCardData.java new file mode 100644 index 00000000..2154f47b --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/BuildingCardData.java @@ -0,0 +1,36 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.BuildingCard; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class BuildingCardData { + @Id + private String name; + private int coins; + private BuildingCard.Color color; + + public static BuildingCardData toData(BuildingCard buildingCard) { + return new BuildingCardData(buildingCard.getName(), buildingCard.getCoins(), buildingCard.getColor()); + } + + public static List toData(List buildingCards) { + return buildingCards.stream().map(BuildingCardData::toData).collect(Collectors.toList()); + } + + public static BuildingCard toDomain(BuildingCardData buildingCardData) { + return new BuildingCard(buildingCardData.getName(), buildingCardData.getCoins(), buildingCardData.getColor()); + } + + public static List toDomains(List buildingCardDataList) { + return buildingCardDataList.stream().map(BuildingCardData::toDomain).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java new file mode 100644 index 00000000..21923f64 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/GameData.java @@ -0,0 +1,41 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.CitadelsGame; + +import java.util.List; + +import static tw.waterballsa.gaas.citadels.spring.repositories.data.BuildingCardData.toDomains; +import static tw.waterballsa.gaas.citadels.spring.repositories.data.RoleCardData.toDomainList; + + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Document("game") +public class GameData { + @Id + private String id; + private List players; + private List roleCardsData; + private List buildingCardsData; + + public static GameData toData(CitadelsGame citadelsGame) { + return GameData.builder().id(citadelsGame.getId()) + .players(PlayerData.toData(citadelsGame.getPlayers())) + .roleCardsData(RoleCardData.toData(citadelsGame.getRoleCards())) + .buildingCardsData(BuildingCardData.toData(citadelsGame.getBuildingCards())) + .build(); + } + + public CitadelsGame toDomain() { + return new CitadelsGame(id, PlayerData.toDomainList(players), toDomainList(roleCardsData), toDomains(buildingCardsData)); + } + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java new file mode 100644 index 00000000..be44616d --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/PlayerData.java @@ -0,0 +1,59 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.BuildingCard; +import tw.waterballsa.gaas.citadels.domain.RoleCard; +import tw.waterballsa.gaas.citadels.domain.Player; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class PlayerData { + @Id + private String id; + private String name; + private String imageName; + private Integer coins; + private List buildingCardDataList; + private RoleCardData characterCardData; + private Boolean hasCrown; + + public static PlayerData toData(Player player) { + return new PlayerData(player.getId(), player.getName(), player.getImageName(), player.getCoins(), + BuildingCardData.toData(player.getBuildingCards()), RoleCardData.toData(player.getRoleCard()), player.getHasCrown()); + } + + public static List toData(List players) { + return players.stream().map(PlayerData::toData).collect(Collectors.toList()); + } + + public static Player toDomain(PlayerData playerData) { + List buildingCards = BuildingCardData.toDomains(playerData.getBuildingCardDataList()); + RoleCard roleCard = RoleCardData.toDomain(playerData.getCharacterCardData()); + return new Player(playerData.getId(), playerData.getName(), playerData.getImageName(), playerData.getCoins(), buildingCards, roleCard, playerData.getHasCrown()); + } + + public static List toDomainList(List playersDataList) { + return playersDataList.stream().map(PlayerData::toDomain).collect(Collectors.toList()); + } + + public static Map toDomains(PlayerData... playersData) { + return toDomains(Arrays.asList(playersData)); + } + + public static Map toDomains(List playersData) { + Map players = new LinkedHashMap<>(); + playersData.forEach(playerData -> players.put(playerData.getId(), toDomain(playerData))); + return players; + } + +} diff --git a/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/RoleCardData.java b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/RoleCardData.java new file mode 100644 index 00000000..9964c0f2 --- /dev/null +++ b/packages/backend/spring/src/main/java/tw/waterballsa/gaas/citadels/spring/repositories/data/RoleCardData.java @@ -0,0 +1,43 @@ +package tw.waterballsa.gaas.citadels.spring.repositories.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import tw.waterballsa.gaas.citadels.domain.RoleCard; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Data +@Document +@AllArgsConstructor +public class RoleCardData { + @Id + private int sequence; + private String name; + + public static RoleCardData toData(RoleCard roleCard) { + return (roleCard != null) ? new RoleCardData(roleCard.getSequence(), roleCard.getName()) : null; + } + + public static List toData(List roleCards) { + return roleCards.stream().map(RoleCardData::toData).collect(Collectors.toList()); + } + + public static RoleCard toDomain(RoleCardData characterCardData) { + return characterCardData != null ? new RoleCard(characterCardData.getSequence(), characterCardData.getName()) : null; + } + + public static Map toDomains(List characterCardDataList) { + Map cards = new LinkedHashMap<>(); + characterCardDataList.forEach(characterCardData -> cards.put(characterCardData.getSequence(), toDomain(characterCardData))); + return cards; + } + + public static List toDomainList(List characterCardDataList) { + return characterCardDataList.stream().map(RoleCardData::toDomain).collect(Collectors.toList()); + } +} diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java index 3cac8881..d6fabb1d 100644 --- a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/CitadelsSpringBootTest.java @@ -16,14 +16,20 @@ import java.util.stream.Collectors; import java.util.stream.Stream; + @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") public abstract class CitadelsSpringBootTest { + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired protected MockMvc mockMvc; @Autowired - protected tw.waterballsa.gaas.citadels.spring.repositories.dao.RoomDAO RoomDAO; + protected tw.waterballsa.gaas.citadels.spring.repositories.dao.RoomDAO roomDAO; + @Autowired + protected tw.waterballsa.gaas.citadels.spring.repositories.dao.GameDAO gameDAO; + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired protected ObjectMapper objectMapper; @Autowired @@ -71,7 +77,6 @@ protected Room givenRoomStarted(String name, String holderId, User... users) { } protected Room findRoomById(String roomId) { - Room room = roomRepository.findRoomById(roomId).orElseThrow(() -> new NotFoundException("CAN NOT FIND ROOM ID=" + roomId)); - return room; + return roomRepository.findRoomById(roomId).orElseThrow(() -> new NotFoundException("CAN NOT FIND ROOM ID=" + roomId)); } } diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java new file mode 100644 index 00000000..20cab5db --- /dev/null +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateGameAndGetGameTest.java @@ -0,0 +1,72 @@ +package tw.waterballsa.gaas.citadels.spring.controllers; + +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.ResultActions; +import tw.waterballsa.gaas.citadels.spring.CitadelsSpringBootTest; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.GetGameView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.PlayerView; +import tw.waterballsa.gaas.citadels.spring.controllers.viewmodel.StartGameView; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static tw.waterballsa.gaas.citadels.domain.CitadelsGame.DEFAULT_CARD_QUANTITY; +import static tw.waterballsa.gaas.citadels.domain.CitadelsGame.DEFAULT_COINS; + +public class CreateGameAndGetGameTest extends CitadelsSpringBootTest { + + @Test + public void whenRoomReady_ShouldGetStartGameSuccessfully() { + GameController.StartGameRequest request = getTestGame(); + StartGameView startGameView = createGame(request); + + GetGameView getGameViewResponse = getGame(startGameView.getGameId()); + GameView gameView = getGameViewResponse.getGameView(); + + assertNotNull(gameView); + assertEquals(startGameView.getGameId(), gameView.getId()); + List playerViews = gameView.getPlayerViews(); + assertEquals(request.getPlayers().size(), playerViews.size()); + playerViews.forEach(player -> { + assertEquals(DEFAULT_COINS, player.getCoins()); + assertEquals(DEFAULT_CARD_QUANTITY, player.getBuildingCardViews().size()); + }); + } + + + @SneakyThrows + private GetGameView getGame(String gameId) { + ResultActions mvcResult = mockMvc.perform(get(API_PREFIX + "/games/{gameId}", gameId)) + .andExpect(status().isOk()); + return getBody(mvcResult, GetGameView.class); + } + + + private GameController.StartGameRequest getTestGame() { + List players = Arrays.asList( + new GameController.UserRequest("1", "p1", "1"), + new GameController.UserRequest("2", "p2", "2"), + new GameController.UserRequest("3", "p3", "3"), + new GameController.UserRequest("4", "p4", "4"), + new GameController.UserRequest("5", "p5", "5") + ); + return new GameController.StartGameRequest("1", "roomA", "1", players); + } + + @SneakyThrows + private StartGameView createGame(GameController.StartGameRequest startGameRequest) { + ResultActions resultActions = mockMvc.perform(post(API_PREFIX + "/games") + .contentType(APPLICATION_JSON) + .content(toJson(startGameRequest))) + .andExpect(status().isOk()); + return getBody(resultActions, StartGameView.class); + } +} diff --git a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java index 5ca0ff6d..3aadccee 100644 --- a/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java +++ b/packages/backend/spring/src/test/java/tw/waterballsa/gaas/citadels/spring/controllers/CreateRoomTest.java @@ -22,7 +22,7 @@ public class CreateRoomTest extends CitadelsSpringBootTest { public void whenUserACreateRoom_ShouldCreateSuccessfully() { CreateRoomController.CreateRoomRequest createRoomRequest = new CreateRoomController.CreateRoomRequest("RoomA", "userA", "user.png"); CreateRoomView createRoomView = getBody(createRoom(createRoomRequest), CreateRoomView.class); - Optional roomData = RoomDAO.findById(createRoomView.getRoom().getRoomId()); + Optional roomData = roomDAO.findById(createRoomView.getRoom().getRoomId()); assertTrue(roomData.isPresent()); RoomData room = roomData.get();