package kickr.web.api; import kickr.web.view.match.NewMatchView; import kickr.web.view.match.MatchesView; import kickr.web.model.match.MatchData; import io.dropwizard.hibernate.UnitOfWork; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import javax.annotation.security.RolesAllowed; import javax.validation.ConstraintViolation; import javax.validation.Validator; import javax.ws.rs.BadRequestException; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.NotAcceptableException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import kickr.db.dao.MatchDAO; import kickr.db.dao.PlayerDAO; import kickr.db.entity.FoosballTable; import kickr.db.entity.Match; import kickr.db.entity.user.User; import kickr.service.MatchService; import support.validation.Constraints; import kickr.db.util.Page; import support.web.api.AbstractResource; import static kickr.web.api.PlayerUtil.parsePlayer; import kickr.web.form.GamesUtil; import kickr.web.form.NewMatchForm; import kickr.web.model.GameData; import kickr.web.model.FoosballTableData; import kickr.web.model.PlayerData; import kickr.web.model.TeamData; import kickr.web.model.match.CoreMatchData; import kickr.web.view.match.LatestMatchesView; import kickr.web.view.match.MatchView; import support.form.FormData; import support.security.annotation.Auth; @Path("match") public class MatchResource extends AbstractResource { protected MatchDAO matchDao; protected PlayerDAO playerDao; protected MatchService matchService; protected Validator validator; public MatchResource(MatchService matchService, MatchDAO matchDao, PlayerDAO playerDao, Validator validator) { this.matchDao = matchDao; this.playerDao = playerDao; this.matchService = matchService; this.validator = validator; } @GET @RolesAllowed("user") @UnitOfWork public MatchesView list( @QueryParam("page") @DefaultValue("1") int pageNo, @QueryParam("filter") String filter, @QueryParam("search") @DefaultValue("") String search) { Page page = new Page(pageNo); List<Match> matches; if (search.isEmpty()) { matches = matchDao.getMatches(page); } else { matches = matchDao.getMatchesByAliases(parseAliases(search), page); } return createView(MatchesView.class) .withMatches(MatchData.fromMatches(matches)) .withPage(pageNo) .withFilter(filter) .withSearch(search); } @GET @Path("latest") @UnitOfWork public LatestMatchesView listLatest() { List<Match> matches = matchDao.getMatches(new Page(1, 5)); return createView(LatestMatchesView.class).withMatches(MatchData.fromMatches(matches)); } @GET @RolesAllowed("user") @Path("new") public NewMatchView newForm() { NewMatchForm.Table table = new NewMatchForm.Table(1l, "camunda HQ", "Kaffee", "Klo"); return createView(NewMatchView.class).withForm(new NewMatchForm().withTable(table)); } @POST @Path("new") @RolesAllowed("user") @UnitOfWork public Response createFromForm(@Auth User user, @FormData NewMatchForm newMatchForm) { Set<ConstraintViolation<NewMatchForm>> validationErrors = validator.validate(newMatchForm); if (!validationErrors.isEmpty()) { throw invalidForm(NewMatchView.class, newMatchForm.withErrors(validationErrors)); } try { CoreMatchData matchData = new CoreMatchData(); matchData.setTeam1(createTeamData("team1", newMatchForm.getTeam1())); matchData.setTeam2(createTeamData("team2", newMatchForm.getTeam2())); matchData.setTable(createTableData(newMatchForm.getTable())); Date played = newMatchForm.getPlayed(); if (played == null) { played = new Date(); } matchData.setPlayed(played); matchData.setGames(createGameData(newMatchForm.getGames())); Match createdMatch = matchService.insertMatch(matchData, user); return redirect("/match/" + createdMatch.getId()).build(); } catch (InvalidPlayerException e) { ConstraintViolation<NewMatchForm> violation = Constraints .createFieldViolation(newMatchForm, e.getMessage(), e.getPropertyPath(), e.getPropertyValue()); throw invalidForm(NewMatchView.class, newMatchForm.withError(violation)); } } @GET @Path("{id}") @RolesAllowed("user") @UnitOfWork public MatchView getMatch(@PathParam("id") Long id) { Match match = matchDao.findMatchById(id); FoosballTable table = match.getTable(); return createView(MatchView.class) .withMatch(MatchData.fromMatch(match)) .withTable(FoosballTableData.fromTable(table)); } @DELETE @Path("{id}") @RolesAllowed("user") @UnitOfWork public void removeMatch(@Auth User user, @PathParam("id") Long id) { Match match = matchDao.findMatchById(id); if (match.isRated() && !user.hasRole("admin")) { throw new NotAcceptableException("already rated"); } matchDao.removeMatch(match); } private TeamData createTeamData(String path, NewMatchForm.Team team) { return new TeamData( createPlayerData(path + ".offense", team.getOffense()), createPlayerData(path + ".defense", team.getDefense()) ); } private FoosballTableData createTableData(NewMatchForm.Table table) { return new FoosballTableData(table.getId()); } private List<String> parseAliases(String searchStr) { return Arrays.asList(searchStr.split(" ")); } public List<GameData> createGameData(String gamesStr) { Matcher matcher = GamesUtil.GAME_PATTERN.matcher(gamesStr); List<GameData> games = new ArrayList<>(); while (matcher.find()) { games.add(new GameData( Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)) )); } if (games.isEmpty()) { throw new BadRequestException("Invalid input"); } return games; } public PlayerData createPlayerData(String path, String playerStr) { PlayerData playerData = parsePlayer(playerStr); if (playerData != null) { String email = playerData.getEmail(); String alias = playerData.getAlias(); if (email == null && playerDao.findPlayerByAlias(alias) == null) { throw new InvalidPlayerException("Non-existant player", path, alias); } } return playerData; } private static class InvalidPlayerException extends RuntimeException { private final String propertyPath; private final Object propertyValue; public InvalidPlayerException(String message, String propertyPath, Object propertyValue) { super(message); this.propertyValue = propertyValue; this.propertyPath = propertyPath; } public String getPropertyPath() { return propertyPath; } public Object getPropertyValue() { return propertyValue; } } }