From 0a24e1455b5fe6daebde791657a89ada7c541dbe Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Thu, 30 Apr 2026 17:03:06 +0200 Subject: [PATCH 01/16] draft merge some modifications into a new composite Signed-off-by: Mathieu DEHARBE --- .../server/CompositeController.java | 12 +++++++++++ .../service/NetworkModificationService.java | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/java/org/gridsuite/modification/server/CompositeController.java b/src/main/java/org/gridsuite/modification/server/CompositeController.java index 89fd56789..903a5769d 100644 --- a/src/main/java/org/gridsuite/modification/server/CompositeController.java +++ b/src/main/java/org/gridsuite/modification/server/CompositeController.java @@ -78,6 +78,18 @@ public ResponseEntity moveSubModification( return ResponseEntity.ok().build(); } + @PostMapping(value = "/groups/{groupUuid}/composite-modification", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Merge some network modifications into a new composite modification") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) + public CompletableFuture> mergeNetworkModificationsIntoNewComposite( + @Parameter(description = "group UUID where the modifications are located") @PathVariable("groupUuid") UUID targetGroupUuid, + @RequestBody Pair, List> modificationApplicationContexts) { + return networkModificationService.mergeNetworkModificationsIntoNewComposite( + targetGroupUuid, + modificationApplicationContexts + ).thenApply(ResponseEntity.ok()::body); + } + @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Create a network composite modification") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java index 2437ba88d..2097c458b 100644 --- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java @@ -21,6 +21,7 @@ import org.gridsuite.filter.AbstractFilter; import org.gridsuite.modification.ModificationType; import org.gridsuite.modification.NetworkModificationException; +import org.gridsuite.modification.dto.CompositeModificationInfos; import org.gridsuite.modification.dto.EquipmentModificationInfos; import org.gridsuite.modification.dto.GenerationDispatchInfos; import org.gridsuite.modification.dto.ModificationInfos; @@ -474,6 +475,25 @@ public CompletableFuture insertCompositeModification new NetworkModificationsResult(ids, result)); } + @Transactional + public CompletableFuture mergeNetworkModificationsIntoNewComposite( + UUID targetGroupUuid, + @NonNull Pair, List> modificationApplicationContexts) { + // TODO : get the target (groupUuid or composite Uuid) + + // TODO : create the composite + + CompositeModificationInfos composite = new CompositeModificationInfos(); + + // TODO : assign modifications (remove previous assignment) + + // apply the composite (and implicitely those contained) : + return applyModifications(targetGroupUuid, List.of(composite), modificationApplicationContexts.getSecond()) + .thenApply(results -> + new NetworkModificationsResult(List.of(composite.getUuid()), results) + ); + } + @Transactional public UUID createNetworkCompositeModification(@NonNull List modificationUuids) { return networkModificationRepository.createNetworkCompositeModification(modificationUuids); From 166a8bc787020d5ff64d248f3cd0639bc5f2200f Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Tue, 5 May 2026 10:17:29 +0200 Subject: [PATCH 02/16] mergeNetworkModificationsIntoNewComposite Signed-off-by: Mathieu DEHARBE --- .../repositories/ModificationRepository.java | 5 +++ .../NetworkModificationRepository.java | 44 +++++++++++++++++++ .../service/NetworkModificationService.java | 13 +++--- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java index 24397faa3..7d710d65d 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java @@ -65,6 +65,11 @@ public interface ModificationRepository extends JpaRepository findModificationIdsByCompositeModificationId(UUID uuid); + // return the uuid of the composite containing the modifcation sent as parameter + // TODO : à changer après fusion de ma fiche sur les ordre de modifications de réseau + @Query(value = "SELECT cast(id AS UUID) FROM composite_modification_sub_modifications WHERE modification_id = :uuid ORDER BY modifications_order", nativeQuery = true) + UUID findCompositeIdByContainedModificationId(UUID uuid); + @Query(value = "SELECT cast(modification_id AS VARCHAR) FROM composite_modification_sub_modifications WHERE id IN (?1) ORDER BY modifications_order", nativeQuery = true) List findModificationIdsByCompositeModificationIdIn(List uuids); diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 931c39379..cb7fb6d0a 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -916,6 +916,50 @@ public List insertCompositeModifications( return newEntities.stream().map(ModificationEntity::toModificationInfos).toList(); } + @Transactional + public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( + List mergedModificationsUuids) { + // get the target (groupUuid or composite Uuid of the first merged modification + its index in this target) + UUID firstModifUuid = mergedModificationsUuids.getFirst(); + ModificationInfos firstModification = getModificationInfo(firstModifUuid); + ModificationEntity firstModificationEntity = getModificationEntity(firstModification.getUuid()); + int targetIndex = firstModificationEntity.getModificationsOrder(); + ModificationGroupEntity targetGroup = firstModificationEntity.getGroup(); + CompositeModificationEntity targetComposite = null; + if (targetGroup == null) { + // the first modification is inside a composite + UUID targetCompositeUuid = modificationRepository.findCompositeIdByContainedModificationId(firstModifUuid); + targetComposite = compositeModificationRepository.getReferenceById(targetCompositeUuid); + } + + // get all the modifications to be merged, remove previous assignment + List mergedModifications = mergedModificationsUuids.stream() + .map(modificationRepository::getReferenceById) + .collect(Collectors.toList()); + // TODO :remove previous assignments (group/composites..) + mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); // TODO : group should be reordered ?? + + // create the composite + CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder().modificationsInfos(List.of()).build(); + CompositeModificationEntity newCompositeEntity = (CompositeModificationEntity) ModificationEntity.fromDTO(newCompositeInfos); + newCompositeEntity.setModificationsOrder(targetIndex); + + // assign modifications + newCompositeEntity.setModifications(mergedModifications); + if ( targetGroup != null) { + // TODO : réordonancement pour être au même point que la fusionnée num 1 + newCompositeEntity.setGroup(targetGroup); + } + if (targetComposite != null) { + List modifications = targetComposite.getModifications(); + modifications.add(newCompositeEntity); // TODO : réordonancement pour être au même point que la fusionnée num 1 + targetComposite.setModifications(modifications); + } + + return modificationRepository.save(newCompositeEntity); + } + + @Transactional // TODO use modificationsOrder like modifications in a group : remove the OrderColumn annotation in CompositeModificationEntity public void moveSubModification(@NonNull UUID groupUuid, UUID sourceCompositeUuid, UUID targetCompositeUuid, diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java index 2097c458b..cca16166f 100644 --- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java @@ -479,18 +479,15 @@ public CompletableFuture insertCompositeModification public CompletableFuture mergeNetworkModificationsIntoNewComposite( UUID targetGroupUuid, @NonNull Pair, List> modificationApplicationContexts) { - // TODO : get the target (groupUuid or composite Uuid) + List mergedModificationsUuids = modificationApplicationContexts.getFirst(); - // TODO : create the composite - - CompositeModificationInfos composite = new CompositeModificationInfos(); - - // TODO : assign modifications (remove previous assignment) + CompositeModificationInfos newComposite = + networkModificationRepository.mergeNetworkModificationsIntoNewComposite(mergedModificationsUuids).toModificationInfos(); // apply the composite (and implicitely those contained) : - return applyModifications(targetGroupUuid, List.of(composite), modificationApplicationContexts.getSecond()) + return applyModifications(targetGroupUuid, List.of(newComposite), modificationApplicationContexts.getSecond()) .thenApply(results -> - new NetworkModificationsResult(List.of(composite.getUuid()), results) + new NetworkModificationsResult(List.of(newComposite.getUuid()), results) ); } From 5e7311fe44485d7fdb25556221222dfae1689cd0 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Wed, 13 May 2026 17:12:32 +0200 Subject: [PATCH 03/16] corrections post merge Signed-off-by: Mathieu DEHARBE --- .../server/repositories/ModificationRepository.java | 1 - .../server/repositories/NetworkModificationRepository.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java index 20b940f23..7c409fa77 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java @@ -76,7 +76,6 @@ SELECT CAST(sm.modification_id AS VARCHAR) @Query(value = "SELECT cast(id AS UUID) FROM composite_modification_sub_modifications WHERE modification_id = :uuid ORDER BY modifications_order", nativeQuery = true) UUID findCompositeIdByContainedModificationId(UUID uuid); - @Query(value = "SELECT cast(modification_id AS VARCHAR) FROM composite_modification_sub_modifications WHERE id IN (?1) ORDER BY modifications_order", nativeQuery = true) @Query(value = """ SELECT CAST(sm.modification_id AS VARCHAR) FROM composite_modification_sub_modifications sm diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 4a324ab34..32565fd01 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -939,7 +939,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( // assign modifications newCompositeEntity.setModifications(mergedModifications); - if ( targetGroup != null) { + if (targetGroup != null) { // TODO : réordonancement pour être au même point que la fusionnée num 1 newCompositeEntity.setGroup(targetGroup); } @@ -952,7 +952,6 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( return modificationRepository.save(newCompositeEntity); } - @Transactional public void moveSubModification(@NonNull UUID groupUuid, UUID sourceCompositeUuid, UUID targetCompositeUuid, @NonNull UUID modificationUuid, UUID beforeUuid) { From 72a768a676e694a4ea96c870bf78a39d9ba855db Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Fri, 15 May 2026 12:11:29 +0200 Subject: [PATCH 04/16] remove auto application + send UUID + adept to refacto Signed-off-by: Mathieu DEHARBE --- .../modification/server/CompositeController.java | 12 +++++++----- .../server/repositories/ModificationRepository.java | 11 ++++++++--- .../repositories/NetworkModificationRepository.java | 12 ++++++++---- .../server/service/NetworkModificationService.java | 8 ++------ 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/CompositeController.java b/src/main/java/org/gridsuite/modification/server/CompositeController.java index 903a5769d..f1969b621 100644 --- a/src/main/java/org/gridsuite/modification/server/CompositeController.java +++ b/src/main/java/org/gridsuite/modification/server/CompositeController.java @@ -81,13 +81,15 @@ public ResponseEntity moveSubModification( @PostMapping(value = "/groups/{groupUuid}/composite-modification", consumes = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Merge some network modifications into a new composite modification") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) - public CompletableFuture> mergeNetworkModificationsIntoNewComposite( + public ResponseEntity mergeNetworkModificationsIntoNewComposite( @Parameter(description = "group UUID where the modifications are located") @PathVariable("groupUuid") UUID targetGroupUuid, @RequestBody Pair, List> modificationApplicationContexts) { - return networkModificationService.mergeNetworkModificationsIntoNewComposite( - targetGroupUuid, - modificationApplicationContexts - ).thenApply(ResponseEntity.ok()::body); + + return ResponseEntity.ok().body( + networkModificationService.mergeNetworkModificationsIntoNewComposite( + targetGroupUuid, + modificationApplicationContexts) + ); } @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java index 7c409fa77..3458f3346 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java @@ -71,9 +71,14 @@ SELECT CAST(sm.modification_id AS VARCHAR) """, nativeQuery = true) List findModificationIdsByCompositeModificationId(UUID uuid); - // return the uuid of the composite containing the modifcation sent as parameter - // TODO : à changer après fusion de ma fiche sur les ordre de modifications de réseau - @Query(value = "SELECT cast(id AS UUID) FROM composite_modification_sub_modifications WHERE modification_id = :uuid ORDER BY modifications_order", nativeQuery = true) + // return the uuid of the composite containing the modification sent as parameter + @Query(value = """ + SELECT CAST(sm.id AS VARCHAR) + FROM composite_modification_sub_modifications sm + INNER JOIN modification m ON sm.modification_id = m.id + WHERE sm.modification_id = :uuid + ORDER BY m.modifications_order + """, nativeQuery = true) UUID findCompositeIdByContainedModificationId(UUID uuid); @Query(value = """ diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 32565fd01..b0517ae48 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -933,19 +933,23 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); // TODO : group should be reordered ?? // create the composite - CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder().modificationsInfos(List.of()).build(); + CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder() + .modificationsInfos(List.of()) + .name("New composite modification") + .build(); CompositeModificationEntity newCompositeEntity = (CompositeModificationEntity) ModificationEntity.fromDTO(newCompositeInfos); newCompositeEntity.setModificationsOrder(targetIndex); // assign modifications newCompositeEntity.setModifications(mergedModifications); if (targetGroup != null) { - // TODO : réordonancement pour être au même point que la fusionnée num 1 - newCompositeEntity.setGroup(targetGroup); + List modifications = targetGroup.getModifications(); + modifications.add(targetIndex, newCompositeEntity); + targetGroup.setModifications(modifications); } if (targetComposite != null) { List modifications = targetComposite.getModifications(); - modifications.add(newCompositeEntity); // TODO : réordonancement pour être au même point que la fusionnée num 1 + modifications.add(targetIndex, newCompositeEntity); targetComposite.setModifications(modifications); } diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java index fb1fb2c8f..b9dcc8252 100644 --- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java @@ -476,7 +476,7 @@ public CompletableFuture insertCompositeModification } @Transactional - public CompletableFuture mergeNetworkModificationsIntoNewComposite( + public UUID mergeNetworkModificationsIntoNewComposite( UUID targetGroupUuid, @NonNull Pair, List> modificationApplicationContexts) { List mergedModificationsUuids = modificationApplicationContexts.getFirst(); @@ -484,11 +484,7 @@ public CompletableFuture mergeNetworkModificationsIn CompositeModificationInfos newComposite = networkModificationRepository.mergeNetworkModificationsIntoNewComposite(mergedModificationsUuids).toModificationInfos(); - // apply the composite (and implicitely those contained) : - return applyModifications(targetGroupUuid, List.of(newComposite), modificationApplicationContexts.getSecond()) - .thenApply(results -> - new NetworkModificationsResult(List.of(newComposite.getUuid()), results) - ); + return newComposite.getUuid(); } @Transactional From 49a522aca1d12373eeb127586b7f8997395b3d19 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Fri, 15 May 2026 14:41:27 +0200 Subject: [PATCH 05/16] frees merges modifications from its groups Signed-off-by: Mathieu DEHARBE --- .../NetworkModificationRepository.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index b0517ae48..85fd0f3a8 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -914,8 +914,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( List mergedModificationsUuids) { // get the target (groupUuid or composite Uuid of the first merged modification + its index in this target) UUID firstModifUuid = mergedModificationsUuids.getFirst(); - ModificationInfos firstModification = getModificationInfo(firstModifUuid); - ModificationEntity firstModificationEntity = getModificationEntity(firstModification.getUuid()); + ModificationEntity firstModificationEntity = getModificationEntity(firstModifUuid); int targetIndex = firstModificationEntity.getModificationsOrder(); ModificationGroupEntity targetGroup = firstModificationEntity.getGroup(); CompositeModificationEntity targetComposite = null; @@ -927,10 +926,21 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( // get all the modifications to be merged, remove previous assignment List mergedModifications = mergedModificationsUuids.stream() - .map(modificationRepository::getReferenceById) - .collect(Collectors.toList()); - // TODO :remove previous assignments (group/composites..) - mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); // TODO : group should be reordered ?? + .map(modificationRepository::findById).filter(Optional::isPresent).map(Optional::get).toList(); + // remove previous assignments of the merged modifications + // 1. cleans and reorders the origin group if there is one : + ModificationGroupEntity originGroup = mergedModifications.stream() + .map(ModificationEntity::getGroup) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + if (originGroup != null) { + List originGroupModifications = originGroup.getModifications(); + originGroupModifications.removeIf(mod -> mergedModificationsUuids.contains(mod.getId())); + originGroup.setModifications(originGroupModifications); + mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); + } + // TODO : 2. from composites // create the composite CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder() @@ -946,8 +956,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( List modifications = targetGroup.getModifications(); modifications.add(targetIndex, newCompositeEntity); targetGroup.setModifications(modifications); - } - if (targetComposite != null) { + } else { List modifications = targetComposite.getModifications(); modifications.add(targetIndex, newCompositeEntity); targetComposite.setModifications(modifications); From 028a31c4e970f79b2377bf825b794b75f09502c9 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Fri, 15 May 2026 16:22:05 +0200 Subject: [PATCH 06/16] cleans the composites whose submodifications are merged into a new one Signed-off-by: Mathieu DEHARBE --- .../NetworkModificationRepository.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 85fd0f3a8..0a37f2c0c 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -921,7 +921,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( if (targetGroup == null) { // the first modification is inside a composite UUID targetCompositeUuid = modificationRepository.findCompositeIdByContainedModificationId(firstModifUuid); - targetComposite = compositeModificationRepository.getReferenceById(targetCompositeUuid); + targetComposite = compositeModificationRepository.findById(targetCompositeUuid).orElse(null); } // get all the modifications to be merged, remove previous assignment @@ -940,9 +940,22 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( originGroup.setModifications(originGroupModifications); mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); } - // TODO : 2. from composites + // 2. cleans the composites whose submodifications are merged into a new one + for (ModificationEntity mergedModification : mergedModifications.stream().filter(mod -> mod.getGroup() == null).toList()) { + UUID compositeUuid = modificationRepository.findCompositeIdByContainedModificationId(mergedModification.getId()); + if (compositeUuid != null) { + CompositeModificationEntity previousOwner = compositeModificationRepository.findById(compositeUuid).orElse(null); + if (previousOwner != null) { + List modificationsLeft = previousOwner.getModifications() + .stream() + .filter(mod -> !mergedModificationsUuids.contains(mod.getId())) + .toList(); + previousOwner.setModifications(modificationsLeft); + } + } + } - // create the composite + // create the new composite CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder() .modificationsInfos(List.of()) .name("New composite modification") @@ -952,14 +965,14 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( // assign modifications newCompositeEntity.setModifications(mergedModifications); + // put the new composite in the target group or composite if (targetGroup != null) { List modifications = targetGroup.getModifications(); modifications.add(targetIndex, newCompositeEntity); targetGroup.setModifications(modifications); - } else { + } else if (targetComposite != null) { List modifications = targetComposite.getModifications(); modifications.add(targetIndex, newCompositeEntity); - targetComposite.setModifications(modifications); } return modificationRepository.save(newCompositeEntity); From db56d65c9469107d6f1153e51abe4e9111a4f133 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Fri, 15 May 2026 17:50:12 +0200 Subject: [PATCH 07/16] simplify endpoint Signed-off-by: Mathieu DEHARBE --- .../modification/server/CompositeController.java | 8 +++----- .../server/service/NetworkModificationService.java | 5 +---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/CompositeController.java b/src/main/java/org/gridsuite/modification/server/CompositeController.java index f1969b621..a500637bb 100644 --- a/src/main/java/org/gridsuite/modification/server/CompositeController.java +++ b/src/main/java/org/gridsuite/modification/server/CompositeController.java @@ -78,17 +78,15 @@ public ResponseEntity moveSubModification( return ResponseEntity.ok().build(); } - @PostMapping(value = "/groups/{groupUuid}/composite-modification", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/composite-modification", consumes = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Merge some network modifications into a new composite modification") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) public ResponseEntity mergeNetworkModificationsIntoNewComposite( - @Parameter(description = "group UUID where the modifications are located") @PathVariable("groupUuid") UUID targetGroupUuid, - @RequestBody Pair, List> modificationApplicationContexts) { + @RequestBody List mergedModificationsUuids) { return ResponseEntity.ok().body( networkModificationService.mergeNetworkModificationsIntoNewComposite( - targetGroupUuid, - modificationApplicationContexts) + mergedModificationsUuids) ); } diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java index b9dcc8252..9e1573791 100644 --- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java @@ -477,10 +477,7 @@ public CompletableFuture insertCompositeModification @Transactional public UUID mergeNetworkModificationsIntoNewComposite( - UUID targetGroupUuid, - @NonNull Pair, List> modificationApplicationContexts) { - List mergedModificationsUuids = modificationApplicationContexts.getFirst(); - + @NonNull List mergedModificationsUuids) { CompositeModificationInfos newComposite = networkModificationRepository.mergeNetworkModificationsIntoNewComposite(mergedModificationsUuids).toModificationInfos(); From 63f60b8794e2fed0897658dc84119107373923e6 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 18 May 2026 16:48:16 +0200 Subject: [PATCH 08/16] testMergeNetworkModificationsIntoNewComposite Signed-off-by: Mathieu DEHARBE --- .../server/CompositeControllerTest.java | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index 64c693773..81e8a0eff 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -38,11 +38,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import static org.gridsuite.modification.ModificationType.COMPOSITE_MODIFICATION; import static org.gridsuite.modification.server.utils.NetworkCreation.VARIANT_ID; @@ -276,7 +272,9 @@ void testDuplicateCompositeModification() throws Exception { Map returnedMap = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); assertEquals(1, returnedMap.size()); - Map.Entry returnedIds = returnedMap.entrySet().stream().findFirst().get(); + Optional> first = returnedMap.entrySet().stream().findFirst(); + assertTrue(first.isPresent()); + Map.Entry returnedIds = first.get(); UUID returnedSourceId = returnedIds.getKey(); UUID returnedNewId = returnedIds.getValue(); assertNotEquals(returnedSourceId, returnedNewId); @@ -484,6 +482,60 @@ void testMoveSubModificationFromCompositeToRoot() throws Exception { assertNull(remainingEntity.getGroup()); } + @Test + void testMergeNetworkModificationsIntoNewComposite() throws Exception { + // Create 3 root-level modifications in the group + List rootMods = createSomeSwitchModifications(TEST_GROUP_ID, 3); + List rootModUuids = rootMods.stream().map(ModificationInfos::getUuid).toList(); + + assertEquals(3, modificationRepository.getModifications(TEST_GROUP_ID, true, true).size()); + + // Merge the first 2 root-level modifications into a new composite + List mergedModificationUuids = rootModUuids.subList(0, 2); + MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/composite-modification") + .content(mapper.writeValueAsString(mergedModificationUuids)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + UUID compositeUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); + assertNotNull(compositeUuid); + + // The root group should now contain the new composite and the remaining non-merged modification + List rootModificationsAfterMerge = modificationRepository.getModifications(TEST_GROUP_ID, true, true); + assertEquals(2, rootModificationsAfterMerge.size()); + assertEquals(compositeUuid, rootModificationsAfterMerge.getFirst().getUuid()); + assertEquals(COMPOSITE_MODIFICATION, rootModificationsAfterMerge.getFirst().getType()); + assertEquals(rootModUuids.get(2), rootModificationsAfterMerge.get(1).getUuid()); + + // The new composite should contain the merged modifications in the same order + Map> compositeContentMap = mapper.readValue( + mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", compositeUuid)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(), + new TypeReference<>() { }); + List compositeContent = compositeContentMap.get(compositeUuid); + + assertEquals(2, compositeContent.size()); + assertEquals(rootModUuids.get(0), compositeContent.get(0).getUuid()); + assertEquals(rootModUuids.get(1), compositeContent.get(1).getUuid()); + + // The new composite must belong to TEST_GROUP_ID at root level + ModificationEntity compositeEntity = modificationRepository.getModificationEntity(compositeUuid); + assertNotNull(compositeEntity.getGroup()); + assertEquals(TEST_GROUP_ID, compositeEntity.getGroup().getId()); + + // The merged modifications must no longer belong directly to the group + ModificationEntity firstMergedEntity = modificationRepository.getModificationEntity(rootModUuids.get(0)); + ModificationEntity secondMergedEntity = modificationRepository.getModificationEntity(rootModUuids.get(1)); + assertNull(firstMergedEntity.getGroup()); + assertNull(secondMergedEntity.getGroup()); + + // The non-merged modification must still belong to TEST_GROUP_ID + ModificationEntity remainingEntity = modificationRepository.getModificationEntity(rootModUuids.get(2)); + assertNotNull(remainingEntity.getGroup()); + assertEquals(TEST_GROUP_ID, remainingEntity.getGroup().getId()); + } + + @Test void testExpandToLeafUuidsNestedComposites() throws Exception { // Build nested structure: outerComposite → [innerComposite → [leaf1, leaf2], leaf3] From 261f807c4ad4a4a3c8182c38b93bda644abc0040 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 18 May 2026 17:03:55 +0200 Subject: [PATCH 09/16] remove useless line Signed-off-by: Mathieu DEHARBE --- .../gridsuite/modification/server/CompositeControllerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index 81e8a0eff..55104f720 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -535,7 +535,6 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { assertEquals(TEST_GROUP_ID, remainingEntity.getGroup().getId()); } - @Test void testExpandToLeafUuidsNestedComposites() throws Exception { // Build nested structure: outerComposite → [innerComposite → [leaf1, leaf2], leaf3] From b9b7e057679bd10de1895eea7fa496ee2ca3460e Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 18 May 2026 17:59:26 +0200 Subject: [PATCH 10/16] test handles more cases Signed-off-by: Mathieu DEHARBE --- .../server/CompositeControllerTest.java | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index 55104f720..a97f6726f 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -20,7 +20,9 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.server.dto.NetworkModificationResult; import org.gridsuite.modification.server.dto.NetworkModificationsResult; +import org.gridsuite.modification.server.entities.CompositeModificationEntity; import org.gridsuite.modification.server.entities.ModificationEntity; +import org.gridsuite.modification.server.repositories.CompositeModificationRepository; import org.gridsuite.modification.server.repositories.NetworkModificationRepository; import org.gridsuite.modification.server.service.ReportService; import org.gridsuite.modification.server.utils.NetworkCreation; @@ -72,6 +74,9 @@ class CompositeControllerTest { @Autowired private NetworkModificationRepository modificationRepository; + @Autowired + private CompositeModificationRepository compositeRepository; + @MockitoBean private ReportService reportService; @@ -490,38 +495,38 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { assertEquals(3, modificationRepository.getModifications(TEST_GROUP_ID, true, true).size()); - // Merge the first 2 root-level modifications into a new composite + // ---- 1. Merge the first 2 root-level modifications into a new composite List mergedModificationUuids = rootModUuids.subList(0, 2); MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/composite-modification") .content(mapper.writeValueAsString(mergedModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); - UUID compositeUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); - assertNotNull(compositeUuid); + UUID firstCompositeUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); + assertNotNull(firstCompositeUuid); // The root group should now contain the new composite and the remaining non-merged modification List rootModificationsAfterMerge = modificationRepository.getModifications(TEST_GROUP_ID, true, true); assertEquals(2, rootModificationsAfterMerge.size()); - assertEquals(compositeUuid, rootModificationsAfterMerge.getFirst().getUuid()); + assertEquals(firstCompositeUuid, rootModificationsAfterMerge.getFirst().getUuid()); assertEquals(COMPOSITE_MODIFICATION, rootModificationsAfterMerge.getFirst().getType()); assertEquals(rootModUuids.get(2), rootModificationsAfterMerge.get(1).getUuid()); // The new composite should contain the merged modifications in the same order Map> compositeContentMap = mapper.readValue( - mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", compositeUuid)) + mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", firstCompositeUuid)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(), new TypeReference<>() { }); - List compositeContent = compositeContentMap.get(compositeUuid); + List compositeContent = compositeContentMap.get(firstCompositeUuid); assertEquals(2, compositeContent.size()); assertEquals(rootModUuids.get(0), compositeContent.get(0).getUuid()); assertEquals(rootModUuids.get(1), compositeContent.get(1).getUuid()); // The new composite must belong to TEST_GROUP_ID at root level - ModificationEntity compositeEntity = modificationRepository.getModificationEntity(compositeUuid); - assertNotNull(compositeEntity.getGroup()); - assertEquals(TEST_GROUP_ID, compositeEntity.getGroup().getId()); + CompositeModificationEntity firstComposite = compositeRepository.findById(firstCompositeUuid).orElseThrow(); + assertNotNull(firstComposite.getGroup()); + assertEquals(TEST_GROUP_ID, firstComposite.getGroup().getId()); // The merged modifications must no longer belong directly to the group ModificationEntity firstMergedEntity = modificationRepository.getModificationEntity(rootModUuids.get(0)); @@ -533,6 +538,43 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { ModificationEntity remainingEntity = modificationRepository.getModificationEntity(rootModUuids.get(2)); assertNotNull(remainingEntity.getGroup()); assertEquals(TEST_GROUP_ID, remainingEntity.getGroup().getId()); + + // ---- 2. now merges a modification which is inside a composite with something that is outside : + mergedModificationUuids = List.of(compositeContent.get(0).getUuid(), rootModUuids.get(2)); + mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/composite-modification") + .content(mapper.writeValueAsString(mergedModificationUuids)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + // this new composite will be generated inside the other composite because its first element was inside it + UUID twodepthCompositeUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); + assertNotNull(twodepthCompositeUuid); + + // The root group should now contain the new composite and nothing else + rootModificationsAfterMerge = modificationRepository.getModifications(TEST_GROUP_ID, true, true); + assertEquals(1, rootModificationsAfterMerge.size()); + + // The first composite should contain the new composite, then the other untouched modification + compositeContentMap = mapper.readValue( + mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", firstCompositeUuid)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(), + new TypeReference<>() { }); + compositeContent = compositeContentMap.get(firstCompositeUuid); + + assertEquals(2, compositeContent.size()); + assertEquals(rootModUuids.get(1), compositeContent.get(0).getUuid()); + assertEquals(twodepthCompositeUuid, compositeContent.get(1).getUuid()); + + // The new 2 depth composite must now belong to the first composite, not to a group + CompositeModificationEntity twoDepthComposite = compositeRepository.findById(twodepthCompositeUuid).orElseThrow(); + assertNull(twoDepthComposite.getGroup()); + compositeContentMap = mapper.readValue( + mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", firstCompositeUuid)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(), + new TypeReference<>() { }); + assertTrue(compositeContentMap.get(firstCompositeUuid).stream() + .map(ModificationInfos::getUuid) + .anyMatch(twodepthCompositeUuid::equals)); } @Test From 97b0370efcd865f7f404e79f022f5d3b2882dc8f Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Tue, 19 May 2026 18:34:44 +0200 Subject: [PATCH 11/16] simplify endpoint Signed-off-by: Mathieu DEHARBE --- .../org/gridsuite/modification/server/CompositeController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/modification/server/CompositeController.java b/src/main/java/org/gridsuite/modification/server/CompositeController.java index a500637bb..71930f5df 100644 --- a/src/main/java/org/gridsuite/modification/server/CompositeController.java +++ b/src/main/java/org/gridsuite/modification/server/CompositeController.java @@ -78,7 +78,7 @@ public ResponseEntity moveSubModification( return ResponseEntity.ok().build(); } - @PostMapping(value = "/composite-modification", consumes = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Merge some network modifications into a new composite modification") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) public ResponseEntity mergeNetworkModificationsIntoNewComposite( From 59a90051e6ec0df77e38c150e1dc5d10fce7f030 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Tue, 26 May 2026 17:09:00 +0200 Subject: [PATCH 12/16] rename Signed-off-by: Mathieu DEHARBE --- .../server/repositories/NetworkModificationRepository.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 0a37f2c0c..c90c4c12c 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -910,8 +910,7 @@ public List insertCompositeModifications( } @Transactional - public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( - List mergedModificationsUuids) { + public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite(List mergedModificationsUuids) { // get the target (groupUuid or composite Uuid of the first merged modification + its index in this target) UUID firstModifUuid = mergedModificationsUuids.getFirst(); ModificationEntity firstModificationEntity = getModificationEntity(firstModifUuid); @@ -927,7 +926,6 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( // get all the modifications to be merged, remove previous assignment List mergedModifications = mergedModificationsUuids.stream() .map(modificationRepository::findById).filter(Optional::isPresent).map(Optional::get).toList(); - // remove previous assignments of the merged modifications // 1. cleans and reorders the origin group if there is one : ModificationGroupEntity originGroup = mergedModifications.stream() .map(ModificationEntity::getGroup) @@ -958,7 +956,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite( // create the new composite CompositeModificationInfos newCompositeInfos = CompositeModificationInfos.builder() .modificationsInfos(List.of()) - .name("New composite modification") + .name("Composite modification") .build(); CompositeModificationEntity newCompositeEntity = (CompositeModificationEntity) ModificationEntity.fromDTO(newCompositeInfos); newCompositeEntity.setModificationsOrder(targetIndex); From 4f216da03e6b712212ed55afb7f866f77f4ca855 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Tue, 26 May 2026 17:53:02 +0200 Subject: [PATCH 13/16] correct TU endpoint Signed-off-by: Mathieu DEHARBE --- .../modification/server/CompositeControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index a97f6726f..6c2a0bf95 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -497,7 +497,7 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { // ---- 1. Merge the first 2 root-level modifications into a new composite List mergedModificationUuids = rootModUuids.subList(0, 2); - MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/composite-modification") + MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") .content(mapper.writeValueAsString(mergedModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); @@ -541,7 +541,7 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { // ---- 2. now merges a modification which is inside a composite with something that is outside : mergedModificationUuids = List.of(compositeContent.get(0).getUuid(), rootModUuids.get(2)); - mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/composite-modification") + mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") .content(mapper.writeValueAsString(mergedModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); From 3771f0bca3d3a5f37ab16c0f2689440b952b0b44 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Thu, 28 May 2026 15:26:40 +0200 Subject: [PATCH 14/16] rename merge to assemble Signed-off-by: Mathieu DEHARBE --- .../server/CompositeController.java | 10 ++--- .../NetworkModificationRepository.java | 26 +++++------ .../service/NetworkModificationService.java | 5 +-- .../server/CompositeControllerTest.java | 44 +++++++++---------- 4 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/CompositeController.java b/src/main/java/org/gridsuite/modification/server/CompositeController.java index 71930f5df..8405071c4 100644 --- a/src/main/java/org/gridsuite/modification/server/CompositeController.java +++ b/src/main/java/org/gridsuite/modification/server/CompositeController.java @@ -79,14 +79,12 @@ public ResponseEntity moveSubModification( } @PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Merge some network modifications into a new composite modification") + @Operation(summary = "Assemble some network modifications into a new composite modification") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")}) - public ResponseEntity mergeNetworkModificationsIntoNewComposite( - @RequestBody List mergedModificationsUuids) { - + public ResponseEntity assembleNetworkModificationsIntoNewComposite( + @RequestBody List assembledModificationsUuids) { return ResponseEntity.ok().body( - networkModificationService.mergeNetworkModificationsIntoNewComposite( - mergedModificationsUuids) + networkModificationService.assembleNetworkModificationsIntoNewComposite(assembledModificationsUuids) ); } diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index c90c4c12c..4c49e4ec9 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -910,9 +910,9 @@ public List insertCompositeModifications( } @Transactional - public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite(List mergedModificationsUuids) { - // get the target (groupUuid or composite Uuid of the first merged modification + its index in this target) - UUID firstModifUuid = mergedModificationsUuids.getFirst(); + public CompositeModificationEntity assembleNetworkModificationsIntoNewComposite(List assembledModificationsUuids) { + // get the target (groupUuid or composite Uuid of the first assembled modification + its index in this target) + UUID firstModifUuid = assembledModificationsUuids.getFirst(); ModificationEntity firstModificationEntity = getModificationEntity(firstModifUuid); int targetIndex = firstModificationEntity.getModificationsOrder(); ModificationGroupEntity targetGroup = firstModificationEntity.getGroup(); @@ -923,30 +923,30 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite(Lis targetComposite = compositeModificationRepository.findById(targetCompositeUuid).orElse(null); } - // get all the modifications to be merged, remove previous assignment - List mergedModifications = mergedModificationsUuids.stream() + // get all the modifications to be assembled, remove previous assignment + List assembledModifications = assembledModificationsUuids.stream() .map(modificationRepository::findById).filter(Optional::isPresent).map(Optional::get).toList(); // 1. cleans and reorders the origin group if there is one : - ModificationGroupEntity originGroup = mergedModifications.stream() + ModificationGroupEntity originGroup = assembledModifications.stream() .map(ModificationEntity::getGroup) .filter(Objects::nonNull) .findFirst() .orElse(null); if (originGroup != null) { List originGroupModifications = originGroup.getModifications(); - originGroupModifications.removeIf(mod -> mergedModificationsUuids.contains(mod.getId())); + originGroupModifications.removeIf(mod -> assembledModificationsUuids.contains(mod.getId())); originGroup.setModifications(originGroupModifications); - mergedModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); + assembledModifications.forEach(modificationEntity -> modificationEntity.setGroup(null)); } - // 2. cleans the composites whose submodifications are merged into a new one - for (ModificationEntity mergedModification : mergedModifications.stream().filter(mod -> mod.getGroup() == null).toList()) { - UUID compositeUuid = modificationRepository.findCompositeIdByContainedModificationId(mergedModification.getId()); + // 2. cleans the composites whose submodifications are assembled into a new one + for (ModificationEntity assembledModification : assembledModifications.stream().filter(mod -> mod.getGroup() == null).toList()) { + UUID compositeUuid = modificationRepository.findCompositeIdByContainedModificationId(assembledModification.getId()); if (compositeUuid != null) { CompositeModificationEntity previousOwner = compositeModificationRepository.findById(compositeUuid).orElse(null); if (previousOwner != null) { List modificationsLeft = previousOwner.getModifications() .stream() - .filter(mod -> !mergedModificationsUuids.contains(mod.getId())) + .filter(mod -> !assembledModificationsUuids.contains(mod.getId())) .toList(); previousOwner.setModifications(modificationsLeft); } @@ -962,7 +962,7 @@ public CompositeModificationEntity mergeNetworkModificationsIntoNewComposite(Lis newCompositeEntity.setModificationsOrder(targetIndex); // assign modifications - newCompositeEntity.setModifications(mergedModifications); + newCompositeEntity.setModifications(assembledModifications); // put the new composite in the target group or composite if (targetGroup != null) { List modifications = targetGroup.getModifications(); diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java index 9e1573791..8cd8b4f44 100644 --- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java @@ -476,10 +476,9 @@ public CompletableFuture insertCompositeModification } @Transactional - public UUID mergeNetworkModificationsIntoNewComposite( - @NonNull List mergedModificationsUuids) { + public UUID assembleNetworkModificationsIntoNewComposite(@NonNull List assembledModificationsUuids) { CompositeModificationInfos newComposite = - networkModificationRepository.mergeNetworkModificationsIntoNewComposite(mergedModificationsUuids).toModificationInfos(); + networkModificationRepository.assembleNetworkModificationsIntoNewComposite(assembledModificationsUuids).toModificationInfos(); return newComposite.getUuid(); } diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index 6c2a0bf95..1724fb53c 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -488,31 +488,31 @@ void testMoveSubModificationFromCompositeToRoot() throws Exception { } @Test - void testMergeNetworkModificationsIntoNewComposite() throws Exception { + void testAssembleNetworkModificationsIntoNewComposite() throws Exception { // Create 3 root-level modifications in the group List rootMods = createSomeSwitchModifications(TEST_GROUP_ID, 3); List rootModUuids = rootMods.stream().map(ModificationInfos::getUuid).toList(); assertEquals(3, modificationRepository.getModifications(TEST_GROUP_ID, true, true).size()); - // ---- 1. Merge the first 2 root-level modifications into a new composite - List mergedModificationUuids = rootModUuids.subList(0, 2); + // ---- 1. Assemble the first 2 root-level modifications into a new composite + List assembledModificationUuids = rootModUuids.subList(0, 2); MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") - .content(mapper.writeValueAsString(mergedModificationUuids)) + .content(mapper.writeValueAsString(assembledModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); UUID firstCompositeUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() { }); assertNotNull(firstCompositeUuid); - // The root group should now contain the new composite and the remaining non-merged modification - List rootModificationsAfterMerge = modificationRepository.getModifications(TEST_GROUP_ID, true, true); - assertEquals(2, rootModificationsAfterMerge.size()); - assertEquals(firstCompositeUuid, rootModificationsAfterMerge.getFirst().getUuid()); - assertEquals(COMPOSITE_MODIFICATION, rootModificationsAfterMerge.getFirst().getType()); - assertEquals(rootModUuids.get(2), rootModificationsAfterMerge.get(1).getUuid()); + // The root group should now contain the new composite and the remaining non-assembled modification + List rootModificationsAfterAssemble = modificationRepository.getModifications(TEST_GROUP_ID, true, true); + assertEquals(2, rootModificationsAfterAssemble.size()); + assertEquals(firstCompositeUuid, rootModificationsAfterAssemble.getFirst().getUuid()); + assertEquals(COMPOSITE_MODIFICATION, rootModificationsAfterAssemble.getFirst().getType()); + assertEquals(rootModUuids.get(2), rootModificationsAfterAssemble.get(1).getUuid()); - // The new composite should contain the merged modifications in the same order + // The new composite should contain the assembled modifications in the same order Map> compositeContentMap = mapper.readValue( mockMvc.perform(get(URI_GET_COMPOSITE_NETWORK_MODIF_CONTENT + "/network-modifications?uuids={id}", firstCompositeUuid)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(), @@ -528,21 +528,21 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { assertNotNull(firstComposite.getGroup()); assertEquals(TEST_GROUP_ID, firstComposite.getGroup().getId()); - // The merged modifications must no longer belong directly to the group - ModificationEntity firstMergedEntity = modificationRepository.getModificationEntity(rootModUuids.get(0)); - ModificationEntity secondMergedEntity = modificationRepository.getModificationEntity(rootModUuids.get(1)); - assertNull(firstMergedEntity.getGroup()); - assertNull(secondMergedEntity.getGroup()); + // The assembled modifications must no longer belong directly to the group + ModificationEntity firstAssembledEntity = modificationRepository.getModificationEntity(rootModUuids.get(0)); + ModificationEntity secondAssembledEntity = modificationRepository.getModificationEntity(rootModUuids.get(1)); + assertNull(firstAssembledEntity.getGroup()); + assertNull(secondAssembledEntity.getGroup()); - // The non-merged modification must still belong to TEST_GROUP_ID + // The non-assembled modification must still belong to TEST_GROUP_ID ModificationEntity remainingEntity = modificationRepository.getModificationEntity(rootModUuids.get(2)); assertNotNull(remainingEntity.getGroup()); assertEquals(TEST_GROUP_ID, remainingEntity.getGroup().getId()); - // ---- 2. now merges a modification which is inside a composite with something that is outside : - mergedModificationUuids = List.of(compositeContent.get(0).getUuid(), rootModUuids.get(2)); + // ---- 2. now assembles a modification which is inside a composite with something that is outside : + assembledModificationUuids = List.of(compositeContent.getFirst().getUuid(), rootModUuids.get(2)); mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") - .content(mapper.writeValueAsString(mergedModificationUuids)) + .content(mapper.writeValueAsString(assembledModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); @@ -551,8 +551,8 @@ void testMergeNetworkModificationsIntoNewComposite() throws Exception { assertNotNull(twodepthCompositeUuid); // The root group should now contain the new composite and nothing else - rootModificationsAfterMerge = modificationRepository.getModifications(TEST_GROUP_ID, true, true); - assertEquals(1, rootModificationsAfterMerge.size()); + rootModificationsAfterAssemble = modificationRepository.getModifications(TEST_GROUP_ID, true, true); + assertEquals(1, rootModificationsAfterAssemble.size()); // The first composite should contain the new composite, then the other untouched modification compositeContentMap = mapper.readValue( From daabea45d61e1082ca979cd1198d201709f77d9b Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Thu, 28 May 2026 17:02:55 +0200 Subject: [PATCH 15/16] better order management Signed-off-by: Mathieu DEHARBE --- .../entities/ModificationGroupEntity.java | 3 +++ .../NetworkModificationRepository.java | 13 +++++----- .../server/CompositeControllerTest.java | 26 +++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java b/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java index 48a0b04a6..80eabaee9 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java @@ -56,5 +56,8 @@ public void setModifications(List modifications) { modifications.forEach(modification -> modification.setGroup(this) ); + for (int i = 0; i < this.getModifications().size(); i++) { + this.getModifications().get(i).setModificationsOrder(i); + } } } diff --git a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java index 4c49e4ec9..20d44279c 100644 --- a/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java +++ b/src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java @@ -912,9 +912,9 @@ public List insertCompositeModifications( @Transactional public CompositeModificationEntity assembleNetworkModificationsIntoNewComposite(List assembledModificationsUuids) { // get the target (groupUuid or composite Uuid of the first assembled modification + its index in this target) - UUID firstModifUuid = assembledModificationsUuids.getFirst(); - ModificationEntity firstModificationEntity = getModificationEntity(firstModifUuid); - int targetIndex = firstModificationEntity.getModificationsOrder(); + final UUID firstModifUuid = assembledModificationsUuids.getFirst(); + final ModificationEntity firstModificationEntity = getModificationEntity(firstModifUuid); + final int targetIndex = firstModificationEntity.getModificationsOrder(); ModificationGroupEntity targetGroup = firstModificationEntity.getGroup(); CompositeModificationEntity targetComposite = null; if (targetGroup == null) { @@ -971,6 +971,9 @@ public CompositeModificationEntity assembleNetworkModificationsIntoNewComposite( } else if (targetComposite != null) { List modifications = targetComposite.getModifications(); modifications.add(targetIndex, newCompositeEntity); + for (int i = 0; i < targetComposite.getModifications().size(); i++) { + targetComposite.getModifications().get(i).setModificationsOrder(i); + } } return modificationRepository.save(newCompositeEntity); @@ -1026,10 +1029,6 @@ public void moveSubModification(@NonNull UUID groupUuid, UUID sourceCompositeUui group.setModifications(notMovedMods); movedMods.forEach(entity -> entity.setGroup(null)); } - // reordering the composite modifications left (not moved) - for (int i = 0; i < notMovedMods.size(); i++) { - notMovedMods.get(i).setModificationsOrder(i); - } if (targetCompositeUuid != null) { // Check if targeted composite isn't already inside modificationUuid diff --git a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java index 1724fb53c..1369c0d8e 100644 --- a/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java +++ b/src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java @@ -491,12 +491,12 @@ void testMoveSubModificationFromCompositeToRoot() throws Exception { void testAssembleNetworkModificationsIntoNewComposite() throws Exception { // Create 3 root-level modifications in the group List rootMods = createSomeSwitchModifications(TEST_GROUP_ID, 3); - List rootModUuids = rootMods.stream().map(ModificationInfos::getUuid).toList(); + final List originalRootModUuids = rootMods.stream().map(ModificationInfos::getUuid).toList(); assertEquals(3, modificationRepository.getModifications(TEST_GROUP_ID, true, true).size()); // ---- 1. Assemble the first 2 root-level modifications into a new composite - List assembledModificationUuids = rootModUuids.subList(0, 2); + List assembledModificationUuids = originalRootModUuids.subList(0, 2); MvcResult mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") .content(mapper.writeValueAsString(assembledModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) @@ -510,7 +510,7 @@ void testAssembleNetworkModificationsIntoNewComposite() throws Exception { assertEquals(2, rootModificationsAfterAssemble.size()); assertEquals(firstCompositeUuid, rootModificationsAfterAssemble.getFirst().getUuid()); assertEquals(COMPOSITE_MODIFICATION, rootModificationsAfterAssemble.getFirst().getType()); - assertEquals(rootModUuids.get(2), rootModificationsAfterAssemble.get(1).getUuid()); + assertEquals(originalRootModUuids.get(2), rootModificationsAfterAssemble.get(1).getUuid()); // The new composite should contain the assembled modifications in the same order Map> compositeContentMap = mapper.readValue( @@ -520,8 +520,8 @@ void testAssembleNetworkModificationsIntoNewComposite() throws Exception { List compositeContent = compositeContentMap.get(firstCompositeUuid); assertEquals(2, compositeContent.size()); - assertEquals(rootModUuids.get(0), compositeContent.get(0).getUuid()); - assertEquals(rootModUuids.get(1), compositeContent.get(1).getUuid()); + assertEquals(originalRootModUuids.get(0), compositeContent.get(0).getUuid()); + assertEquals(originalRootModUuids.get(1), compositeContent.get(1).getUuid()); // The new composite must belong to TEST_GROUP_ID at root level CompositeModificationEntity firstComposite = compositeRepository.findById(firstCompositeUuid).orElseThrow(); @@ -529,18 +529,18 @@ void testAssembleNetworkModificationsIntoNewComposite() throws Exception { assertEquals(TEST_GROUP_ID, firstComposite.getGroup().getId()); // The assembled modifications must no longer belong directly to the group - ModificationEntity firstAssembledEntity = modificationRepository.getModificationEntity(rootModUuids.get(0)); - ModificationEntity secondAssembledEntity = modificationRepository.getModificationEntity(rootModUuids.get(1)); + ModificationEntity firstAssembledEntity = modificationRepository.getModificationEntity(originalRootModUuids.get(0)); + ModificationEntity secondAssembledEntity = modificationRepository.getModificationEntity(originalRootModUuids.get(1)); assertNull(firstAssembledEntity.getGroup()); assertNull(secondAssembledEntity.getGroup()); // The non-assembled modification must still belong to TEST_GROUP_ID - ModificationEntity remainingEntity = modificationRepository.getModificationEntity(rootModUuids.get(2)); - assertNotNull(remainingEntity.getGroup()); - assertEquals(TEST_GROUP_ID, remainingEntity.getGroup().getId()); + ModificationEntity remainingInGroupEntity = modificationRepository.getModificationEntity(originalRootModUuids.get(2)); + assertNotNull(remainingInGroupEntity.getGroup()); + assertEquals(TEST_GROUP_ID, remainingInGroupEntity.getGroup().getId()); // ---- 2. now assembles a modification which is inside a composite with something that is outside : - assembledModificationUuids = List.of(compositeContent.getFirst().getUuid(), rootModUuids.get(2)); + assembledModificationUuids = List.of(compositeContent.getFirst().getUuid(), remainingInGroupEntity.getId()); mvcResult = mockMvc.perform(post(URI_COMPOSITE_NETWORK_MODIF_BASE + "/") .content(mapper.writeValueAsString(assembledModificationUuids)) .contentType(MediaType.APPLICATION_JSON)) @@ -562,8 +562,8 @@ void testAssembleNetworkModificationsIntoNewComposite() throws Exception { compositeContent = compositeContentMap.get(firstCompositeUuid); assertEquals(2, compositeContent.size()); - assertEquals(rootModUuids.get(1), compositeContent.get(0).getUuid()); - assertEquals(twodepthCompositeUuid, compositeContent.get(1).getUuid()); + assertEquals(twodepthCompositeUuid, compositeContent.get(0).getUuid()); + assertEquals(originalRootModUuids.get(1), compositeContent.get(1).getUuid()); // The new 2 depth composite must now belong to the first composite, not to a group CompositeModificationEntity twoDepthComposite = compositeRepository.findById(twodepthCompositeUuid).orElseThrow(); From 9dc574a4d411eca942fecae5a78e970c27a0a3dc Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 1 Jun 2026 12:28:03 +0200 Subject: [PATCH 16/16] better order management Signed-off-by: Mathieu DEHARBE --- .../modification/server/entities/ModificationGroupEntity.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java b/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java index 80eabaee9..48a0b04a6 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java @@ -56,8 +56,5 @@ public void setModifications(List modifications) { modifications.forEach(modification -> modification.setGroup(this) ); - for (int i = 0; i < this.getModifications().size(); i++) { - this.getModifications().get(i).setModificationsOrder(i); - } } }