1
0
Fork 0
mirror of https://codeberg.org/beerbrawl/beerbrawl.git synced 2024-09-22 21:20:52 +02:00

fix(#15): add tournament team delete endpoint

This commit is contained in:
motzik 2024-05-25 10:45:30 +02:00
parent e871ddd874
commit 136b2f1e53
6 changed files with 162 additions and 78 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# db files
/database/db.mv.db
/database/db.trace.db
/database/db.lock.db
# logs
/log

View file

@ -51,8 +51,8 @@ public class TournamentEndpoint {
@Autowired @Autowired
public TournamentEndpoint(TournamentService tournamentService, public TournamentEndpoint(TournamentService tournamentService,
TournamentMapper tournamentMapper, TournamentMapper tournamentMapper,
TeamMapper teamMapper) { TeamMapper teamMapper) {
this.tournamentService = tournamentService; this.tournamentService = tournamentService;
this.tournamentMapper = tournamentMapper; this.tournamentMapper = tournamentMapper;
this.teamMapper = teamMapper; this.teamMapper = teamMapper;
@ -61,7 +61,8 @@ public class TournamentEndpoint {
@Secured("ROLE_USER") @Secured("ROLE_USER")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get a list of all tournaments from a specific Organizer", security = @SecurityRequirement(name = "apiKey")) @Operation(summary = "Get a list of all tournaments from a specific Organizer",
security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<List<TournamentListDto>> tournaments(Authentication authentication) { public ResponseEntity<List<TournamentListDto>> tournaments(Authentication authentication) {
LOG.info("GET {}", BASE_ENDPOINT); LOG.info("GET {}", BASE_ENDPOINT);
var tournaments = tournamentService.findAllByOrganizer(authentication.getName()); var tournaments = tournamentService.findAllByOrganizer(authentication.getName());
@ -73,11 +74,11 @@ public class TournamentEndpoint {
@PostMapping @PostMapping
@Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey")) @Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey"))
public TournamentDto createTournament(@Valid @RequestBody CreateTournamentDto messageDto, public TournamentDto createTournament(@Valid @RequestBody CreateTournamentDto messageDto,
Authentication authentication) { Authentication authentication) {
LOG.info("POST /api/v1/tournaments body: {}", messageDto); LOG.info("POST /api/v1/tournaments body: {}", messageDto);
return tournamentMapper.entityToDto(tournamentService.create(tournamentMapper.createDtoToEntity(messageDto), return tournamentMapper.entityToDto(tournamentService.create(tournamentMapper.createDtoToEntity(messageDto),
authentication.getName())); authentication.getName()));
} }
@Secured("ROLE_USER") @Secured("ROLE_USER")
@ -85,29 +86,13 @@ public class TournamentEndpoint {
@PostMapping(value = "{id}/qualification-matches") @PostMapping(value = "{id}/qualification-matches")
@Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey")) @Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey"))
public List<TournamentQualificationMatchDto> generateQualificationMatches( public List<TournamentQualificationMatchDto> generateQualificationMatches(
@PathVariable(name = "id") Long tournamentId, Authentication authentication) { @PathVariable(name = "id") Long tournamentId, Authentication authentication) {
LOG.info("POST /api/v1/tournaments/{}/generate-qualification-matches", tournamentId); LOG.info("POST /api/v1/tournaments/{}/generate-qualification-matches", tournamentId);
return tournamentService.generateQualificationMatchesForTournament(tournamentId, authentication.getName()) return tournamentService.generateQualificationMatchesForTournament(tournamentId, authentication.getName())
.stream() .stream()
.map(tournamentMapper::qualificationMatchEntityToDto) .map(tournamentMapper::qualificationMatchEntityToDto)
.toList(); .toList();
}
@Secured("ROLE_USER")
@ResponseStatus(HttpStatus.OK)
@GetMapping(value = "{id}/teams")
@Operation(summary = "Get teams for a tournament", security = @SecurityRequirement(name = "apiKey"))
public List<TeamDto> getTournamentTeams(@PathVariable(name = "id") Long tournamentId,
Authentication authentication) {
LOG.info("GET /api/v1/tournaments/{}/teams", tournamentId);
// check if user is organizer of tournament
if (!tournamentService.isOrganizer(authentication.getName(), tournamentId)) {
throw new AccessDeniedException("Current user isn't organizer of tournament.");
}
return tournamentService.getTournamentTeams(tournamentId).stream().map(teamMapper::entityToDto).toList();
} }
@Secured("ROLE_USER") @Secured("ROLE_USER")
@ -115,7 +100,7 @@ public class TournamentEndpoint {
@DeleteMapping("/{tournamentId}") @DeleteMapping("/{tournamentId}")
@Operation(summary = "Delete a tournament", security = @SecurityRequirement(name = "apiKey")) @Operation(summary = "Delete a tournament", security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<Void> deleteTournament(@PathVariable("tournamentId") long tournamentId, public ResponseEntity<Void> deleteTournament(@PathVariable("tournamentId") long tournamentId,
Authentication authentication) { Authentication authentication) {
LOG.info("DELETE {}/{}", BASE_ENDPOINT, tournamentId); LOG.info("DELETE {}/{}", BASE_ENDPOINT, tournamentId);
tournamentService.deleteTournament(tournamentId, authentication.getName()); tournamentService.deleteTournament(tournamentId, authentication.getName());
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
@ -126,13 +111,13 @@ public class TournamentEndpoint {
@PostMapping(value = "{id}/generate-ko-matches") @PostMapping(value = "{id}/generate-ko-matches")
@Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey")) @Operation(summary = "Create a new tournament", security = @SecurityRequirement(name = "apiKey"))
public void generateKoMatches( public void generateKoMatches(
@PathVariable(name = "id") Long tournamentId, @PathVariable(name = "id") Long tournamentId,
Authentication authentication) { Authentication authentication) {
LOG.info("POST /api/v1/tournaments/{}/generate-ko-matches", tournamentId); LOG.info("POST /api/v1/tournaments/{}/generate-ko-matches", tournamentId);
tournamentService.generateKoMatchesForTournament( tournamentService.generateKoMatchesForTournament(
tournamentId, tournamentId,
authentication.getName()); authentication.getName());
} }
@ -157,13 +142,47 @@ public class TournamentEndpoint {
public record SignupTeamResponseDto(SignupTeamResult signupTeamResult) { public record SignupTeamResponseDto(SignupTeamResult signupTeamResult) {
} }
@Secured("ROLE_USER")
@ResponseStatus(HttpStatus.OK)
@GetMapping(value = "{id}/teams")
@Operation(summary = "Get teams for a tournament", security = @SecurityRequirement(name = "apiKey"))
public List<TeamDto> getTournamentTeams(@PathVariable(name = "id") Long tournamentId,
Authentication authentication) {
LOG.info("GET /api/v1/tournaments/{}/teams", tournamentId);
// check if user is organizer of tournament
if (!tournamentService.isOrganizer(authentication.getName(), tournamentId)) {
throw new AccessDeniedException("Current user isn't organizer of tournament.");
}
return tournamentService.getTournamentTeams(tournamentId).stream().map(teamMapper::entityToDto).toList();
}
@Secured("ROLE_USER")
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping(value = "{tournamentId}/teams/{teamId}")
@Operation(summary = "Delete team from a tournament", security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<Void> deleteTournamentTeam(@PathVariable("tournamentId") Long tournamentId,
@PathVariable("teamId") Long teamId,
Authentication authentication
) {
LOG.info("Delete /api/v1/tournaments/{}/teams/{}", tournamentId, teamId);
// check if user is organizer of tournament
if (!tournamentService.isOrganizer(authentication.getName(), tournamentId)) {
throw new AccessDeniedException("Current user isn't organizer of tournament.");
}
tournamentService.deleteTeam(tournamentId, teamId);
return ResponseEntity.noContent().build();
}
@PermitAll @PermitAll
@ResponseStatus(HttpStatus.OK) // No location header, thus 200 @ResponseStatus(HttpStatus.OK) // No location header, thus 200
@PostMapping("{tournamentId}/teams") @PostMapping("{tournamentId}/teams")
@Operation(summary = "Create a new team") @Operation(summary = "Create a new team")
public ResponseEntity<SignupTeamResponseDto> signupTeamForTournament( public ResponseEntity<SignupTeamResponseDto> signupTeamForTournament(
@PathVariable("tournamentId") long tournamentId, @PathVariable("tournamentId") long tournamentId,
@Valid @RequestBody CreateTeamDto messageDto) { @Valid @RequestBody CreateTeamDto messageDto) {
LOG.info("POST {} body: {}", BASE_ENDPOINT, messageDto); LOG.info("POST {} body: {}", BASE_ENDPOINT, messageDto);
final var signupResult = tournamentService.signupTeamForTournament(tournamentId, messageDto.name); final var signupResult = tournamentService.signupTeamForTournament(tournamentId, messageDto.name);
if (signupResult != SignupTeamResult.SUCCESS) { if (signupResult != SignupTeamResult.SUCCESS) {

View file

@ -3,6 +3,7 @@ package at.ac.tuwien.sepr.groupphase.backend.endpoint.exceptionhandler;
import at.ac.tuwien.sepr.groupphase.backend.exception.NotFoundException; import at.ac.tuwien.sepr.groupphase.backend.exception.NotFoundException;
import at.ac.tuwien.sepr.groupphase.backend.exception.PreconditionFailedException; import at.ac.tuwien.sepr.groupphase.backend.exception.PreconditionFailedException;
import at.ac.tuwien.sepr.groupphase.backend.exception.TournamentAlreadyStartedException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -37,7 +38,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
/** /**
* Use the @ExceptionHandler annotation to write handler for custom exceptions. * Use the @ExceptionHandler annotation to write handler for custom exceptions.
*/ */
@ExceptionHandler(value = { NotFoundException.class }) @ExceptionHandler(value = {NotFoundException.class})
protected ResponseEntity<Object> handleNotFound(RuntimeException ex, WebRequest request) { protected ResponseEntity<Object> handleNotFound(RuntimeException ex, WebRequest request) {
LOGGER.warn(ex.getMessage()); LOGGER.warn(ex.getMessage());
return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.NOT_FOUND, request); return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.NOT_FOUND, request);
@ -49,15 +50,15 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
*/ */
@Override @Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpHeaders headers,
HttpStatusCode status, WebRequest request) { HttpStatusCode status, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>(); Map<String, Object> body = new LinkedHashMap<>();
// Get all errors // Get all errors
List<String> errors = ex.getBindingResult() List<String> errors = ex.getBindingResult()
.getFieldErrors() .getFieldErrors()
.stream() .stream()
.map(err -> err.getField() + " " + err.getDefaultMessage()) .map(err -> err.getField() + " " + err.getDefaultMessage())
.collect(Collectors.toList()); .collect(Collectors.toList());
body.put("Validation errors", errors); body.put("Validation errors", errors);
return new ResponseEntity<>(body.toString(), headers, status); return new ResponseEntity<>(body.toString(), headers, status);
@ -67,18 +68,18 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
* Overrides the handling of PreconditionFailedException that sends a HTTP 400 * Overrides the handling of PreconditionFailedException that sends a HTTP 400
* response. * response.
*/ */
@ExceptionHandler(value = { PreconditionFailedException.class }) @ExceptionHandler(value = {PreconditionFailedException.class})
protected ResponseEntity<Object> handleAccessPreconditionFailedException(RuntimeException ex, WebRequest request) { protected ResponseEntity<Object> handleAccessPreconditionFailedException(RuntimeException ex, WebRequest request) {
var preconditionException = (PreconditionFailedException) ex; var preconditionException = (PreconditionFailedException) ex;
return handleExceptionInternal( return handleExceptionInternal(
ex, ex,
String.format("A precondition wasn't met: %s", preconditionException.getMessage()), String.format("A precondition wasn't met: %s", preconditionException.getMessage()),
new HttpHeaders(), new HttpHeaders(),
HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST,
request); request);
} }
@ExceptionHandler({ BadCredentialsException.class, UsernameNotFoundException.class }) @ExceptionHandler({BadCredentialsException.class, UsernameNotFoundException.class})
protected ResponseEntity<Object> handleBadCredentialsException(RuntimeException ex, WebRequest request) { protected ResponseEntity<Object> handleBadCredentialsException(RuntimeException ex, WebRequest request) {
LOGGER.debug(ex.getMessage()); LOGGER.debug(ex.getMessage());
@ -90,4 +91,16 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN,
request); request);
} }
@ExceptionHandler({TournamentAlreadyStartedException.class})
protected ResponseEntity<Object> handleTournamentAlreadyStartedException(RuntimeException ex, WebRequest request) {
LOGGER.debug(ex.getMessage());
return handleExceptionInternal(
ex,
"Tournament already started",
new HttpHeaders(),
HttpStatus.CONFLICT,
request);
}
} }

View file

@ -0,0 +1,19 @@
package at.ac.tuwien.sepr.groupphase.backend.exception;
public class TournamentAlreadyStartedException extends RuntimeException {
public TournamentAlreadyStartedException() {
}
public TournamentAlreadyStartedException(String message) {
super(message);
}
public TournamentAlreadyStartedException(String message, Throwable cause) {
super(message, cause);
}
public TournamentAlreadyStartedException(Exception e) {
super(e);
}
}

View file

@ -94,4 +94,12 @@ public interface TournamentService {
* (domain-level authorization) * (domain-level authorization)
*/ */
void generateKoMatchesForTournament(Long tournamentId, String subject); void generateKoMatchesForTournament(Long tournamentId, String subject);
/**
* Delete a team from a tournament.
*
* @param tournamentId the id of the tournament entity
* @param teamId the id of the team entity
*/
void deleteTeam(Long tournamentId, Long teamId);
} }

View file

@ -8,6 +8,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import at.ac.tuwien.sepr.groupphase.backend.entity.Team; import at.ac.tuwien.sepr.groupphase.backend.entity.Team;
import at.ac.tuwien.sepr.groupphase.backend.exception.TournamentAlreadyStartedException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
@ -42,12 +43,12 @@ public class TournamentServiceImpl implements TournamentService {
private KoStandingsRepository koStandingsRepository; private KoStandingsRepository koStandingsRepository;
public TournamentServiceImpl( public TournamentServiceImpl(
TournamentRepository tournamentRepository, TournamentRepository tournamentRepository,
TeamRepository teamRepository, TeamRepository teamRepository,
QualificationMatchRepository qualificatonRepository, QualificationMatchRepository qualificatonRepository,
UserRepository userRepository, UserRepository userRepository,
QualificationParticipationRepository qualificationParticipationRepository, QualificationParticipationRepository qualificationParticipationRepository,
KoStandingsRepository koStandingsRepository) { KoStandingsRepository koStandingsRepository) {
super(); super();
this.teamRepository = teamRepository; this.teamRepository = teamRepository;
@ -84,33 +85,33 @@ public class TournamentServiceImpl implements TournamentService {
@Override @Override
public List<QualificationMatch> generateQualificationMatchesForTournament(Long tournamentId, String currentUserName) public List<QualificationMatch> generateQualificationMatchesForTournament(Long tournamentId, String currentUserName)
throws PreconditionFailedException, AccessDeniedException, NotFoundException { throws PreconditionFailedException, AccessDeniedException, NotFoundException {
LOGGER.debug("Create qualifying matches for tournament with id {}", tournamentId); LOGGER.debug("Create qualifying matches for tournament with id {}", tournamentId);
var tournamentOrganizer = tournamentRepository var tournamentOrganizer = tournamentRepository
.findById(tournamentId) .findById(tournamentId)
.orElseThrow(() -> new NotFoundException()) .orElseThrow(() -> new NotFoundException())
.getOrganizer(); .getOrganizer();
if (tournamentOrganizer == null || !tournamentOrganizer.getUsername().equals(currentUserName)) { if (tournamentOrganizer == null || !tournamentOrganizer.getUsername().equals(currentUserName)) {
LOGGER.debug( LOGGER.debug(
"Couldn't create qualifying matches for tournament with id {}, because the user who started the process isn't the same as the creator of the tournament.", "Couldn't create qualifying matches for tournament with id {}, because the user who started the process isn't the same as the creator of the tournament.",
tournamentId); tournamentId);
throw new AccessDeniedException("Current user isn't organizer of tournament."); throw new AccessDeniedException("Current user isn't organizer of tournament.");
} }
var teams = teamRepository.findAllByTournamentId(tournamentId); var teams = teamRepository.findAllByTournamentId(tournamentId);
if (teams.size() < 16) { if (teams.size() < 16) {
LOGGER.debug( LOGGER.debug(
"Couldn't create qualifying matches for tournament with id {}, because there were less than 16 teams assigned to the tournament.", "Couldn't create qualifying matches for tournament with id {}, because there were less than 16 teams assigned to the tournament.",
tournamentId); tournamentId);
throw new PreconditionFailedException("Not enough teams in specified tournament."); throw new PreconditionFailedException("Not enough teams in specified tournament.");
} }
if (teams.stream().anyMatch(t -> qualificationParticipationRepository.existsByTeamId(t.getId()))) { if (teams.stream().anyMatch(t -> qualificationParticipationRepository.existsByTeamId(t.getId()))) {
LOGGER.debug( LOGGER.debug(
"Couldn't create qualifying matches for tournament with id {}, because there were already qualification matches assigned.", "Couldn't create qualifying matches for tournament with id {}, because there were already qualification matches assigned.",
tournamentId); tournamentId);
throw new PreconditionFailedException("Qualification matches already created for tournament."); throw new PreconditionFailedException("Qualification matches already created for tournament.");
} }
@ -146,7 +147,7 @@ public class TournamentServiceImpl implements TournamentService {
LOGGER.debug("Create new team {} for tournament {}", name, tournamentId); LOGGER.debug("Create new team {} for tournament {}", name, tournamentId);
final var tournament = tournamentRepository.findById(tournamentId) final var tournament = tournamentRepository.findById(tournamentId)
.orElseThrow(() -> new IllegalArgumentException("Tournament not found")); .orElseThrow(() -> new IllegalArgumentException("Tournament not found"));
final var newTeam = new Team(name, tournament); final var newTeam = new Team(name, tournament);
var result = tournament.signupTeam(newTeam); var result = tournament.signupTeam(newTeam);
@ -163,7 +164,7 @@ public class TournamentServiceImpl implements TournamentService {
LOGGER.debug("Get basic information about tournament {}", tournamentId); LOGGER.debug("Get basic information about tournament {}", tournamentId);
var tournament = tournamentRepository.findById(tournamentId) var tournament = tournamentRepository.findById(tournamentId)
.orElseThrow(() -> new IllegalArgumentException("Tournament not found")); .orElseThrow(() -> new IllegalArgumentException("Tournament not found"));
return tournament; return tournament;
} }
@ -183,11 +184,11 @@ public class TournamentServiceImpl implements TournamentService {
@Transactional @Transactional
public void deleteTournament(long tournamentId, String currentUserName) public void deleteTournament(long tournamentId, String currentUserName)
throws NotFoundException, AccessDeniedException { throws NotFoundException, AccessDeniedException {
LOGGER.debug("Deleting tournament with id {}", tournamentId); LOGGER.debug("Deleting tournament with id {}", tournamentId);
Tournament tournament = tournamentRepository.findById(tournamentId) Tournament tournament = tournamentRepository.findById(tournamentId)
.orElseThrow(() -> new NotFoundException("Tournament not found")); .orElseThrow(() -> new NotFoundException("Tournament not found"));
if (!tournament.getOrganizer().getUsername().equals(currentUserName)) { if (!tournament.getOrganizer().getUsername().equals(currentUserName)) {
throw new AccessDeniedException("You do not have permission to delete this tournament"); throw new AccessDeniedException("You do not have permission to delete this tournament");
@ -199,15 +200,15 @@ public class TournamentServiceImpl implements TournamentService {
@Transactional @Transactional
public void generateKoMatchesForTournament(Long tournamentId, String subjectName) public void generateKoMatchesForTournament(Long tournamentId, String subjectName)
throws NotFoundException, AccessDeniedException { throws NotFoundException, AccessDeniedException {
LOGGER.debug("Create knockout matches for tournament with id {}", tournamentId); LOGGER.debug("Create knockout matches for tournament with id {}", tournamentId);
// authorization // authorization
final Tournament tournament = tournamentRepository.getReferenceById(tournamentId); final Tournament tournament = tournamentRepository.getReferenceById(tournamentId);
if (!tournament.getOrganizer().getUsername().equals(subjectName)) { if (!tournament.getOrganizer().getUsername().equals(subjectName)) {
LOGGER.debug( LOGGER.debug(
"Subject {} illegally tried to generate KO phase of non-owned tournament {}", "Subject {} illegally tried to generate KO phase of non-owned tournament {}",
subjectName, tournamentId); subjectName, tournamentId);
throw new AccessDeniedException("Current user isn't organizer of tournament."); throw new AccessDeniedException("Current user isn't organizer of tournament.");
} }
@ -216,8 +217,8 @@ public class TournamentServiceImpl implements TournamentService {
qualiIncomplete = matches.stream().anyMatch(m -> m.getWinner() == null); qualiIncomplete = matches.stream().anyMatch(m -> m.getWinner() == null);
if (qualiIncomplete) { if (qualiIncomplete) {
LOGGER.debug( LOGGER.debug(
"Couldn't create knockout matches for tournament with id {}, because there were still qualification matches running.", "Couldn't create knockout matches for tournament with id {}, because there were still qualification matches running.",
tournamentId); tournamentId);
throw new PreconditionFailedException("Qualification matches still running."); throw new PreconditionFailedException("Qualification matches still running.");
} }
@ -230,14 +231,14 @@ public class TournamentServiceImpl implements TournamentService {
final var participations = qualificationParticipationRepository.findByQualificationMatchIn(matches); final var participations = qualificationParticipationRepository.findByQualificationMatchIn(matches);
final var pointsByTeam = participations.stream() final var pointsByTeam = participations.stream()
.collect(Collectors.groupingBy(QualificationParticipation::getTeam, .collect(Collectors.groupingBy(QualificationParticipation::getTeam,
Collectors.summingLong(p -> p.getQualificiatonMatch().getWinnerPoints()))); Collectors.summingLong(p -> p.getQualificiatonMatch().getWinnerPoints())));
final var rankedTeamIds = pointsByTeam.entrySet().stream() final var rankedTeamIds = pointsByTeam.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue())).map(e -> e.getKey()) .sorted((a, b) -> Long.compare(b.getValue(), a.getValue())).map(e -> e.getKey())
.limit(limit) .limit(limit)
.map(team -> new KoStanding(tournament, null, team)) .map(team -> new KoStanding(tournament, null, team))
.toArray(KoStanding[]::new); .toArray(KoStanding[]::new);
// map the teams to the leaf layer of the KO tree // map the teams to the leaf layer of the KO tree
// undefined ranking logic, so i just cross-match the teams like in wendy's // undefined ranking logic, so i just cross-match the teams like in wendy's
@ -258,7 +259,7 @@ public class TournamentServiceImpl implements TournamentService {
for (int i = 0; i < layer.length; i++) { for (int i = 0; i < layer.length; i++) {
final var preceeding = List.of(prevLayer[i * 2], prevLayer[i * 2 + 1]); final var preceeding = List.of(prevLayer[i * 2], prevLayer[i * 2 + 1]);
layer[i] = new KoStanding(tournament, layer[i] = new KoStanding(tournament,
preceeding, null); preceeding, null);
// passing the preceeding standings to the parent does not help us, as the child // passing the preceeding standings to the parent does not help us, as the child
// owns the relationship // owns the relationship
for (var p : preceeding) { for (var p : preceeding) {
@ -277,4 +278,21 @@ public class TournamentServiceImpl implements TournamentService {
koStandingsRepository.saveAndFlush(layer[0]); koStandingsRepository.saveAndFlush(layer[0]);
} }
@Override
public void deleteTeam(Long tournamentId, Long teamId)
throws NotFoundException, IllegalArgumentException, IllegalStateException {
LOGGER.debug("Delete team with id {} from tournament with id {}", teamId, tournamentId);
var team = teamRepository.findById(teamId).orElseThrow(() -> new NotFoundException("Team not found in tournament"));
var tournament = team.getTournament();
if (!tournament.getId().equals(tournamentId)) {
throw new NotFoundException("Team not found in tournament");
}
if ((long) tournament.getQualificationMatches().size() > 0) {
throw new TournamentAlreadyStartedException();
}
teamRepository.delete(team);
}
} }