MatchesController.java
package net.andresbustamante.yafoot.core.web.controllers;
import net.andresbustamante.yafoot.commons.exceptions.ApplicationException;
import net.andresbustamante.yafoot.commons.exceptions.DatabaseException;
import net.andresbustamante.yafoot.commons.model.UserContext;
import net.andresbustamante.yafoot.commons.web.controllers.AbstractController;
import net.andresbustamante.yafoot.core.exceptions.UnauthorisedUserException;
import net.andresbustamante.yafoot.core.model.Player;
import net.andresbustamante.yafoot.core.model.enums.MatchStatusEnum;
import net.andresbustamante.yafoot.core.model.enums.SportEnum;
import net.andresbustamante.yafoot.core.services.CarpoolingService;
import net.andresbustamante.yafoot.core.services.MatchManagementService;
import net.andresbustamante.yafoot.core.services.MatchSearchService;
import net.andresbustamante.yafoot.core.services.PlayerSearchService;
import net.andresbustamante.yafoot.core.web.mappers.CarMapper;
import net.andresbustamante.yafoot.core.web.mappers.MatchMapper;
import net.andresbustamante.yafoot.core.web.mappers.RegistrationMapper;
import net.andresbustamante.yafoot.web.dto.Car;
import net.andresbustamante.yafoot.web.dto.CarConfirmation;
import net.andresbustamante.yafoot.web.dto.Match;
import net.andresbustamante.yafoot.web.dto.MatchForm;
import net.andresbustamante.yafoot.web.dto.MatchStatus;
import net.andresbustamante.yafoot.web.dto.Registration;
import net.andresbustamante.yafoot.web.dto.RegistrationForm;
import net.andresbustamante.yafoot.web.dto.SportCode;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE;
import static org.springframework.http.HttpStatus.*;
/**
* REST service for managing and searching matches.
*
* @author andresbustamante
*/
@RestController
public class MatchesController extends AbstractController implements MatchesApi {
/* Message codes */
private static final String UNKNOWN_MATCH_ERROR = "unknown.match.error";
private static final String UNKNOWN_PLAYER_REGISTRATION_ERROR = "unknown.player.registration.error";
private static final String UNKNOWN_PLAYER_ERROR = "unknown.player.error";
private final MatchSearchService matchSearchService;
private final PlayerSearchService playerSearchService;
private final MatchManagementService matchManagementService;
private final CarpoolingService carpoolingService;
private final MatchMapper matchMapper;
private final RegistrationMapper registrationMapper;
private final CarMapper carMapper;
@Value("${api.matches.one.path}")
private String matchApiPath;
@Value("${api.matches.one.registrations.one.path}")
private String matchRegistrationApiPath;
public MatchesController(
final MatchSearchService matchSearchService, final PlayerSearchService playerSearchService,
final CarpoolingService carpoolingService, final MatchManagementService matchManagementService,
final MatchMapper matchMapper, final RegistrationMapper registrationMapper, final CarMapper carMapper) {
this.matchSearchService = matchSearchService;
this.matchManagementService = matchManagementService;
this.playerSearchService = playerSearchService;
this.carpoolingService = carpoolingService;
this.matchMapper = matchMapper;
this.registrationMapper = registrationMapper;
this.carMapper = carMapper;
}
@Override
public ResponseEntity<Match> loadMatchByCode(final String code) {
try {
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
if (match != null) {
return ResponseEntity.ok(matchMapper.map(match));
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
}
}
@Override
public ResponseEntity<List<Registration>> loadMatchRegistrations(final String code) {
try {
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
if (match != null) {
return ResponseEntity.ok(registrationMapper.map(match.getRegistrations()));
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
}
}
@Override
public ResponseEntity<List<Match>> findMatches(
@DateTimeFormat(iso = DATE) final LocalDate startDate,
@DateTimeFormat(iso = DATE) final LocalDate endDate,
final SportCode sport, final MatchStatus status) {
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
throw new ResponseStatusException(BAD_REQUEST);
}
try {
UserContext ctx = getUserContext();
List<net.andresbustamante.yafoot.core.model.Match> matches = matchSearchService.findMatches(
(status != null) ? MatchStatusEnum.valueOf(status.name()) : null,
(sport != null) ? SportEnum.valueOf(sport.name()) : null,
startDate, endDate, ctx);
if (CollectionUtils.isNotEmpty(matches)) {
List<Match> result = matchMapper.map(matches);
return ResponseEntity.ok(result);
} else {
return ResponseEntity.ok(Collections.emptyList());
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
}
}
@Override
public ResponseEntity<Void> cancelMatch(final String code) {
try {
UserContext userContext = getUserContext();
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
if (match != null) {
matchManagementService.cancelMatch(match, userContext);
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
} catch (ApplicationException e) {
throw new ResponseStatusException(FORBIDDEN, translate(e.getCode(), null));
}
}
@CrossOrigin(exposedHeaders = {HttpHeaders.LOCATION})
@Override
public ResponseEntity<Void> createMatch(final MatchForm match) {
try {
UserContext userContext = getUserContext();
net.andresbustamante.yafoot.core.model.Match m = matchMapper.map(match);
matchManagementService.saveMatch(m, userContext);
String location = String.format(matchApiPath, m.getCode());
return ResponseEntity.created(getLocationURI(location)).build();
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
} catch (ApplicationException e) {
throw new ResponseStatusException(BAD_REQUEST, translate(e.getCode(), null));
}
}
@Override
public ResponseEntity<List<Car>> findCarsForMatch(final String code) {
try {
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
if (match != null) {
List<net.andresbustamante.yafoot.core.model.Car> cars = carpoolingService.findAvailableCarsByMatch(
match);
return ResponseEntity.ok(carMapper.map(cars));
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
}
}
@CrossOrigin(exposedHeaders = {HttpHeaders.LOCATION})
@Override
public ResponseEntity<Void> registerPlayerToMatch(final String code, final RegistrationForm registration) {
try {
UserContext ctx = getUserContext();
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
Player player = registration.getPlayerId() != null
? playerSearchService.findPlayerById(registration.getPlayerId()) // A given player
: playerSearchService.findPlayerByEmail(ctx.getUsername(), ctx); // The player himself/herself
if (match == null) {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
} else if (player == null) {
throw new ResponseStatusException(BAD_REQUEST, translate(UNKNOWN_PLAYER_ERROR,
new String[]{registration.getPlayerId().toString()}));
}
net.andresbustamante.yafoot.core.model.Car car = null;
if (registration.getCarId() != null) {
car = new net.andresbustamante.yafoot.core.model.Car(registration.getCarId());
}
matchManagementService.registerPlayer(player, match, car, ctx);
String location = String.format(matchRegistrationApiPath, match.getCode(),
registration.getPlayerId());
return ResponseEntity.created(getLocationURI(location)).build();
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
} catch (ApplicationException e) {
throw new ResponseStatusException(BAD_REQUEST, translate(e.getCode(), null));
}
}
@Override
public ResponseEntity<Void> updateCarForRegistration(
final String code, final Integer pid, final CarConfirmation carConfirmation) {
try {
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
if (match != null && match.getRegistrations() != null) {
Player player = playerSearchService.findPlayerById(pid);
if (player == null) {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_PLAYER_REGISTRATION_ERROR, null));
}
Optional<net.andresbustamante.yafoot.core.model.Registration> registration =
match.getRegistrations().stream().filter(reg -> reg.getPlayer().equals(player)).findFirst();
if (registration.isPresent()) {
UserContext ctx = getUserContext();
net.andresbustamante.yafoot.core.model.Car car = new net.andresbustamante.yafoot.core.model.Car(
carConfirmation.getCarId());
carpoolingService.updateCarpoolingInformation(match, player, car, carConfirmation.isConfirmed(),
ctx);
return ResponseEntity.accepted().build();
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_PLAYER_REGISTRATION_ERROR, null));
}
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
} catch (UnauthorisedUserException e) {
throw new ResponseStatusException(FORBIDDEN, translate(UNAUTHORISED_USER_ERROR, null));
} catch (ApplicationException e) {
throw new ResponseStatusException(BAD_REQUEST, translate(e.getCode(), null));
}
}
@Override
public ResponseEntity<Void> unregisterPlayerFromMatch(final String code, final Integer pid) {
try {
UserContext userContext = getUserContext();
net.andresbustamante.yafoot.core.model.Match match = matchSearchService.findMatchByCode(code);
Player player = playerSearchService.findPlayerById(pid);
if (match != null && player != null) {
matchManagementService.unregisterPlayer(player, match, userContext);
return ResponseEntity.noContent().build();
} else if (match == null) {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_MATCH_ERROR, null));
} else {
throw new ResponseStatusException(NOT_FOUND, translate(UNKNOWN_PLAYER_REGISTRATION_ERROR, null));
}
} catch (DatabaseException e) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, translate(DATABASE_BASIC_ERROR, null));
} catch (ApplicationException e) {
throw new ResponseStatusException(BAD_REQUEST, translate(e.getCode(), null));
}
}
}