Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 53 additions & 51 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>cat.udl.eps.softarch</groupId>
<artifactId>spring-template</artifactId>
Expand Down Expand Up @@ -50,13 +51,13 @@
<artifactId>spring-data-rest-hal-explorer</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Expand All @@ -77,7 +78,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -89,24 +90,24 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>6.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>6.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
Expand All @@ -118,36 +119,37 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<useModulePath>false</useModulePath>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<useModulePath>false</useModulePath>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cat.udl.eps.softarch.fll.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cat.udl.eps.softarch.fll.controller.dto.MatchAssignmentRequest;
import cat.udl.eps.softarch.fll.controller.dto.MatchAssignmentResponse;
import cat.udl.eps.softarch.fll.domain.Match;
import cat.udl.eps.softarch.fll.service.MatchAssignmentService;
import jakarta.validation.Valid;

@RestController
@RequestMapping("/matchAssignments")
public class MatchAssignmentController {
private final MatchAssignmentService matchAssignmentService;

public MatchAssignmentController(MatchAssignmentService matchAssignmentService) {
this.matchAssignmentService = matchAssignmentService;
}

@PostMapping("/assign")
public ResponseEntity<MatchAssignmentResponse> assignReferee(@Valid @RequestBody MatchAssignmentRequest request) {
Match assignedMatch = matchAssignmentService.assignReferee(request.matchId(), request.refereeId());
MatchAssignmentResponse response = new MatchAssignmentResponse(
String.valueOf(assignedMatch.getId()),
String.valueOf(assignedMatch.getReferee().getId()),
"ASSIGNED");
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cat.udl.eps.softarch.fll.controller.dto;

import jakarta.validation.constraints.NotBlank;

public record MatchAssignmentRequest(
@NotBlank String matchId,
@NotBlank String refereeId
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cat.udl.eps.softarch.fll.controller.dto;

public record MatchAssignmentResponse(
String matchId,
String refereeId,
String status
) {}
25 changes: 25 additions & 0 deletions src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.time.LocalTime;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
Expand Down Expand Up @@ -33,6 +35,13 @@ public class Match extends UriEntity<Long> {
@JoinColumn(name = "table_id")
private CompetitionTable competitionTable;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "referee_id")
private Referee referee;

@Enumerated(EnumType.STRING)
private MatchState state = MatchState.SCHEDULED;

public Match() {}

@Override
Expand Down Expand Up @@ -75,4 +84,20 @@ public CompetitionTable getCompetitionTable() {
public void setCompetitionTable(CompetitionTable competitionTable) {
this.competitionTable = competitionTable;
}

public Referee getReferee() {
return referee;
}

public void setReferee(Referee referee) {
this.referee = referee;
}

public MatchState getState() {
return state;
}

public void setState(MatchState state) {
this.state = state;
}
}
7 changes: 7 additions & 0 deletions src/main/java/cat/udl/eps/softarch/fll/domain/MatchState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cat.udl.eps.softarch.fll.domain;

public enum MatchState {
SCHEDULED,
IN_PROGRESS,
FINISHED
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
@Getter
@Setter
@ToString
@EqualsAndHashCode(of = "id")
@EqualsAndHashCode(of = "id", callSuper = false)
public class ScientificProject extends UriEntity<Long> {

@Id
Expand Down
1 change: 1 addition & 0 deletions src/main/java/cat/udl/eps/softarch/fll/domain/Team.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public String getId() {
private LocalDate inscriptionDate;

@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
@Size(max = 10, message = "A team cannot have more than 10 members")
@ToString.Exclude
private List<TeamMember> members = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cat.udl.eps.softarch.fll.exception;

public enum MatchAssignmentErrorCode {
MATCH_NOT_FOUND,
REFEREE_NOT_FOUND,
INVALID_ROLE,
AVAILABILITY_CONFLICT,
MATCH_ALREADY_HAS_REFEREE,
INVALID_MATCH_STATE,
INVALID_ID_FORMAT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cat.udl.eps.softarch.fll.exception;

public class MatchAssignmentException extends RuntimeException {
private final MatchAssignmentErrorCode errorCode;

public MatchAssignmentException(MatchAssignmentErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public MatchAssignmentErrorCode getErrorCode() {
return errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cat.udl.eps.softarch.fll.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class MatchAssignmentExceptionHandler {

@ExceptionHandler(MatchAssignmentException.class)
public ResponseEntity<ErrorResponse> handleMatchAssignmentException(MatchAssignmentException ex) {
HttpStatus status = switch (ex.getErrorCode()) {
case MATCH_NOT_FOUND, REFEREE_NOT_FOUND -> HttpStatus.NOT_FOUND;
case AVAILABILITY_CONFLICT, MATCH_ALREADY_HAS_REFEREE -> HttpStatus.CONFLICT;
case INVALID_ROLE, INVALID_MATCH_STATE, INVALID_ID_FORMAT -> HttpStatus.UNPROCESSABLE_CONTENT;
};
ErrorResponse body = new ErrorResponse(ex.getErrorCode().name(), ex.getMessage());
return ResponseEntity.status(status).body(body);
}

public record ErrorResponse(String error, String message) {}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
package cat.udl.eps.softarch.fll.repository;

import java.time.LocalTime;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import cat.udl.eps.softarch.fll.domain.Match;
import cat.udl.eps.softarch.fll.domain.Referee;
import jakarta.persistence.LockModeType;

@Repository
@RepositoryRestResource
public interface MatchRepository extends CrudRepository<Match, Long>, PagingAndSortingRepository<Match, Long> {
@Query("""
SELECT m FROM Match m
WHERE m.referee = :referee
AND m.startTime < :newMatchEndTime
AND m.endTime > :newMatchStartTime
AND m.id <> :currentMatchId
""")
List<Match> findOverlappingAssignments(
@Param("referee") Referee referee,
@Param("newMatchStartTime") LocalTime newMatchStartTime,
@Param("newMatchEndTime") LocalTime newMatchEndTime,
@Param("currentMatchId") Long currentMatchId);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT m FROM Match m WHERE m.id = :id")
@RestResource(exported = false)
Optional<Match> findByIdForUpdate(@Param("id") Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
Expand All @@ -10,6 +12,7 @@
import cat.udl.eps.softarch.fll.domain.Volunteer;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.persistence.LockModeType;

@Tag(name = "Volunteers", description = "Repository for managing Volunteer entities")
@RepositoryRestResource
Expand All @@ -34,5 +37,10 @@ public interface VolunteerRepository extends CrudRepository<Volunteer, Long>, Pa
@Operation(summary = "Find volunteer by email",
description = "Returns the Volunteer whose email address matches the given value.")
Optional<Volunteer> findByEmailAddress(@Param("email") String email);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT v FROM Volunteer v WHERE v.id = :id")
@RestResource(exported = false)
Optional<Volunteer> findByIdForUpdate(@Param("id") Long id);
}

Loading