PMT-43: Separate Validation Logic

This commit is contained in:
Dominik Säume 2024-10-23 09:48:18 +02:00
parent 2cd2b73ffc
commit 51f1f7a0af
Signed by untrusted user who does not match committer: Snoweuph
GPG key ID: BEFC41DA223CEC55
3 changed files with 59 additions and 23 deletions

View file

@ -10,6 +10,7 @@ import de.hmmh.pmt.employee.ApiClientFactory;
import de.hmmh.pmt.employee.dtos.EmployeeResponseDTO; import de.hmmh.pmt.employee.dtos.EmployeeResponseDTO;
import de.hmmh.pmt.oas.DefaultApi; import de.hmmh.pmt.oas.DefaultApi;
import de.hmmh.pmt.util.Mapper; import de.hmmh.pmt.util.Mapper;
import de.hmmh.pmt.util.Validator;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -31,6 +32,10 @@ public class ApiController implements DefaultApi {
@Autowired @Autowired
private ApiClientFactory apiClientFactory; private ApiClientFactory apiClientFactory;
@Autowired @Autowired
private Validator validator;
@Autowired
private ApiTools apiTools;
@Autowired
private ProjectRepository projectRepository; private ProjectRepository projectRepository;
@Autowired @Autowired
AllocationRepository allocationRepository; AllocationRepository allocationRepository;
@ -88,7 +93,7 @@ public class ApiController implements DefaultApi {
} }
Project project = mapper.map(body); Project project = mapper.map(body);
if (!project.isValid()) { if (!validator.isValidProject(project)) {
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
} }
projectRepository.save(project); projectRepository.save(project);
@ -122,16 +127,11 @@ public class ApiController implements DefaultApi {
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
} }
long start = project.getStart().toEpochSecond(ZoneOffset.UTC); if (validator.areAllocationTimeRangesOverlapping(
long plannedEnd = project.getPlannedEnd().toEpochSecond(ZoneOffset.UTC); project.getStart(),
List<Allocation> allocations = allocationRepository.findAllByEmployeeId(body.getEmployeeId()); project.getPlannedEnd(),
if (allocations.stream() allocationRepository.findAllByEmployeeId(body.getEmployeeId())
.map(Allocation::getProject) )) {
.anyMatch(allocatedProject -> {
long allocatedStart = allocatedProject.getStart().toEpochSecond(ZoneOffset.UTC);
long allocatedPlannedEnd = allocatedProject.getPlannedEnd().toEpochSecond(ZoneOffset.UTC);
return Math.max(start, allocatedStart) <= Math.min(plannedEnd, allocatedPlannedEnd);
})) {
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
} }

View file

@ -47,16 +47,4 @@ public class Project {
private LocalDateTime plannedEnd; private LocalDateTime plannedEnd;
private LocalDateTime realEnd; // Cant be named just "end" because it's and SQL Keyword private LocalDateTime realEnd; // Cant be named just "end" because it's and SQL Keyword
public boolean isValid() {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Project>> violations = validator.validate(this);
return violations.isEmpty() &&
plannedEnd.isAfter(start) &&
(realEnd == null || realEnd.isAfter(start));
} }
}

View file

@ -0,0 +1,48 @@
package de.hmmh.pmt.util;
import de.hmmh.pmt.db.Allocation;
import de.hmmh.pmt.db.Project;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Set;
@Component
public class Validator {
public boolean isValidProject(Project project) {
jakarta.validation.Validator validator = Validation
.buildDefaultValidatorFactory()
.getValidator();
Set<ConstraintViolation<Project>> violations = validator.validate(project);
LocalDateTime start = project.getStart();
LocalDateTime plannedEnd = project.getPlannedEnd();
LocalDateTime realEnd = project.getRealEnd();
return violations.isEmpty() &&
plannedEnd.isAfter(start) &&
(realEnd == null || realEnd.isAfter(start));
}
public boolean areAllocationTimeRangesOverlapping(
LocalDateTime start,
LocalDateTime plannedEnd,
List<Allocation> allocations
){
long startUnix = start.toEpochSecond(ZoneOffset.UTC);
long plannedEndUnix = plannedEnd.toEpochSecond(ZoneOffset.UTC);
return allocations.stream()
.map(Allocation::getProject)
.anyMatch(allocatedProject -> {
long allocatedStart = allocatedProject.getStart().toEpochSecond(ZoneOffset.UTC);
long allocatedPlannedEnd = allocatedProject.getPlannedEnd().toEpochSecond(ZoneOffset.UTC);
return Math.max(startUnix, allocatedStart) <= Math.min(plannedEndUnix, allocatedPlannedEnd);
});
}
}