begin switch to WSTO architecture

This commit is contained in:
2025-10-29 13:23:43 +01:00
parent f013ef8438
commit 4aa9cb5b34
8 changed files with 174 additions and 30 deletions

View File

@@ -1,21 +1,18 @@
package de.pnreichmuth.timekeep_backend.controllers; package de.pnreichmuth.timekeep_backend.controllers;
import de.pnreichmuth.timekeep_backend.entities.Racer; 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.RacerService;
import de.pnreichmuth.timekeep_backend.wsto.RacerWSTO;
import de.pnreichmuth.timekeep_backend.services.TeamService;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail; import org.springframework.http.ProblemDetail;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List; import org.springframework.web.bind.annotation.RequestParam;
import java.util.UUID; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/racer") @RequestMapping("/racer")
@@ -24,10 +21,9 @@ import java.util.UUID;
public class RacerRestController { public class RacerRestController {
private final RacerService racerService; private final RacerService racerService;
private final TeamService teamService;
@GetMapping("singleRacer") @GetMapping("singleRacer")
public ResponseEntity<Racer> getSingleRacers(@RequestParam Racer requestRacer){ public ResponseEntity<@NonNull RacerWSTO> getSingleRacers(@RequestParam Racer requestRacer){
Racer actualRacer = racerService.getRacer(requestRacer.getFirstName(), requestRacer.getLastName()); Racer actualRacer = racerService.getRacer(requestRacer.getFirstName(), requestRacer.getLastName());
if (actualRacer == null){ if (actualRacer == null){
return ResponseEntity.of( return ResponseEntity.of(
@@ -39,7 +35,7 @@ public class RacerRestController {
) )
).build(); ).build();
} }
return ResponseEntity.ok(actualRacer); return ResponseEntity.ok(RacerWSTO.of(actualRacer));
} }
// @GetMapping("byTeam") // @GetMapping("byTeam")

View File

@@ -4,17 +4,16 @@ import de.pnreichmuth.timekeep_backend.entities.Team;
import de.pnreichmuth.timekeep_backend.exceptions.TeamExistsException; import de.pnreichmuth.timekeep_backend.exceptions.TeamExistsException;
import de.pnreichmuth.timekeep_backend.exceptions.TeamNotFoundException; import de.pnreichmuth.timekeep_backend.exceptions.TeamNotFoundException;
import de.pnreichmuth.timekeep_backend.services.TeamService; import de.pnreichmuth.timekeep_backend.services.TeamService;
import de.pnreichmuth.timekeep_backend.wsto.TeamWSTO;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.HttpStatus;
import org.springframework.http.ProblemDetail; import org.springframework.http.ProblemDetail;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -27,36 +26,41 @@ public class TeamRestController {
private final TeamService teamService; private final TeamService teamService;
@PostMapping("createTeam") @PostMapping("createTeam")
public ResponseEntity<Team> createTeam(@RequestBody Team team){ public ResponseEntity<@NonNull TeamWSTO> createTeam(@RequestBody Team team){
try { try {
teamService.createTeam(team); teamService.createTeam(team);
return new ResponseEntity<>(team, HttpStatus.CREATED); return new ResponseEntity<>(TeamWSTO.of(team), HttpStatus.CREATED);
} catch (TeamExistsException e) { } catch (TeamExistsException e) {
return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build(); return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build();
} }
} }
@PostMapping("createTeamDebug") @PostMapping("createTeamDebug")
public ResponseEntity<Team> createTeam(){ public ResponseEntity<@NonNull TeamWSTO> createTeam(){
try { try {
Team team = teamService.createTeam(); Team team = teamService.createTeam();
return new ResponseEntity<>(team, HttpStatus.CREATED); team.setTeamName("DEBUG");
return new ResponseEntity<>(TeamWSTO.of(team), HttpStatus.CREATED);
} catch (TeamExistsException e) { } catch (TeamExistsException e) {
return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build(); return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,"This team already exists")).build();
} }
} }
@GetMapping("all") @GetMapping("all")
public ResponseEntity<@NonNull List<Team>> getAllTeams(){ public ResponseEntity<@NonNull List<TeamWSTO>> getAllTeams(){
List<Team> teams = teamService.getTeams(); List<Team> teams = teamService.getTeams();
List<TeamWSTO> teamWSTOs = new ArrayList<>();
if(teams.isEmpty()){ if(teams.isEmpty()){
return new ResponseEntity<>(HttpStatus.NOT_FOUND); 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") @GetMapping("single-team")
public ResponseEntity<@NonNull Team> getSingleTeam(@RequestBody Team reqTeam){ public ResponseEntity<@NonNull TeamWSTO> getSingleTeam(@RequestBody Team reqTeam){
UUID id = reqTeam.getId(); UUID id = reqTeam.getId();
String name = reqTeam.getTeamName(); String name = reqTeam.getTeamName();
Team actualTeam; Team actualTeam;
@@ -68,7 +72,7 @@ public class TeamRestController {
actualTeam = teamService.getTeam(name); actualTeam = teamService.getTeam(name);
} }
else return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Must provide either id or name")).build(); 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){ catch(TeamNotFoundException e){
if(id != null) return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,"Team with id "+ id +" not found")).build(); if(id != null) return ResponseEntity.of(ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,"Team with id "+ id +" not found")).build();

View File

@@ -18,4 +18,11 @@ public class Racer{
private String lastName; private String lastName;
private Boolean isFirstSemester; private Boolean isFirstSemester;
private String phoneNumber; 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;
}
} }

View File

@@ -5,12 +5,13 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Getter @Getter
@NoArgsConstructor
public class Station { public class Station {
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
@@ -19,4 +20,10 @@ public class Station {
private String name; private String name;
private String location; private String location;
private String passwordHash; private String passwordHash;
public Station(String name, String location){
this.name = name;
this.location = location;
this.passwordHash = null;
}
} }

View File

@@ -1,9 +1,7 @@
package de.pnreichmuth.timekeep_backend.entities; package de.pnreichmuth.timekeep_backend.entities;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.*;
import lombok.NonNull;
import lombok.Setter;
import java.util.HashMap; import java.util.HashMap;
@@ -36,7 +34,14 @@ public class Team {
this.passedStations = new HashMap<>(); this.passedStations = new HashMap<>();
} }
public Team(String teamName, boolean firstSemesterTeam, Map<String, Racer> members, Map<String, Station> 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 * 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 * 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){ public void removeMember(String firstName, String lastName){
this.members.remove(id); this.members.remove(this.generateMapKeyFromName(firstName, lastName));
this.checkFirstSemesterTeam(); this.checkFirstSemesterTeam();
} }
private String generateMapKeyFromName(String firstName, String lastName){
return String.format("%s, %s", firstName, lastName);
}
} }

View File

@@ -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()
);
}
}

View File

@@ -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()
);
}
}

View File

@@ -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<String, RacerWSTO> teamMembers,
Map<String, StationWSTO> 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<String, RacerWSTO> tmpMemberMap = new HashMap<>();
for(Map.Entry<String, Racer> member : team.getMembers().entrySet()){
tmpMemberMap.put(member.getKey(), RacerWSTO.of(member.getValue()));
}
Map<String, StationWSTO> tmpStationMap = new HashMap<>();
for(Map.Entry<String,Station> 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<String, Racer> tmpMemberMap = new HashMap<>();
for(Map.Entry<String, RacerWSTO> member : teamWSTO.teamMembers().entrySet()){
tmpMemberMap.put(member.getKey(), RacerWSTO.toEntity(member.getValue()));
}
Map<String, Station> tmpStationMap = new HashMap<>();
for (Map.Entry<String, StationWSTO> station : teamWSTO.passedStations().entrySet()) {
tmpStationMap.put(station.getKey(), StationWSTO.toEntity(station.getValue()));
}
return new Team(
teamWSTO.teamName(),
teamWSTO.firstSemesterTeam(),
tmpMemberMap,
tmpStationMap
);
}
}