diff --git a/src/main/java/io/papermc/patchroulette/controller/RESTController.java b/src/main/java/io/papermc/patchroulette/controller/RESTController.java index 781ec38..7304eb9 100644 --- a/src/main/java/io/papermc/patchroulette/controller/RESTController.java +++ b/src/main/java/io/papermc/patchroulette/controller/RESTController.java @@ -1,13 +1,14 @@ package io.papermc.patchroulette.controller; -import io.papermc.patchroulette.model.Patch; import io.papermc.patchroulette.model.PatchId; import io.papermc.patchroulette.service.PatchService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -24,20 +25,22 @@ public RESTController(final PatchService patchService) { this.patchService = patchService; } + public record PatchInfo(String path, Integer size) {} + @PreAuthorize("hasRole('PATCH')") @GetMapping( value = "/get-available-patches", produces = "application/json" ) - public ResponseEntity> getAvailablePatches(@RequestParam final String minecraftVersion) { + public ResponseEntity> getAvailablePatches(@RequestParam final String minecraftVersion) { return ResponseEntity.ok( this.patchService.getAvailablePatches(minecraftVersion).stream() - .map(Patch::getPath) + .map(patch -> new PatchInfo(patch.getPath(), patch.getSize())) .toList() ); } - public record PatchDetails(String path, String status, String responsibleUser) {} + public record PatchDetails(String path, String status, String responsibleUser, Integer size) {} @PreAuthorize("hasRole('PATCH')") @GetMapping( @@ -47,12 +50,12 @@ public record PatchDetails(String path, String status, String responsibleUser) { public ResponseEntity> getAllPatches(@RequestParam final String minecraftVersion) { return ResponseEntity.ok( this.patchService.getAllPatches(minecraftVersion).stream() - .map(patch -> new PatchDetails(patch.getPath(), patch.getStatus().name(), patch.getResponsibleUser())) + .map(patch -> new PatchDetails(patch.getPath(), patch.getStatus().name(), patch.getResponsibleUser(), patch.getSize())) .toList() ); } - public record Patches(String minecraftVersion, List paths) {} + public record Patches(String minecraftVersion, List patches) {} @PreAuthorize("hasRole('PATCH')") @PostMapping( @@ -61,7 +64,7 @@ public record Patches(String minecraftVersion, List paths) {} produces = "text/plain" ) public ResponseEntity setPatches(@RequestBody final Patches input) { - this.patchService.setPatches(input.minecraftVersion(), input.paths()); + this.patchService.setPatches(input.minecraftVersion(), input.patches()); return ResponseEntity.ok("Patches set."); } @@ -88,10 +91,30 @@ private String getUser(final Authentication authentication) { ) public ResponseEntity startPatch(final Authentication auth, @RequestBody final PatchId input) { final String user = this.getUser(auth); - this.patchService.startWorkOnPatch(input, user); + final List result = this.patchService.startWorkOnPatches(input.getMinecraftVersion(), List.of(input.getPath()), user); + if (result.isEmpty()) { + return ResponseEntity.status(HttpStatus.CONFLICT).body("Patch not available."); + } return ResponseEntity.ok("Patch started."); } + public record PatchList(String minecraftVersion, List patches) {} + + @PreAuthorize("hasRole('PATCH')") + @PostMapping( + value = "/start-patches", + consumes = "application/json", + produces = "application/json" + ) + public ResponseEntity startPatch(final Authentication auth, @RequestBody final PatchList input) { + final String user = this.getUser(auth); + final List result = this.patchService.startWorkOnPatches(input.minecraftVersion(), input.patches(), user); + if (result.isEmpty()) { + return ResponseEntity.status(HttpStatus.CONFLICT).body("None of the patches are available."); + } + return ResponseEntity.ok(result); + } + @PreAuthorize("hasRole('PATCH')") @PostMapping( value = "/complete-patch", @@ -115,4 +138,20 @@ public ResponseEntity cancelPatch(@RequestBody final PatchId input) { return ResponseEntity.ok("Patch cancelled."); } + @PreAuthorize("hasRole('PATCH')") + @PostMapping( + value = "/undo-patch", + consumes = "application/json", + produces = "text/plain" + ) + public ResponseEntity undoPatch(final Authentication auth, @RequestBody final PatchId input) { + final String user = this.getUser(auth); + this.patchService.undoPatch(input, user); + return ResponseEntity.ok("Patch moved to WIP."); + } + + @ExceptionHandler(IllegalStateException.class) + public ResponseEntity handleException(final IllegalStateException e) { + return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage()); + } } diff --git a/src/main/java/io/papermc/patchroulette/model/Patch.java b/src/main/java/io/papermc/patchroulette/model/Patch.java index 73c1925..451ac47 100644 --- a/src/main/java/io/papermc/patchroulette/model/Patch.java +++ b/src/main/java/io/papermc/patchroulette/model/Patch.java @@ -22,6 +22,7 @@ public class Patch { private Status status; private String responsibleUser; + private Integer size; public Patch() { } @@ -57,4 +58,12 @@ public String getResponsibleUser() { public void setResponsibleUser(final String responsibleUser) { this.responsibleUser = responsibleUser; } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } } diff --git a/src/main/java/io/papermc/patchroulette/model/PatchId.java b/src/main/java/io/papermc/patchroulette/model/PatchId.java index f0c02dd..1f648e9 100644 --- a/src/main/java/io/papermc/patchroulette/model/PatchId.java +++ b/src/main/java/io/papermc/patchroulette/model/PatchId.java @@ -13,6 +13,14 @@ public class PatchId implements Serializable { public PatchId() { } + public String getMinecraftVersion() { + return minecraftVersion; + } + + public String getPath() { + return path; + } + @JsonCreator public PatchId(@JsonProperty("minecraftVersion") final String minecraftVersion, @JsonProperty("path") final String path) { diff --git a/src/main/java/io/papermc/patchroulette/service/PatchService.java b/src/main/java/io/papermc/patchroulette/service/PatchService.java index 3a22be5..6d1a9b4 100644 --- a/src/main/java/io/papermc/patchroulette/service/PatchService.java +++ b/src/main/java/io/papermc/patchroulette/service/PatchService.java @@ -4,11 +4,15 @@ import io.papermc.patchroulette.model.PatchId; import io.papermc.patchroulette.model.Status; import io.papermc.patchroulette.repository.PatchRepository; + +import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static io.papermc.patchroulette.controller.RESTController.PatchInfo; + @Service public class PatchService { @@ -20,10 +24,11 @@ public PatchService(final PatchRepository patchRepository) { } @Transactional - public void setPatches(final String minecraftVersion, final List paths) { - final List patches = paths.stream().map(path -> { + public void setPatches(final String minecraftVersion, final List patchInfos) { + final List patches = patchInfos.stream().map(patchInfo -> { final Patch patch = new Patch(); - patch.setPath(path); + patch.setPath(patchInfo.path()); + patch.setSize(patchInfo.size()); patch.setStatus(Status.AVAILABLE); patch.setMinecraftVersion(minecraftVersion); return patch; @@ -41,17 +46,23 @@ public List getAllPatches(final String minecraftVersion) { } @Transactional - public void startWorkOnPatch(final PatchId patchId, final String user) { - final Patch patch = this.patchRepository.getReferenceById(patchId); - if (patch.getStatus() != Status.AVAILABLE) { - throw new IllegalStateException("Patch " + patchId + " is not available"); - } - if (patch.getResponsibleUser() != null) { - throw new IllegalStateException("Patch " + patchId + " is already claimed by another user"); + public List startWorkOnPatches(final String minecraftVersion, final List patches, final String user) { + final List startedPatches = new ArrayList<>(); + for (final String path : patches) { + final PatchId patchId = new PatchId(minecraftVersion, path); + final Patch patch = this.patchRepository.getReferenceById(patchId); + if (patch.getStatus() != Status.AVAILABLE) { + continue; + } + if (patch.getResponsibleUser() != null) { + continue; + } + patch.setStatus(Status.WIP); + patch.setResponsibleUser(user); + this.patchRepository.save(patch); + startedPatches.add(path); } - patch.setStatus(Status.WIP); - patch.setResponsibleUser(user); - this.patchRepository.save(patch); + return startedPatches; } @Transactional @@ -78,6 +89,17 @@ public void finishWorkOnPatch(final PatchId patchId, final String user) { this.patchRepository.save(patch); } + @Transactional + public void undoPatch(final PatchId patchId, final String user) { + final Patch patch = this.patchRepository.getReferenceById(patchId); + if (patch.getStatus() != Status.DONE) { + throw new IllegalStateException("Patch " + patchId + " is not DONE"); + } + patch.setStatus(Status.WIP); + patch.setResponsibleUser(user); + this.patchRepository.save(patch); + } + public void clearPatches(final String minecraftVersion) { this.patchRepository.deleteAllByMinecraftVersion(minecraftVersion); }