Correction bug suppression complète coté lore (et suppression dans tout ce qui est campagne de la partie lore liée).
Améliorations ux : - Bandeau en haut qui reste accessible lors de la création d'un élément (chapitre, page, scène etc...) - Mise en place d'un surlignage pour voir su quel élément on est positionné
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
package com.loremind.application.campaigncontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Arc;
|
||||
import com.loremind.domain.campaigncontext.Chapter;
|
||||
import com.loremind.domain.campaigncontext.ports.ArcRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.ChapterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.SceneRepository;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -17,11 +21,20 @@ import java.util.Optional;
|
||||
public class ArcService {
|
||||
|
||||
private final ArcRepository arcRepository;
|
||||
private final ChapterRepository chapterRepository;
|
||||
private final SceneRepository sceneRepository;
|
||||
|
||||
public ArcService(ArcRepository arcRepository) {
|
||||
public ArcService(ArcRepository arcRepository,
|
||||
ChapterRepository chapterRepository,
|
||||
SceneRepository sceneRepository) {
|
||||
this.arcRepository = arcRepository;
|
||||
this.chapterRepository = chapterRepository;
|
||||
this.sceneRepository = sceneRepository;
|
||||
}
|
||||
|
||||
/** Compte des entités qui seront supprimées en cascade avec l'arc. */
|
||||
public record DeletionImpact(int chapters, int scenes) {}
|
||||
|
||||
public Arc createArc(String name, String description, String campaignId, int order) {
|
||||
Arc arc = Arc.builder()
|
||||
.name(name)
|
||||
@@ -59,7 +72,31 @@ public class ArcService {
|
||||
return arcRepository.save(arc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'impact d'une suppression en cascade : chapitres + scènes
|
||||
* qui disparaîtront avec l'arc.
|
||||
*/
|
||||
public DeletionImpact getDeletionImpact(String id) {
|
||||
List<Chapter> chapters = chapterRepository.findByArcId(id);
|
||||
int sceneTotal = 0;
|
||||
for (Chapter chapter : chapters) {
|
||||
sceneTotal += sceneRepository.findByChapterId(chapter.getId()).size();
|
||||
}
|
||||
return new DeletionImpact(chapters.size(), sceneTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime l'arc et toutes ses entités dépendantes (chapitres → scènes).
|
||||
* Transactionnel : atomique.
|
||||
*/
|
||||
@Transactional
|
||||
public void deleteArc(String id) {
|
||||
for (Chapter chapter : chapterRepository.findByArcId(id)) {
|
||||
for (var scene : sceneRepository.findByChapterId(chapter.getId())) {
|
||||
sceneRepository.deleteById(scene.getId());
|
||||
}
|
||||
chapterRepository.deleteById(chapter.getId());
|
||||
}
|
||||
arcRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ package com.loremind.application.campaigncontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Chapter;
|
||||
import com.loremind.domain.campaigncontext.ports.ChapterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.SceneRepository;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -17,11 +19,16 @@ import java.util.Optional;
|
||||
public class ChapterService {
|
||||
|
||||
private final ChapterRepository chapterRepository;
|
||||
private final SceneRepository sceneRepository;
|
||||
|
||||
public ChapterService(ChapterRepository chapterRepository) {
|
||||
public ChapterService(ChapterRepository chapterRepository, SceneRepository sceneRepository) {
|
||||
this.chapterRepository = chapterRepository;
|
||||
this.sceneRepository = sceneRepository;
|
||||
}
|
||||
|
||||
/** Compte des scènes qui seront supprimées en cascade avec le chapitre. */
|
||||
public record DeletionImpact(int scenes) {}
|
||||
|
||||
public Chapter createChapter(String name, String description, String arcId, int order) {
|
||||
Chapter chapter = Chapter.builder()
|
||||
.name(name)
|
||||
@@ -58,7 +65,17 @@ public class ChapterService {
|
||||
return chapterRepository.save(chapter);
|
||||
}
|
||||
|
||||
/** Compte des scènes qui tomberont avec le chapitre. */
|
||||
public DeletionImpact getDeletionImpact(String id) {
|
||||
return new DeletionImpact(sceneRepository.findByChapterId(id).size());
|
||||
}
|
||||
|
||||
/** Supprime le chapitre et toutes ses scènes. Transactionnel : atomique. */
|
||||
@Transactional
|
||||
public void deleteChapter(String id) {
|
||||
for (var scene : sceneRepository.findByChapterId(id)) {
|
||||
sceneRepository.deleteById(scene.getId());
|
||||
}
|
||||
chapterRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,4 +68,12 @@ public class ArcController {
|
||||
arcService.deleteArc(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/deletion-impact")
|
||||
public ResponseEntity<ArcService.DeletionImpact> getDeletionImpact(@PathVariable String id) {
|
||||
if (!arcService.arcExists(id)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(arcService.getDeletionImpact(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,4 +68,12 @@ public class ChapterController {
|
||||
chapterService.deleteChapter(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/deletion-impact")
|
||||
public ResponseEntity<ChapterService.DeletionImpact> getDeletionImpact(@PathVariable String id) {
|
||||
if (!chapterService.chapterExists(id)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(chapterService.getDeletionImpact(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.loremind.application.campaigncontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Arc;
|
||||
import com.loremind.domain.campaigncontext.Chapter;
|
||||
import com.loremind.domain.campaigncontext.Scene;
|
||||
import com.loremind.domain.campaigncontext.ports.ArcRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.ChapterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.SceneRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -14,6 +18,7 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -26,6 +31,10 @@ public class ArcServiceTest {
|
||||
|
||||
@Mock
|
||||
private ArcRepository arcRepository;
|
||||
@Mock
|
||||
private ChapterRepository chapterRepository;
|
||||
@Mock
|
||||
private SceneRepository sceneRepository;
|
||||
|
||||
@InjectMocks
|
||||
private ArcService arcService;
|
||||
@@ -159,15 +168,48 @@ public class ArcServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteArc() {
|
||||
// Arrange
|
||||
doNothing().when(arcRepository).deleteById("arc-1");
|
||||
|
||||
// Act
|
||||
void testDeleteArc_EmptyArc() {
|
||||
// Aucun chapitre : Mockito renvoie List.of() par défaut.
|
||||
arcService.deleteArc("arc-1");
|
||||
|
||||
// Assert
|
||||
verify(arcRepository, times(1)).deleteById("arc-1");
|
||||
verify(arcRepository).deleteById("arc-1");
|
||||
verify(chapterRepository, never()).deleteById(anyString());
|
||||
verify(sceneRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteArc_CascadesChaptersAndScenes() {
|
||||
Chapter chapter = Chapter.builder().id("chap-1").arcId("arc-1").name("C").build();
|
||||
Scene s1 = Scene.builder().id("s-1").chapterId("chap-1").name("S1").build();
|
||||
Scene s2 = Scene.builder().id("s-2").chapterId("chap-1").name("S2").build();
|
||||
|
||||
when(chapterRepository.findByArcId("arc-1")).thenReturn(List.of(chapter));
|
||||
when(sceneRepository.findByChapterId("chap-1")).thenReturn(List.of(s1, s2));
|
||||
|
||||
arcService.deleteArc("arc-1");
|
||||
|
||||
verify(sceneRepository).deleteById("s-1");
|
||||
verify(sceneRepository).deleteById("s-2");
|
||||
verify(chapterRepository).deleteById("chap-1");
|
||||
verify(arcRepository).deleteById("arc-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDeletionImpact() {
|
||||
Chapter c1 = Chapter.builder().id("chap-1").arcId("arc-1").name("C1").build();
|
||||
Chapter c2 = Chapter.builder().id("chap-2").arcId("arc-1").name("C2").build();
|
||||
Scene s1 = Scene.builder().id("s-1").chapterId("chap-1").name("S1").build();
|
||||
Scene s2 = Scene.builder().id("s-2").chapterId("chap-2").name("S2").build();
|
||||
Scene s3 = Scene.builder().id("s-3").chapterId("chap-2").name("S3").build();
|
||||
|
||||
when(chapterRepository.findByArcId("arc-1")).thenReturn(List.of(c1, c2));
|
||||
when(sceneRepository.findByChapterId("chap-1")).thenReturn(List.of(s1));
|
||||
when(sceneRepository.findByChapterId("chap-2")).thenReturn(List.of(s2, s3));
|
||||
|
||||
ArcService.DeletionImpact impact = arcService.getDeletionImpact("arc-1");
|
||||
|
||||
assertEquals(2, impact.chapters());
|
||||
assertEquals(3, impact.scenes());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.loremind.application.campaigncontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Chapter;
|
||||
import com.loremind.domain.campaigncontext.Scene;
|
||||
import com.loremind.domain.campaigncontext.ports.ChapterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.SceneRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -14,6 +16,7 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -26,6 +29,8 @@ public class ChapterServiceTest {
|
||||
|
||||
@Mock
|
||||
private ChapterRepository chapterRepository;
|
||||
@Mock
|
||||
private SceneRepository sceneRepository;
|
||||
|
||||
@InjectMocks
|
||||
private ChapterService chapterService;
|
||||
@@ -157,15 +162,36 @@ public class ChapterServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteChapter() {
|
||||
// Arrange
|
||||
doNothing().when(chapterRepository).deleteById("chapter-1");
|
||||
|
||||
// Act
|
||||
void testDeleteChapter_EmptyChapter() {
|
||||
// Aucune scène : Mockito renvoie List.of() par défaut.
|
||||
chapterService.deleteChapter("chapter-1");
|
||||
|
||||
// Assert
|
||||
verify(chapterRepository, times(1)).deleteById("chapter-1");
|
||||
verify(chapterRepository).deleteById("chapter-1");
|
||||
verify(sceneRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteChapter_CascadesScenes() {
|
||||
Scene s1 = Scene.builder().id("s-1").chapterId("chapter-1").name("S1").build();
|
||||
Scene s2 = Scene.builder().id("s-2").chapterId("chapter-1").name("S2").build();
|
||||
when(sceneRepository.findByChapterId("chapter-1")).thenReturn(List.of(s1, s2));
|
||||
|
||||
chapterService.deleteChapter("chapter-1");
|
||||
|
||||
verify(sceneRepository).deleteById("s-1");
|
||||
verify(sceneRepository).deleteById("s-2");
|
||||
verify(chapterRepository).deleteById("chapter-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDeletionImpact() {
|
||||
Scene s1 = Scene.builder().id("s-1").chapterId("chapter-1").name("S1").build();
|
||||
Scene s2 = Scene.builder().id("s-2").chapterId("chapter-1").name("S2").build();
|
||||
when(sceneRepository.findByChapterId("chapter-1")).thenReturn(List.of(s1, s2));
|
||||
|
||||
ChapterService.DeletionImpact impact = chapterService.getDeletionImpact("chapter-1");
|
||||
|
||||
assertEquals(2, impact.scenes());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user