From 4aa9cb5b344df38fd88d069de5e5d92d740f062a Mon Sep 17 00:00:00 2001
From: Paul Reichmuth
Date: Wed, 29 Oct 2025 13:23:43 +0100
Subject: [PATCH] begin switch to WSTO architecture
---
.../controllers/RacerRestController.java | 18 +++---
.../controllers/TeamRestController.java | 26 +++++----
.../timekeep_backend/entities/Racer.java | 7 +++
.../timekeep_backend/entities/Station.java | 9 ++-
.../timekeep_backend/entities/Team.java | 24 +++++---
.../timekeep_backend/wsto/RacerWSTO.java | 35 ++++++++++++
.../timekeep_backend/wsto/StationWSTO.java | 28 +++++++++
.../timekeep_backend/wsto/TeamWSTO.java | 57 +++++++++++++++++++
8 files changed, 174 insertions(+), 30 deletions(-)
create mode 100644 src/main/java/de/pnreichmuth/timekeep_backend/wsto/RacerWSTO.java
create mode 100644 src/main/java/de/pnreichmuth/timekeep_backend/wsto/StationWSTO.java
create mode 100644 src/main/java/de/pnreichmuth/timekeep_backend/wsto/TeamWSTO.java
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/controllers/RacerRestController.java b/src/main/java/de/pnreichmuth/timekeep_backend/controllers/RacerRestController.java
index 0b83995..f3a5016 100644
--- a/src/main/java/de/pnreichmuth/timekeep_backend/controllers/RacerRestController.java
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/controllers/RacerRestController.java
@@ -1,21 +1,18 @@
package de.pnreichmuth.timekeep_backend.controllers;
import de.pnreichmuth.timekeep_backend.entities.Racer;
-import de.pnreichmuth.timekeep_backend.entities.Team;
import de.pnreichmuth.timekeep_backend.services.RacerService;
-
-import de.pnreichmuth.timekeep_backend.services.TeamService;
+import de.pnreichmuth.timekeep_backend.wsto.RacerWSTO;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
-import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-import java.util.UUID;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/racer")
@@ -24,10 +21,9 @@ import java.util.UUID;
public class RacerRestController {
private final RacerService racerService;
- private final TeamService teamService;
@GetMapping("singleRacer")
- public ResponseEntity getSingleRacers(@RequestParam Racer requestRacer){
+ public ResponseEntity<@NonNull RacerWSTO> getSingleRacers(@RequestParam Racer requestRacer){
Racer actualRacer = racerService.getRacer(requestRacer.getFirstName(), requestRacer.getLastName());
if (actualRacer == null){
return ResponseEntity.of(
@@ -39,7 +35,7 @@ public class RacerRestController {
)
).build();
}
- return ResponseEntity.ok(actualRacer);
+ return ResponseEntity.ok(RacerWSTO.of(actualRacer));
}
// @GetMapping("byTeam")
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/controllers/TeamRestController.java b/src/main/java/de/pnreichmuth/timekeep_backend/controllers/TeamRestController.java
index c10679e..f7c4f3f 100644
--- a/src/main/java/de/pnreichmuth/timekeep_backend/controllers/TeamRestController.java
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/controllers/TeamRestController.java
@@ -4,17 +4,16 @@ import de.pnreichmuth.timekeep_backend.entities.Team;
import de.pnreichmuth.timekeep_backend.exceptions.TeamExistsException;
import de.pnreichmuth.timekeep_backend.exceptions.TeamNotFoundException;
import de.pnreichmuth.timekeep_backend.services.TeamService;
+import de.pnreichmuth.timekeep_backend.wsto.TeamWSTO;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.antlr.v4.runtime.misc.NotNull;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
-import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -27,36 +26,41 @@ public class TeamRestController {
private final TeamService teamService;
@PostMapping("createTeam")
- public ResponseEntity createTeam(@RequestBody Team team){
+ public ResponseEntity<@NonNull TeamWSTO> createTeam(@RequestBody Team team){
try {
teamService.createTeam(team);
- return new ResponseEntity<>(team, HttpStatus.CREATED);
+ return new ResponseEntity<>(TeamWSTO.of(team), HttpStatus.CREATED);
} catch (TeamExistsException e) {
return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build();
}
}
@PostMapping("createTeamDebug")
- public ResponseEntity createTeam(){
+ public ResponseEntity<@NonNull TeamWSTO> createTeam(){
try {
Team team = teamService.createTeam();
- return new ResponseEntity<>(team, HttpStatus.CREATED);
+ team.setTeamName("DEBUG");
+ return new ResponseEntity<>(TeamWSTO.of(team), HttpStatus.CREATED);
} catch (TeamExistsException e) {
return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build();
}
}
@GetMapping("all")
- public ResponseEntity<@NonNull List> getAllTeams(){
+ public ResponseEntity<@NonNull List> getAllTeams(){
List teams = teamService.getTeams();
+ List teamWSTOs = new ArrayList<>();
if(teams.isEmpty()){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
- return new ResponseEntity<>(teams, HttpStatus.OK);
+ for(Team team : teams){
+ teamWSTOs.add(TeamWSTO.of(team));
+ }
+ return new ResponseEntity<>(teamWSTOs, HttpStatus.OK);
}
@GetMapping("single-team")
- public ResponseEntity<@NonNull Team> getSingleTeam(@RequestBody Team reqTeam){
+ public ResponseEntity<@NonNull TeamWSTO> getSingleTeam(@RequestBody Team reqTeam){
UUID id = reqTeam.getId();
String name = reqTeam.getTeamName();
Team actualTeam;
@@ -68,7 +72,7 @@ public class TeamRestController {
actualTeam = teamService.getTeam(name);
}
else return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Must provide either id or name")).build();
- return new ResponseEntity<>(actualTeam, HttpStatus.OK);
+ return new ResponseEntity<>(TeamWSTO.of(actualTeam), HttpStatus.OK);
}
catch(TeamNotFoundException e){
if(id != null) return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,"Team with id "+ id +" not found")).build();
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Racer.java b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Racer.java
index f5b1f4a..8e27c12 100644
--- a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Racer.java
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Racer.java
@@ -18,4 +18,11 @@ public class Racer{
private String lastName;
private Boolean isFirstSemester;
private String phoneNumber;
+
+ public Racer(String firstName, String lastName, Boolean isFirstSemester, String phoneNumber){
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.isFirstSemester = isFirstSemester;
+ this.phoneNumber = phoneNumber;
+ }
}
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Station.java b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Station.java
index 49275f3..add004a 100644
--- a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Station.java
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Station.java
@@ -5,12 +5,13 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import java.util.UUID;
@Entity
@Getter
-
+@NoArgsConstructor
public class Station {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@@ -19,4 +20,10 @@ public class Station {
private String name;
private String location;
private String passwordHash;
+
+ public Station(String name, String location){
+ this.name = name;
+ this.location = location;
+ this.passwordHash = null;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Team.java b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Team.java
index e304a64..c7db604 100644
--- a/src/main/java/de/pnreichmuth/timekeep_backend/entities/Team.java
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/entities/Team.java
@@ -1,9 +1,7 @@
package de.pnreichmuth.timekeep_backend.entities;
import jakarta.persistence.*;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.Setter;
+import lombok.*;
import java.util.HashMap;
@@ -36,7 +34,14 @@ public class Team {
this.passedStations = new HashMap<>();
}
-
+ public Team(String teamName, boolean firstSemesterTeam, Map members, Map passedStations){
+ this.teamName = teamName;
+ this.firstSemesterTeam = firstSemesterTeam;
+ this.members = new HashMap<>(members.size());
+ this.members.putAll(members);
+ this.passedStations = new HashMap<>(passedStations.size());
+ this.passedStations.putAll(passedStations);
+ }
/**
* Treat this team as a team of first semester students, if at least 50% of its members are in the first semester
@@ -61,10 +66,15 @@ public class Team {
/**
* Removes a member from the team by name
- * @param id the UUID of the member to be removed
+ * @param firstName the first name of the racer to be removed
+ * @param lastName the last name of the racer to be removed
*/
- public void removeMember(UUID id){
- this.members.remove(id);
+ public void removeMember(String firstName, String lastName){
+ this.members.remove(this.generateMapKeyFromName(firstName, lastName));
this.checkFirstSemesterTeam();
}
+
+ private String generateMapKeyFromName(String firstName, String lastName){
+ return String.format("%s, %s", firstName, lastName);
+ }
}
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/wsto/RacerWSTO.java b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/RacerWSTO.java
new file mode 100644
index 0000000..4cf43a7
--- /dev/null
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/RacerWSTO.java
@@ -0,0 +1,35 @@
+package de.pnreichmuth.timekeep_backend.wsto;
+
+import de.pnreichmuth.timekeep_backend.entities.Racer;
+
+import java.util.Objects;
+
+public record RacerWSTO(
+ String firstName,
+ String lastName,
+ boolean isFirstSemester,
+ String phoneNumber
+) {
+ public RacerWSTO{
+ Objects.requireNonNull(firstName, "firstName must be set");
+ Objects.requireNonNull(lastName, "lastName must be set");
+ }
+
+ public static RacerWSTO of(Racer racer){
+ return new RacerWSTO(
+ racer.getFirstName(),
+ racer.getLastName(),
+ racer.getIsFirstSemester(),
+ racer.getPhoneNumber()
+ );
+ }
+
+ public static Racer toEntity(RacerWSTO racerWSTO){
+ return new Racer(
+ racerWSTO.firstName(),
+ racerWSTO.lastName(),
+ racerWSTO.isFirstSemester(),
+ racerWSTO.phoneNumber()
+ );
+ }
+}
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/wsto/StationWSTO.java b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/StationWSTO.java
new file mode 100644
index 0000000..8fb80d5
--- /dev/null
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/StationWSTO.java
@@ -0,0 +1,28 @@
+package de.pnreichmuth.timekeep_backend.wsto;
+
+import de.pnreichmuth.timekeep_backend.entities.Station;
+
+import java.util.Objects;
+
+public record StationWSTO(
+ String name,
+ String locationName
+) {
+ public StationWSTO{
+ Objects.requireNonNull(locationName, "Station has to have a location");
+ }
+
+ public static StationWSTO of(Station station){
+ return new StationWSTO(
+ station.getName(),
+ station.getLocation()
+ );
+ }
+
+ public static Station toEntity(StationWSTO stationWSTO){
+ return new Station(
+ stationWSTO.name(),
+ stationWSTO.locationName()
+ );
+ }
+}
diff --git a/src/main/java/de/pnreichmuth/timekeep_backend/wsto/TeamWSTO.java b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/TeamWSTO.java
new file mode 100644
index 0000000..f627b2f
--- /dev/null
+++ b/src/main/java/de/pnreichmuth/timekeep_backend/wsto/TeamWSTO.java
@@ -0,0 +1,57 @@
+package de.pnreichmuth.timekeep_backend.wsto;
+
+import de.pnreichmuth.timekeep_backend.entities.Racer;
+import de.pnreichmuth.timekeep_backend.entities.Station;
+import de.pnreichmuth.timekeep_backend.entities.Team;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public record TeamWSTO(String teamName,
+ boolean firstSemesterTeam,
+ Map teamMembers,
+ Map passedStations) {
+ public TeamWSTO{
+ Objects.requireNonNull(teamName, "Cannot identify Team without a name");
+ teamMembers = Map.copyOf(teamMembers);
+ passedStations = Map.copyOf(passedStations);
+ }
+
+ public static TeamWSTO of(Team team){
+ Map tmpMemberMap = new HashMap<>();
+ for(Map.Entry member : team.getMembers().entrySet()){
+ tmpMemberMap.put(member.getKey(), RacerWSTO.of(member.getValue()));
+ }
+
+ Map tmpStationMap = new HashMap<>();
+ for(Map.Entry station : team.getPassedStations().entrySet()){
+ tmpStationMap.put(station.getKey(), StationWSTO.of(station.getValue()));
+ }
+ return new TeamWSTO(
+ team.getTeamName(),
+ team.isFirstSemesterTeam(),
+ Map.copyOf(tmpMemberMap),
+ Map.copyOf(tmpStationMap)
+ );
+ }
+
+ public static Team toEntity(TeamWSTO teamWSTO){
+ Map tmpMemberMap = new HashMap<>();
+ for(Map.Entry member : teamWSTO.teamMembers().entrySet()){
+ tmpMemberMap.put(member.getKey(), RacerWSTO.toEntity(member.getValue()));
+ }
+
+ Map tmpStationMap = new HashMap<>();
+ for (Map.Entry station : teamWSTO.passedStations().entrySet()) {
+ tmpStationMap.put(station.getKey(), StationWSTO.toEntity(station.getValue()));
+ }
+
+ return new Team(
+ teamWSTO.teamName(),
+ teamWSTO.firstSemesterTeam(),
+ tmpMemberMap,
+ tmpStationMap
+ );
+ }
+}