Skip to content

feat: track patch sizes (implements #1) #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<List<String>> getAvailablePatches(@RequestParam final String minecraftVersion) {
public ResponseEntity<List<PatchInfo>> 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(
Expand All @@ -47,12 +50,12 @@ public record PatchDetails(String path, String status, String responsibleUser) {
public ResponseEntity<List<PatchDetails>> 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<String> paths) {}
public record Patches(String minecraftVersion, List<PatchInfo> patches) {}

@PreAuthorize("hasRole('PATCH')")
@PostMapping(
Expand All @@ -61,7 +64,7 @@ public record Patches(String minecraftVersion, List<String> paths) {}
produces = "text/plain"
)
public ResponseEntity<String> setPatches(@RequestBody final Patches input) {
this.patchService.setPatches(input.minecraftVersion(), input.paths());
this.patchService.setPatches(input.minecraftVersion(), input.patches());
return ResponseEntity.ok("Patches set.");
}

Expand All @@ -88,10 +91,30 @@ private String getUser(final Authentication authentication) {
)
public ResponseEntity<String> startPatch(final Authentication auth, @RequestBody final PatchId input) {
final String user = this.getUser(auth);
this.patchService.startWorkOnPatch(input, user);
final List<String> 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<String> 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<String> 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",
Expand All @@ -115,4 +138,20 @@ public ResponseEntity<String> 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<String> 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<String> handleException(final IllegalStateException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
}
9 changes: 9 additions & 0 deletions src/main/java/io/papermc/patchroulette/model/Patch.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Patch {
private Status status;

private String responsibleUser;
private Integer size;

public Patch() {
}
Expand Down Expand Up @@ -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;
}
}
8 changes: 8 additions & 0 deletions src/main/java/io/papermc/patchroulette/model/PatchId.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
48 changes: 35 additions & 13 deletions src/main/java/io/papermc/patchroulette/service/PatchService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -20,10 +24,11 @@ public PatchService(final PatchRepository patchRepository) {
}

@Transactional
public void setPatches(final String minecraftVersion, final List<String> paths) {
final List<Patch> patches = paths.stream().map(path -> {
public void setPatches(final String minecraftVersion, final List<PatchInfo> patchInfos) {
final List<Patch> 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;
Expand All @@ -41,17 +46,23 @@ public List<Patch> 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<String> startWorkOnPatches(final String minecraftVersion, final List<String> patches, final String user) {
final List<String> 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
Expand All @@ -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);
}
Expand Down