diff options
Diffstat (limited to 'src/main/java')
13 files changed, 180 insertions, 126 deletions
diff --git a/src/main/java/org/luxons/sevenwonders/controllers/GameController.java b/src/main/java/org/luxons/sevenwonders/controllers/GameController.java index 0bb3eb23..0deac4a3 100644 --- a/src/main/java/org/luxons/sevenwonders/controllers/GameController.java +++ b/src/main/java/org/luxons/sevenwonders/controllers/GameController.java @@ -48,16 +48,16 @@ public class GameController { private void sendPreparedCard(PreparedCard preparedCard, Game game) { for (Player player : game.getPlayers()) { - String userName = player.getUserName(); - template.convertAndSendToUser(userName, "/topic/game/" + game.getId() + "/prepared", preparedCard); + String username = player.getUsername(); + template.convertAndSendToUser(username, "/topic/game/" + game.getId() + "/prepared", preparedCard); } } private void sendTurnInfo(Game game) { List<PlayerTurnInfo> turnInfos = game.getTurnInfo(); for (PlayerTurnInfo turnInfo : turnInfos) { - String userName = turnInfo.getPlayer().getUserName(); - template.convertAndSendToUser(userName, "/topic/game/" + game.getId() + "/turn", turnInfo); + String username = turnInfo.getPlayer().getUsername(); + template.convertAndSendToUser(username, "/topic/game/" + game.getId() + "/turn", turnInfo); } } } diff --git a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java index 1664d05e..996ea361 100644 --- a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java +++ b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java @@ -52,10 +52,10 @@ public class LobbyController { @MessageMapping("/chooseName") @SendToUser("/queue/nameChoice") public Player chooseName(@Validated ChooseNameAction action, Principal principal) { - String userName = principal.getName(); - Player player = playerRepository.updateOrCreatePlayer(userName, action.getPlayerName()); + String username = principal.getName(); + Player player = playerRepository.createOrUpdate(username, action.getPlayerName()); - logger.info("Player '{}' chose the name '{}'", userName, player.getDisplayName()); + logger.info("Player '{}' chose the name '{}'", username, player.getDisplayName()); return player; } @@ -75,7 +75,7 @@ public class LobbyController { gameOwner.setLobby(lobby); logger.info("Game '{}' ({}) created by {} ({})", lobby.getName(), lobby.getId(), gameOwner.getDisplayName(), - gameOwner.getUserName()); + gameOwner.getUsername()); return Collections.singletonList(lobby); } @@ -89,7 +89,7 @@ public class LobbyController { lobby.addPlayer(newPlayer); newPlayer.setLobby(lobby); - logger.info("Player '{}' ({}) joined game {}", newPlayer.getDisplayName(), newPlayer.getUserName(), + logger.info("Player '{}' ({}) joined game {}", newPlayer.getDisplayName(), newPlayer.getUsername(), lobby.getName()); sendLobbyUpdateToPlayers(lobby); return lobby; @@ -151,14 +151,14 @@ public class LobbyController { } private static class UserNotInLobbyException extends ApiMisuseException { - UserNotInLobbyException(String userName) { - super("User " + userName + " is not in a lobby, create or join a game first"); + UserNotInLobbyException(String username) { + super("User " + username + " is not in a lobby, create or join a game first"); } } private static class UserIsNotOwnerException extends ApiMisuseException { - UserIsNotOwnerException(String userName) { - super("User " + userName + " does not own the lobby he's in"); + UserIsNotOwnerException(String username) { + super("User " + username + " does not own the lobby he's in"); } } diff --git a/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java b/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java index 71df185f..1cd18d09 100644 --- a/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java +++ b/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java @@ -1,7 +1,5 @@ package org.luxons.sevenwonders.errors; -public enum ErrorType { - USER, - VALIDATION, - INTERNAL +enum ErrorType { + USER_INPUT, VALIDATION, CLIENT, SERVER } diff --git a/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java b/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java index 9e26cbe3..628da4f8 100644 --- a/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java +++ b/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java @@ -11,6 +11,7 @@ import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.handler.annotation.MessageExceptionHandler; import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException; import org.springframework.messaging.simp.annotation.SendToUser; +import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -19,9 +20,9 @@ public class ExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); - private static final String ERROR_CODE_VALIDATION = "VALIDATION_ERROR"; + private static final String ERROR_CODE_VALIDATION = "INVALID_DATA"; - private static final String ERROR_CODE_CONVERSION = "MESSAGE_FORMAT_ERROR"; + private static final String ERROR_CODE_CONVERSION = "INVALID_MESSAGE_FORMAT"; private static final String ERROR_MSG_VALIDATION = "Invalid input data"; @@ -36,41 +37,45 @@ public class ExceptionHandler { @MessageExceptionHandler @SendToUser("/queue/errors") - private UIError handleValidationError(MethodArgumentNotValidException exception) { + public UIError handleUserInputError(UserInputException exception) { + logger.error("Incorrect user input: " + exception.getMessage()); + String messageKey = exception.getMessageResourceKey(); + String message = messageSource.getMessage(messageKey, exception.getParams(), messageKey, Locale.US); + return new UIError(messageKey, message, ErrorType.USER_INPUT); + } + + @MessageExceptionHandler + @SendToUser("/queue/errors") + public UIError handleValidationError(MethodArgumentNotValidException exception) { logger.error("Invalid input", exception); - List<ObjectError> errors = exception.getBindingResult().getAllErrors(); UIError uiError = new UIError(ERROR_CODE_VALIDATION, ERROR_MSG_VALIDATION, ErrorType.VALIDATION); - uiError.setValidationErrors(errors); + + BindingResult result = exception.getBindingResult(); + if (result != null) { + List<ObjectError> errors = result.getAllErrors(); + uiError.addDetails(errors); + } return uiError; } @MessageExceptionHandler @SendToUser("/queue/errors") - private UIError handleConversionError(MessageConversionException exception) { + public UIError handleConversionError(MessageConversionException exception) { logger.error("Error interpreting the message", exception); return new UIError(ERROR_CODE_CONVERSION, ERROR_MSG_CONVERSION, ErrorType.VALIDATION); } @MessageExceptionHandler @SendToUser("/queue/errors") - private UIError handleGenericUserError(UserInputException exception) { - logger.error("Incorrect user input: " + exception.getMessage()); - String messageKey = exception.getMessageResourceKey(); - String message = messageSource.getMessage(messageKey, exception.getParams(), messageKey, Locale.US); - return new UIError(messageKey, message, ErrorType.USER); - } - - @MessageExceptionHandler - @SendToUser("/queue/errors") - private UIError handleApiError(ApiMisuseException exception) { + public UIError handleApiError(ApiMisuseException exception) { logger.error("Invalid API input", exception); - return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.INTERNAL); + return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.CLIENT); } @MessageExceptionHandler @SendToUser("/queue/errors") - private UIError handleUnexpectedInternalError(Throwable exception) { + public UIError handleUnexpectedInternalError(Throwable exception) { logger.error("Uncaught exception thrown during message handling", exception); - return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.INTERNAL); + return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.SERVER); } } diff --git a/src/main/java/org/luxons/sevenwonders/errors/UIError.java b/src/main/java/org/luxons/sevenwonders/errors/UIError.java index 67802f22..ee5fcbe0 100644 --- a/src/main/java/org/luxons/sevenwonders/errors/UIError.java +++ b/src/main/java/org/luxons/sevenwonders/errors/UIError.java @@ -1,7 +1,9 @@ package org.luxons.sevenwonders.errors; +import java.util.ArrayList; import java.util.List; +import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; public class UIError { @@ -12,9 +14,9 @@ public class UIError { private final ErrorType type; - private List<ObjectError> validationErrors; + private List<UIErrorDetail> details = new ArrayList<>(); - public UIError(String code, String message, ErrorType type) { + UIError(String code, String message, ErrorType type) { this.code = code; this.message = message; this.type = type; @@ -32,11 +34,21 @@ public class UIError { return type; } - public List<ObjectError> getValidationErrors() { - return validationErrors; + public List<UIErrorDetail> getDetails() { + return details; } - public void setValidationErrors(List<ObjectError> validationErrors) { - this.validationErrors = validationErrors; + void addDetails(List<ObjectError> objectErrors) { + for (ObjectError objectError : objectErrors) { + this.details.add(convertError(objectError)); + } + } + + private UIErrorDetail convertError(ObjectError objectError) { + if (objectError instanceof FieldError) { + return new UIErrorDetail((FieldError)objectError); + } else { + return new UIErrorDetail(objectError); + } } } diff --git a/src/main/java/org/luxons/sevenwonders/errors/UIErrorDetail.java b/src/main/java/org/luxons/sevenwonders/errors/UIErrorDetail.java new file mode 100644 index 00000000..dc4250bb --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/errors/UIErrorDetail.java @@ -0,0 +1,37 @@ +package org.luxons.sevenwonders.errors; + +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; + +class UIErrorDetail { + + private final Object rejectedValue; + + private final String path; + + private final String message; + + UIErrorDetail(FieldError error) { + rejectedValue = error.getRejectedValue(); + path = error.getObjectName() + '.' + error.getField(); + message = "Invalid value for field '" + error.getField() + "': " + error.getDefaultMessage(); + } + + UIErrorDetail(ObjectError error) { + rejectedValue = null; + path = error.getObjectName(); + message = "Invalid value for object '" + error.getObjectName() + "': " + error.getDefaultMessage(); + } + + public Object getRejectedValue() { + return rejectedValue; + } + + public String getPath() { + return path; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/game/Game.java b/src/main/java/org/luxons/sevenwonders/game/Game.java index bc9c3b17..8aa7d1b9 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Game.java +++ b/src/main/java/org/luxons/sevenwonders/game/Game.java @@ -55,8 +55,8 @@ public class Game { return id; } - public boolean containsUser(String userName) { - return players.stream().anyMatch(p -> p.getUserName().equals(userName)); + public boolean containsUser(String username) { + return players.stream().anyMatch(p -> p.getUsername().equals(username)); } public List<Player> getPlayers() { @@ -96,8 +96,8 @@ public class Game { } } - public PreparedCard prepareCard(String userName, PlayerMove playerMove) throws InvalidMoveException { - Player player = getPlayer(userName); + public PreparedCard prepareCard(String username, PlayerMove playerMove) throws InvalidMoveException { + Player player = getPlayer(username); Card card = decks.getCard(playerMove.getCardName()); Move move = playerMove.getType().resolve(player.getIndex(), card, playerMove); validate(move); @@ -105,11 +105,11 @@ public class Game { return new PreparedCard(player, card.getBack()); } - private Player getPlayer(String userName) { + private Player getPlayer(String username) { return players.stream() - .filter(p -> p.getUserName().equals(userName)) + .filter(p -> p.getUsername().equals(username)) .findAny() - .orElseThrow(() -> new UnknownPlayerException(userName)); + .orElseThrow(() -> new UnknownPlayerException(username)); } private void validate(Move move) throws InvalidMoveException { @@ -222,8 +222,8 @@ public class Game { } private static class UnknownPlayerException extends IllegalArgumentException { - UnknownPlayerException(String userName) { - super(userName); + UnknownPlayerException(String username) { + super(username); } } diff --git a/src/main/java/org/luxons/sevenwonders/game/Lobby.java b/src/main/java/org/luxons/sevenwonders/game/Lobby.java index c0201438..6975349a 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Lobby.java +++ b/src/main/java/org/luxons/sevenwonders/game/Lobby.java @@ -90,29 +90,29 @@ public class Lobby { return players.size() >= gameDefinition.getMinPlayers(); } - public void reorderPlayers(List<String> orderedUserNames) { + public void reorderPlayers(List<String> orderedUsernames) { List<Player> formerList = new ArrayList<>(players); players.clear(); - for (int i = 0; i < orderedUserNames.size(); i++) { - Player player = getPlayer(formerList, orderedUserNames.get(i)); + for (int i = 0; i < orderedUsernames.size(); i++) { + Player player = getPlayer(formerList, orderedUsernames.get(i)); players.add(player); player.setIndex(i); } } - private static Player getPlayer(List<Player> players, String userName) { + private static Player getPlayer(List<Player> players, String username) { return players.stream() - .filter(p -> p.getUserName().equals(userName)) + .filter(p -> p.getUsername().equals(username)) .findAny() - .orElseThrow(() -> new UnknownPlayerException(userName)); + .orElseThrow(() -> new UnknownPlayerException(username)); } - public boolean isOwner(String userName) { - return owner.getUserName().equals(userName); + public boolean isOwner(String username) { + return owner.getUsername().equals(username); } - public boolean containsUser(String userName) { - return players.stream().anyMatch(p -> p.getUserName().equals(userName)); + public boolean containsUser(String username) { + return players.stream().anyMatch(p -> p.getUsername().equals(username)); } static class GameAlreadyStartedException extends IllegalStateException { @@ -131,8 +131,8 @@ public class Lobby { } static class UnknownPlayerException extends IllegalArgumentException { - UnknownPlayerException(String userName) { - super(userName); + UnknownPlayerException(String username) { + super(username); } } } diff --git a/src/main/java/org/luxons/sevenwonders/game/Player.java b/src/main/java/org/luxons/sevenwonders/game/Player.java index c8c1509c..f1095049 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Player.java +++ b/src/main/java/org/luxons/sevenwonders/game/Player.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; public class Player { - private final String userName; + private final String username; private String displayName; @@ -14,13 +14,13 @@ public class Player { private transient Game game; - public Player(String userName, String displayName) { - this.userName = userName; + public Player(String username, String displayName) { + this.username = username; this.displayName = displayName; } - public String getUserName() { - return userName; + public String getUsername() { + return username; } public String getDisplayName() { diff --git a/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java b/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java index 59e984af..efe39b85 100644 --- a/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java +++ b/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java @@ -12,14 +12,14 @@ public class GameRepository { private Map<Long, Game> games = new HashMap<>(); - public void add(Game game) { + public void add(Game game) throws GameAlreadyExistsException { if (games.containsKey(game.getId())) { throw new GameAlreadyExistsException(game.getId()); } games.put(game.getId(), game); } - public Game find(long gameId) { + public Game find(long gameId) throws GameNotFoundException { Game game = games.get(gameId); if (game == null) { throw new GameNotFoundException(gameId); @@ -27,13 +27,21 @@ public class GameRepository { return game; } + public Game remove(long gameId) throws GameNotFoundException { + Game game = games.remove(gameId); + if (game == null) { + throw new GameNotFoundException(gameId); + } + return game; + } + public static class GameNotFoundException extends ApiMisuseException { GameNotFoundException(long id) { super("Game " + id + " doesn't exist"); } } - private static class GameAlreadyExistsException extends ApiMisuseException { + static class GameAlreadyExistsException extends ApiMisuseException { GameAlreadyExistsException(long id) { super("Game " + id + " already exists"); } diff --git a/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java b/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java index 3561161b..8f305791 100644 --- a/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java +++ b/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.luxons.sevenwonders.errors.UserInputException; import org.luxons.sevenwonders.game.Lobby; import org.luxons.sevenwonders.game.Player; import org.luxons.sevenwonders.game.data.GameDefinitionLoader; @@ -16,9 +15,7 @@ public class LobbyRepository { private final GameDefinitionLoader gameDefinitionLoader; - private Map<String, Lobby> lobbies = new HashMap<>(); - - private Map<Long, Lobby> lobbiesById = new HashMap<>(); + private Map<Long, Lobby> lobbies = new HashMap<>(); private long lastGameId = 0; @@ -32,33 +29,31 @@ public class LobbyRepository { } public Lobby create(String gameName, Player owner) { - if (lobbies.containsKey(gameName)) { - throw new GameNameAlreadyUsedException(gameName); - } long id = lastGameId++; Lobby lobby = new Lobby(id, gameName, owner, gameDefinitionLoader.getGameDefinition()); - lobbies.put(gameName, lobby); - lobbiesById.put(id, lobby); + lobbies.put(id, lobby); return lobby; } - public Lobby find(long lobbyId) { - Lobby lobby = lobbiesById.get(lobbyId); + public Lobby find(long lobbyId) throws LobbyNotFoundException { + Lobby lobby = lobbies.get(lobbyId); if (lobby == null) { - throw new LobbyNotFoundException(String.valueOf(lobbyId)); + throw new LobbyNotFoundException(lobbyId); } return lobby; } - public static class LobbyNotFoundException extends RuntimeException { - LobbyNotFoundException(String name) { - super("Lobby not found for game '" + name + "'"); + public Lobby remove(long lobbyId) throws LobbyNotFoundException { + Lobby lobby = lobbies.remove(lobbyId); + if (lobby == null) { + throw new LobbyNotFoundException(lobbyId); } + return lobby; } - private static class GameNameAlreadyUsedException extends UserInputException { - GameNameAlreadyUsedException(String name) { - super("Game name '" + name + "' already exists"); + public static class LobbyNotFoundException extends RuntimeException { + LobbyNotFoundException(long id) { + super("Lobby not found for id '" + id + "'"); } } } diff --git a/src/main/java/org/luxons/sevenwonders/repositories/PlayerRepository.java b/src/main/java/org/luxons/sevenwonders/repositories/PlayerRepository.java index 76df13b4..049c5ef9 100644 --- a/src/main/java/org/luxons/sevenwonders/repositories/PlayerRepository.java +++ b/src/main/java/org/luxons/sevenwonders/repositories/PlayerRepository.java @@ -12,50 +12,49 @@ public class PlayerRepository { private Map<String, Player> players = new HashMap<>(); - public Player updateOrCreatePlayer(String userName, String displayName) { - if (players.containsKey(userName)) { - return updatePlayerName(userName, displayName); + public boolean contains(String username) { + return players.containsKey(username); + } + + public Player createOrUpdate(String username, String displayName) { + if (players.containsKey(username)) { + return update(username, displayName); } else { - return createPlayer(userName, displayName); + return create(username, displayName); } } - private Player createPlayer(String userName, String displayName) throws PlayerAlreadyExistsException { - Player player = new Player(userName, displayName); - add(player); + private Player create(String username, String displayName) { + Player player = new Player(username, displayName); + players.put(username, player); return player; } - private void add(Player player) throws PlayerAlreadyExistsException { - if (players.containsKey(player.getUserName())) { - throw new PlayerAlreadyExistsException(player.getUserName()); - } - players.put(player.getUserName(), player); - } - - private Player updatePlayerName(String userName, String displayName) throws PlayerNotFoundException { - Player player = find(userName); + private Player update(String username, String displayName) throws PlayerNotFoundException { + Player player = find(username); player.setDisplayName(displayName); return player; } - public Player find(String userName) throws PlayerNotFoundException { - Player player = players.get(userName); + public Player find(String username) throws PlayerNotFoundException { + Player player = players.get(username); if (player == null) { - throw new PlayerNotFoundException(userName); + throw new PlayerNotFoundException(username); } return player; } - public static class PlayerNotFoundException extends ApiMisuseException { - PlayerNotFoundException(String userName) { - super("Player '" + userName + "' doesn't exist"); + public Player remove(String username) { + Player player = players.remove(username); + if (player == null) { + throw new PlayerNotFoundException(username); } + return player; } - private static class PlayerAlreadyExistsException extends ApiMisuseException { - PlayerAlreadyExistsException(String userName) { - super("Player '" + userName + "' already exists"); + static class PlayerNotFoundException extends ApiMisuseException { + PlayerNotFoundException(String username) { + super("Player '" + username + "' doesn't exist"); } } } diff --git a/src/main/java/org/luxons/sevenwonders/validation/DestinationAccessValidator.java b/src/main/java/org/luxons/sevenwonders/validation/DestinationAccessValidator.java index 64f14dee..65b3623c 100644 --- a/src/main/java/org/luxons/sevenwonders/validation/DestinationAccessValidator.java +++ b/src/main/java/org/luxons/sevenwonders/validation/DestinationAccessValidator.java @@ -27,46 +27,46 @@ public class DestinationAccessValidator { this.gameRepository = gameRepository; } - public boolean hasAccess(String userName, String destination) { - if (userName == null) { + public boolean hasAccess(String username, String destination) { + if (username == null) { // unnamed user cannot belong to anything return false; } - if (hasForbiddenGameReference(userName, destination)) { + if (hasForbiddenGameReference(username, destination)) { return false; } - if (hasForbiddenLobbyReference(userName, destination)) { + if (hasForbiddenLobbyReference(username, destination)) { return false; } return true; } - private boolean hasForbiddenGameReference(String userName, String destination) { + private boolean hasForbiddenGameReference(String username, String destination) { Matcher gameMatcher = gameDestination.matcher(destination); if (!gameMatcher.matches()) { return false; // no game reference is always OK } int gameId = extractId(gameMatcher); - return !isUserInGame(userName, gameId); + return !isUserInGame(username, gameId); } - private boolean hasForbiddenLobbyReference(String userName, String destination) { + private boolean hasForbiddenLobbyReference(String username, String destination) { Matcher lobbyMatcher = lobbyDestination.matcher(destination); if (!lobbyMatcher.matches()) { return false; // no lobby reference is always OK } int lobbyId = extractId(lobbyMatcher); - return !isUserInLobby(userName, lobbyId); + return !isUserInLobby(username, lobbyId); } - private boolean isUserInGame(String userName, int gameId) { + private boolean isUserInGame(String username, int gameId) { Game game = gameRepository.find(gameId); - return game.containsUser(userName); + return game.containsUser(username); } - private boolean isUserInLobby(String userName, int lobbyId) { + private boolean isUserInLobby(String username, int lobbyId) { Lobby lobby = lobbyRepository.find(lobbyId); - return lobby.containsUser(userName); + return lobby.containsUser(username); } private static int extractId(Matcher matcher) { |