From aba2d7fdf63936d58fa365138cb0d0dd5bbe265a Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sat, 22 Aug 2026 19:34:51 +0200 Subject: [PATCH 1/5] feat(item): add reorderLoreById method --- .../vulpes/backend/service/ItemService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/onelitefeather/vulpes/backend/service/ItemService.java b/src/main/java/net/onelitefeather/vulpes/backend/service/ItemService.java index ca27a3e..c4ebc7d 100644 --- a/src/main/java/net/onelitefeather/vulpes/backend/service/ItemService.java +++ b/src/main/java/net/onelitefeather/vulpes/backend/service/ItemService.java @@ -144,6 +144,16 @@ public interface ItemService extends CrudService Date: Sat, 22 Aug 2026 19:35:08 +0200 Subject: [PATCH 2/5] chore(item): implement new method --- .../backend/service/impl/ItemServiceImpl.java | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/onelitefeather/vulpes/backend/service/impl/ItemServiceImpl.java b/src/main/java/net/onelitefeather/vulpes/backend/service/impl/ItemServiceImpl.java index fb6e5a6..00299fc 100644 --- a/src/main/java/net/onelitefeather/vulpes/backend/service/impl/ItemServiceImpl.java +++ b/src/main/java/net/onelitefeather/vulpes/backend/service/impl/ItemServiceImpl.java @@ -4,8 +4,10 @@ import io.micronaut.data.model.Pageable; import jakarta.inject.Inject; import jakarta.inject.Singleton; +import jakarta.transaction.Transactional; import net.onelitefeather.vulpes.api.model.ItemEntity; import net.onelitefeather.vulpes.api.model.item.ItemEnchantmentEntity; +import net.onelitefeather.vulpes.api.model.item.ItemLoreEntity; import net.onelitefeather.vulpes.api.repository.ItemRepository; import net.onelitefeather.vulpes.api.repository.item.ItemEnchantmentRepository; import net.onelitefeather.vulpes.api.repository.item.ItemFlagRepository; @@ -20,6 +22,7 @@ import net.onelitefeather.vulpes.backend.domain.item.ItemModelResponseDTO; import net.onelitefeather.vulpes.backend.service.ItemService; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -116,19 +119,25 @@ public Page findLoreById(UUID id, Pageable pageable) { } @Override + @Transactional public ItemLoreResponseDTO updateLoreById(UUID id, ItemLoreDTO lore) { var byId = this.repository.findById(id); if (byId.isEmpty()) { return new ItemLoreResponseDTO.ItemLoreErrorDTO(GENERIC_ERROR); } var item = byId.get(); - var entity = lore.toEntity(); - entity.setItem(item); + var existingLore = this.itemLoreRepository.findById(lore.id()); + if (existingLore.isEmpty() || !existingLore.get().getItem().getId().equals(item.getId())) { + return new ItemLoreResponseDTO.ItemLoreErrorDTO(GENERIC_ERROR); + } + var entity = existingLore.get(); + entity.setText(lore.text()); var saved = this.itemLoreRepository.update(entity); return ItemLoreResponseDTO.ItemLoreDTO.createDTO(saved); } @Override + @Transactional public ItemLoreResponseDTO createLoreById(UUID id, ItemLoreDTO loreDto) { var byId = this.repository.findById(id); if (byId.isEmpty()) { @@ -137,6 +146,8 @@ public ItemLoreResponseDTO createLoreById(UUID id, ItemLoreDTO loreDto) { var item = byId.get(); var entity = loreDto.toEntity(); entity.setItem(item); + int nextIndex = (int) this.itemLoreRepository.findLoreById(item.getId(), Pageable.unpaged()).getTotalSize(); + entity.setOrderIndex(nextIndex); var saved = this.itemLoreRepository.save(entity); return ItemLoreResponseDTO.ItemLoreDTO.createDTO(saved); } @@ -160,6 +171,38 @@ public ItemLoreResponseDTO deleteLoreById(UUID id, UUID loreId) { return ItemLoreResponseDTO.ItemLoreDTO.createDTO(resolvedEntity); } + @Override + @Transactional + public ItemLoreResponseDTO reorderLoreById(UUID id, UUID entryId, int newIndex) { + var byId = this.repository.findById(id); + if (byId.isEmpty()) { + return new ItemLoreResponseDTO.ItemLoreErrorDTO(GENERIC_ERROR); + } + var item = byId.get(); + List loreLines = new ArrayList<>( + this.itemLoreRepository.findLoreById(item.getId(), Pageable.unpaged()).getContent()); + + var entryToMove = loreLines.stream() + .filter(entry -> entry.getId().equals(entryId)) + .findFirst() + .orElse(null); + + if (entryToMove == null) { + return new ItemLoreResponseDTO.ItemLoreErrorDTO(GENERIC_ERROR); + } + + loreLines.remove(entryToMove); + int clampedIndex = Math.max(0, Math.min(newIndex, loreLines.size())); + loreLines.add(clampedIndex, entryToMove); + + for (int i = 0; i < loreLines.size(); i++) { + loreLines.get(i).setOrderIndex(i); + } + this.itemLoreRepository.updateAll(loreLines); + + return ItemLoreResponseDTO.ItemLoreDTO.createDTO(entryToMove); + } + @Override public List deleteAllLoreById(UUID id) { var byId = this.repository.findById(id); From c715ff146a46814253bb685fa94fa7f9932e6878 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sat, 22 Aug 2026 19:35:51 +0200 Subject: [PATCH 3/5] chore(dto): remove orderIndex --- .../vulpes/backend/domain/item/ItemLoreDTO.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreDTO.java b/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreDTO.java index 13e7435..727ca32 100644 --- a/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreDTO.java +++ b/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreDTO.java @@ -6,7 +6,6 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Null; -import jakarta.validation.constraints.Positive; import net.onelitefeather.vulpes.api.model.item.ItemLoreEntity; import java.util.UUID; @@ -23,18 +22,13 @@ public record ItemLoreDTO( UUID id, @Schema(description = "Text of the lore", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(groups = {Create.class, Update.class}) - String text, - @Schema(description = "Order index of the lore", requiredMode = Schema.RequiredMode.REQUIRED) - @Positive - int orderIndex + String text ) { - public ItemLoreEntity toEntity() { ItemLoreEntity entity = new ItemLoreEntity(); entity.setId(this.id); entity.setText(this.text); - entity.setOrderIndex(this.orderIndex); return entity; } } From 78118278710a1248376628a35c48a55b13921485 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sat, 22 Aug 2026 19:35:59 +0200 Subject: [PATCH 4/5] chore(controller): implement new method --- .../controller/item/ItemLoreController.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/java/net/onelitefeather/vulpes/backend/controller/item/ItemLoreController.java b/src/main/java/net/onelitefeather/vulpes/backend/controller/item/ItemLoreController.java index 62ac9f8..d00af9d 100644 --- a/src/main/java/net/onelitefeather/vulpes/backend/controller/item/ItemLoreController.java +++ b/src/main/java/net/onelitefeather/vulpes/backend/controller/item/ItemLoreController.java @@ -8,6 +8,7 @@ import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Delete; import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Patch; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.annotation.Post; import io.micronaut.http.annotation.Produces; @@ -20,6 +21,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.inject.Inject; import net.onelitefeather.vulpes.backend.domain.item.ItemLoreDTO; +import net.onelitefeather.vulpes.backend.domain.item.ItemLoreReorderDTO; import net.onelitefeather.vulpes.backend.domain.item.ItemLoreResponseDTO; import net.onelitefeather.vulpes.backend.service.ItemService; import net.onelitefeather.vulpes.backend.validation.ValidationGroup; @@ -134,6 +136,33 @@ public HttpResponse createLore(@PathVariable("itemId") UUID return HttpResponse.ok(createResult); } + @Operation( + summary = "Reorder a lore entry of an item", + operationId = "reorderLore", + description = "Moves the lore entry identified by entryId to a new position within the lore list of the item identified by itemId.", + tags = {"Item"} + ) + @ApiResponse( + responseCode = "204", + description = "Lore successfully reordered." + ) + @ApiResponse( + responseCode = "400", + description = "Item or lore entry for the given ID was not found.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = ItemLoreResponseDTO.ItemLoreErrorDTO.class) + ) + ) + @Patch("/{itemId}/lore/reorder") + @Validated + public HttpResponse reorderLore(@PathVariable("itemId") UUID itemId, @Body ItemLoreReorderDTO reorder) { + ItemLoreResponseDTO reorderResult = itemService.reorderLoreById(itemId, reorder.entryId(), reorder.newIndex()); + if (reorderResult instanceof ItemLoreResponseDTO.ItemLoreErrorDTO) { + return HttpResponse.badRequest(reorderResult); + } + return HttpResponse.noContent(); + } @Operation( summary = "Delete lore of an item", From 7d98cb2b414e2281b17b84e3d5b9f190504510c8 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sat, 22 Aug 2026 19:36:42 +0200 Subject: [PATCH 5/5] feat(item): add lore reorder DTO --- .../domain/item/ItemLoreReorderDTO.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreReorderDTO.java diff --git a/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreReorderDTO.java b/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreReorderDTO.java new file mode 100644 index 0000000..ca30863 --- /dev/null +++ b/src/main/java/net/onelitefeather/vulpes/backend/domain/item/ItemLoreReorderDTO.java @@ -0,0 +1,22 @@ +package net.onelitefeather.vulpes.backend.domain.item; + +import io.micronaut.core.annotation.Introspected; +import io.micronaut.serde.annotation.Serdeable; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; + +import java.util.UUID; + +@Schema(description = "Request DTO to reorder a lore entry of an item") +@Introspected +@Serdeable +public record ItemLoreReorderDTO( + @Schema(description = "ID of the lore entry to move", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull + UUID entryId, + @Schema(description = "Target index of the lore entry within the lore list", requiredMode = Schema.RequiredMode.REQUIRED) + @PositiveOrZero + int newIndex +) { +}