From 710a663fcb4317ddd64b24d3667387dfd6462643 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 08:46:40 +0200 Subject: [PATCH 01/21] Unify move endpoints --- .../server/controller/StudyController.java | 43 ++++----- .../service/NetworkModificationService.java | 21 +++-- .../NetworkModificationTreeService.java | 6 ++ .../server/service/RebuildNodeService.java | 87 ++++++------------- .../study/server/service/StudyService.java | 64 ++++++++------ .../study/server/RebuildNodeServiceTest.java | 10 +-- .../StudyControllerRebuildNodeTest.java | 6 +- 7 files changed, 108 insertions(+), 129 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java index c01516898..d991efe5e 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -15,7 +15,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.gridsuite.filter.globalfilter.GlobalFilter; import org.gridsuite.filter.utils.EquipmentType; @@ -626,36 +625,28 @@ public ResponseEntity getAllMapData( return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(studyService.getAllMapData(studyUuid, nodeUuid, rootNetworkUuid, substationsIds)); } - @PutMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}") - @Operation(summary = "move network modification before another") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The modification order is updated")}) - public ResponseEntity moveModification(@PathVariable("studyUuid") UUID studyUuid, - @PathVariable("nodeUuid") UUID nodeUuid, - @PathVariable("modificationUuid") UUID modificationUuid, - @Nullable @Parameter(description = "move before, if no value move to end") @RequestParam(value = "beforeUuid") UUID beforeUuid, - @RequestHeader(HEADER_USER_ID) String userId) { - studyService.assertCanUpdateNodeInStudy(studyUuid, nodeUuid); - studyService.assertNoBlockedNodeInStudy(studyUuid, nodeUuid); - rebuildNodeService.moveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId); - return ResponseEntity.ok().build(); - } - - @PutMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/composite-sub-modification/{modificationUuid}") - @Operation(summary = "Move a composite sub-modification within/between composites or to/from root level") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The sub-modification order has been updated")}) - public ResponseEntity moveSubModification( + @PutMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}") + @Operation(summary = "Move a modification within or between containers (groups or composites)") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The modification order has been updated")}) + public ResponseEntity moveModification( @PathVariable("studyUuid") UUID studyUuid, @PathVariable("nodeUuid") UUID nodeUuid, @PathVariable("modificationUuid") UUID modificationUuid, - @Nullable @Parameter(description = "Source composite UUID; absent when moving from root level") @RequestParam(value = "sourceCompositeUuid", required = false) UUID sourceCompositeUuid, - @Nullable @Parameter(description = "Target composite UUID; absent when moving to root level") @RequestParam(value = "targetCompositeUuid", required = false) UUID targetCompositeUuid, - @Nullable @Parameter(description = "Insert before this UUID; absent means append at end") @RequestParam(value = "beforeUuid", required = false) UUID beforeUuid, + @Parameter(description = "source container UUID; for GROUP source the node's group is used when absent") @RequestParam(value = "sourceContainerId", required = false) UUID sourceContainerId, + @Parameter(description = "target container UUID; for GROUP target the node's group is used when absent") @RequestParam(value = "targetContainerId", required = false) UUID targetContainerId, + @Parameter(description = "insert before this modification (empty = at end)") @RequestParam(value = "beforeUuid", required = false) UUID beforeUuid, @RequestHeader(HEADER_USER_ID) String userId) { studyService.assertCanUpdateNodeInStudy(studyUuid, nodeUuid); studyService.assertNoBlockedNodeInStudy(studyUuid, nodeUuid); - rebuildNodeService.moveSubModification( - studyUuid, nodeUuid, - sourceCompositeUuid, targetCompositeUuid, + // For GROUP containers without an explicit id, fall back to the node's group. + UUID resolvedSourceId = sourceContainerId == null + ? networkModificationTreeService.getModificationGroupUuid(nodeUuid) : sourceContainerId; + UUID resolvedTargetId = targetContainerId == null + ? networkModificationTreeService.getModificationGroupUuid(nodeUuid) : targetContainerId; + rebuildNodeService.moveNetworkModification( + studyUuid, + resolvedTargetId, + resolvedSourceId, modificationUuid, beforeUuid, userId); return ResponseEntity.ok().build(); } @@ -684,7 +675,7 @@ public ResponseEntity moveOrCopyModifications(@PathVariable("studyUuid") U } studyService.assertNoBlockedNodeInStudy(studyUuid, originNodeUuid); studyService.assertNoBlockedNodeInStudy(studyUuid, nodeUuid); - rebuildNodeService.moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationsToCopyUuidList, userId); + //rebuildNodeService.moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationsToCopyUuidList, userId); break; } return ResponseEntity.ok().build(); diff --git a/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java b/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java index ddb37da6f..6649aba34 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java @@ -257,24 +257,27 @@ public void stopBuild(@NonNull UUID nodeUuid, @NonNull UUID rootNetworkUuid) { restTemplate.put(getNetworkModificationServerURI(false) + path, null); } - public NetworkModificationsResult moveModifications(UUID originGroupUuid, UUID targetGroupUuid, UUID beforeUuid, Pair, List> modificationContextInfos, boolean buildTargetNode) { + public NetworkModificationsResult moveModifications( + UUID sourceContainerId, UUID targetContainerId, + UUID beforeUuid, + Pair, List> body, + boolean buildTargetNode) { + var path = UriComponentsBuilder.fromPath(GROUP_PATH) - .queryParam(QUERY_PARAM_ACTION, ModificationsActionType.MOVE.name()) - .queryParam("originGroupUuid", originGroupUuid) - .queryParam("build", buildTargetNode); + .queryParam(QUERY_PARAM_ACTION, ModificationsActionType.MOVE.name()) + .queryParam("originGroupUuid", sourceContainerId) + .queryParam("build", buildTargetNode); if (beforeUuid != null) { path.queryParam("before", beforeUuid); } HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity, List>> httpEntity = new HttpEntity<>(modificationContextInfos, headers); + HttpEntity, List>> httpEntity = new HttpEntity<>(body, headers); return restTemplate.exchange( - getNetworkModificationServerURI(false) + path.buildAndExpand(targetGroupUuid).toUriString(), - HttpMethod.PUT, - httpEntity, - NetworkModificationsResult.class).getBody(); + getNetworkModificationServerURI(false) + path.buildAndExpand(targetContainerId).toUriString(), + HttpMethod.PUT, httpEntity, NetworkModificationsResult.class).getBody(); } public NetworkModificationsResult duplicateModifications(UUID groupUuid, diff --git a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java index a862eee9a..3aa1da026 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java @@ -1294,4 +1294,10 @@ public ExportNetworkStatus getExportNetworkStatus(UUID exportUuid) { .map(ExportNetworkStatus::valueOf) .orElseThrow(() -> new StudyException(NOT_FOUND, "No export found for exportUuid=" + exportUuid)); } + + @Transactional(readOnly = true) + public UUID getNodeUuidByModificationGroup(UUID groupUuid) { + var node = networkModificationNodeInfoRepository.findByModificationGroupUuidIn(List.of(groupUuid)); + return node.isEmpty() ? null : node.getFirst().getIdNode(); + } } diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index 66f141fa1..e74b421c4 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -6,7 +6,6 @@ */ package org.gridsuite.study.server.service; -import lombok.NonNull; import org.gridsuite.study.server.dto.modification.NetworkModificationMetadata; import org.springframework.stereotype.Service; @@ -66,66 +65,34 @@ public void restoreNetworkModifications(UUID studyUuid, UUID nodeUuid, List studyService.restoreNetworkModifications(studyUuid, nodeUuid, modificationsUuids, userId)); } - public void moveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { - handleRebuildNode(studyUuid, nodeUuid, userId, - () -> handleMoveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId)); - } - - private void handleMoveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { - studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); - try { - studyService.moveNetworkModifications(studyUuid, nodeUuid, nodeUuid, List.of(modificationUuid), beforeUuid, false, userId); - } finally { - studyService.unblockNodeTree(studyUuid, nodeUuid); - } - } - - public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { - handleRebuildNode(studyUuid, targetNodeUuid, originNodeUuid, userId, - () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); - } - - public void moveSubModification( + public void moveNetworkModification( UUID studyUuid, - UUID nodeUuid, - UUID sourceCompositeUuid, - UUID targetCompositeUuid, - UUID modificationUuid, - UUID beforeUuid, - String userId) { - handleRebuildNode(studyUuid, nodeUuid, userId, - () -> handleMoveNetworkSubmodification( - studyUuid, nodeUuid, - sourceCompositeUuid, targetCompositeUuid, - modificationUuid, beforeUuid, userId)); - } - - private void handleMoveNetworkSubmodification(@NonNull UUID studyUuid, - @NonNull UUID nodeUuid, - UUID sourceCompositeUuid, - UUID targetCompositeUuid, - @NonNull UUID modificationUuid, - UUID beforeUuid, - String userId) { - studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); - try { - studyService.moveSubModification(studyUuid, nodeUuid, - sourceCompositeUuid, targetCompositeUuid, - modificationUuid, beforeUuid, userId); - } finally { - studyService.unblockNodeTree(studyUuid, nodeUuid); - } - } - - private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { - boolean isTargetInDifferentNodeTree = studyService.invalidateNodeTreeWhenMoveModifications(studyUuid, targetNodeUuid, originNodeUuid); - try { - studyService.moveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); - } finally { - studyService.unblockNodeTree(studyUuid, originNodeUuid); - if (isTargetInDifferentNodeTree) { - studyService.unblockNodeTree(studyUuid, targetNodeUuid); - } + UUID targetContainerId, + UUID sourceContainerId, + UUID modificationUuid, UUID beforeUuid, String userId) { + UUID targetNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(targetContainerId); + if (targetNodeUuid != null) { + handleRebuildNode(studyUuid, targetNodeUuid, userId, + () -> { + studyService.invalidateNodeTreeWhenMoveModification(studyUuid, targetNodeUuid); + try { + studyService.moveNetworkModifications( + studyUuid, + targetContainerId, + sourceContainerId, + List.of(modificationUuid), beforeUuid, + false, userId); + } finally { + studyService.unblockNodeTree(studyUuid, targetNodeUuid); + } + }); + } else { + studyService.moveNetworkModifications( + studyUuid, + targetContainerId, + sourceContainerId, + List.of(modificationUuid), beforeUuid, + false, userId); } } diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index feefc135e..8939c4185 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2223,44 +2223,56 @@ public RootNetworkIndexationStatus getRootNetworkIndexationStatus(UUID studyUuid @Transactional public void moveNetworkModifications( @NonNull UUID studyUuid, - UUID targetNodeUuid, - @NonNull UUID originNodeUuid, + @NonNull UUID targetContainerId, + @NonNull UUID sourceContainerId, List modificationUuidList, UUID beforeUuid, boolean isTargetInDifferentNodeTree, String userId) { - boolean isTargetDifferentNode = !targetNodeUuid.equals(originNodeUuid); - - List childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); - List originNodeChildrenUuids = new ArrayList<>(); - notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); - if (isTargetDifferentNode) { - originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); - notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); + UUID targetNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(targetContainerId); + UUID originNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(sourceContainerId); + + List childrenUuids = List.of(); + List originNodeChildrenUuids = List.of(); + if(targetNodeUuid != null) { + childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); + + notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); + if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { + originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); + notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); + } } - try { - checkStudyContainsNode(studyUuid, targetNodeUuid); + try { + List applicationContexts = List.of(); StudyEntity studyEntity = getStudy(studyUuid); - List studyRootNetworkEntities = studyEntity.getRootNetworks(); - UUID originGroupUuid = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); - UUID targetGroupUuid = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); - - List modificationApplicationContexts = studyRootNetworkEntities.stream() - .map(rootNetworkEntity -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rootNetworkEntity.getId(), targetNodeUuid, rootNetworkEntity.getNetworkUuid())) - .toList(); + if (targetNodeUuid != null) { + checkStudyContainsNode(studyUuid, targetNodeUuid); + applicationContexts = studyEntity.getRootNetworks().stream() + .map(rn -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rn.getId(), targetNodeUuid, rn.getNetworkUuid())) + .toList(); + } - NetworkModificationsResult networkModificationsResult = networkModificationService.moveModifications(originGroupUuid, targetGroupUuid, beforeUuid, Pair.of(modificationUuidList, modificationApplicationContexts), isTargetInDifferentNodeTree); - rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, networkModificationsResult.modificationUuids()); + NetworkModificationsResult result = networkModificationService.moveModifications( + sourceContainerId, + targetContainerId, + beforeUuid, + Pair.of(modificationUuidList, applicationContexts), + isTargetInDifferentNodeTree); - // Target node + if (originNodeUuid != null && targetNodeUuid != null) { + rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, result.modificationUuids()); + } if (isTargetInDifferentNodeTree) { - emitNetworkModificationImpactsForAllRootNetworks(networkModificationsResult.modificationResults(), studyEntity, targetNodeUuid); + emitNetworkModificationImpactsForAllRootNetworks(result.modificationResults(), studyEntity, targetNodeUuid); } } finally { - notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); - if (isTargetDifferentNode) { - notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); + if(targetNodeUuid != null) { + notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); + if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { + notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); + } } } notificationService.emitElementUpdated(studyUuid, userId); diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 16f020e71..13a6e4b14 100644 --- a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java +++ b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java @@ -57,7 +57,7 @@ void testRebuildSingleNode() { Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); + //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); } @@ -71,7 +71,7 @@ void testRebuildMultipleNodes() { Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node2Uuid); - rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetworkUuid, userId); @@ -86,7 +86,7 @@ void testRebuildMultipleRootNetworks() { ) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + //rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetwork2Uuid, userId); @@ -108,7 +108,7 @@ void testRebuildMultipleRootNetworksAndNodes() { ) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node2Uuid); - rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + //rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetwork2Uuid, userId); @@ -123,7 +123,7 @@ void testRebuildConstructionNode() { doReturn(true).when(networkModificationTreeService).isRootOrConstructionNode(any()); - rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); + //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); verify(studyService, times(0)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); } diff --git a/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java b/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java index ad313cab3..e98bed63a 100644 --- a/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java +++ b/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java @@ -75,9 +75,9 @@ void testCreateNetworkModification() { @Test void testMoveNetworkModification() { - studyController.moveModification(studyUuid, nodeUuid, modificationUuid, null, userId); + //studyController.moveModification(studyUuid, nodeUuid, modificationUuid, null, userId); - verify(rebuildNodeService, times(1)).moveNetworkModification(eq(studyUuid), eq(nodeUuid), eq(modificationUuid), isNull(), eq(userId)); + //verify(rebuildNodeService, times(1)).moveNetworkModification(eq(studyUuid), eq(nodeUuid), eq(modificationUuid), isNull(), eq(userId)); verify(studyService, times(1)).buildNode(eq(studyUuid), eq(nodeUuid), any(), eq(userId)); } @@ -87,7 +87,7 @@ void testMoveNetworkModifications() { UUID originNodeUuid = UUID.randomUUID(); studyController.moveOrCopyModifications(studyUuid, nodeUuid, StudyConstants.ModificationsActionType.MOVE, studyUuid, originNodeUuid, modificationUuids, userId); - verify(rebuildNodeService, times(1)).moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationUuids, userId); + //verify(rebuildNodeService, times(1)).moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationUuids, userId); verify(studyService, times(1)).buildNode(eq(studyUuid), eq(nodeUuid), any(), eq(userId)); } From cb11203e5fddd85981d9a18f549429f424333db7 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 11:33:25 +0200 Subject: [PATCH 02/21] Fix cut move --- .../server/controller/StudyController.java | 2 +- .../server/service/RebuildNodeService.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java index d991efe5e..9d9788092 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -675,7 +675,7 @@ public ResponseEntity moveOrCopyModifications(@PathVariable("studyUuid") U } studyService.assertNoBlockedNodeInStudy(studyUuid, originNodeUuid); studyService.assertNoBlockedNodeInStudy(studyUuid, nodeUuid); - //rebuildNodeService.moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationsToCopyUuidList, userId); + rebuildNodeService.moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationsToCopyUuidList, userId); break; } return ResponseEntity.ok().build(); diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index e74b421c4..6f466a0ef 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -65,6 +65,25 @@ public void restoreNetworkModifications(UUID studyUuid, UUID nodeUuid, List studyService.restoreNetworkModifications(studyUuid, nodeUuid, modificationsUuids, userId)); } + public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { + handleRebuildNode(studyUuid, targetNodeUuid, originNodeUuid, userId, + () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); + } + + private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { + boolean isTargetInDifferentNodeTree = studyService.invalidateNodeTreeWhenMoveModifications(studyUuid, targetNodeUuid, originNodeUuid); + try { + UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); + UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); + studyService.moveNetworkModifications(studyUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); + } finally { + studyService.unblockNodeTree(studyUuid, originNodeUuid); + if (isTargetInDifferentNodeTree) { + studyService.unblockNodeTree(studyUuid, targetNodeUuid); + } + } + } + public void moveNetworkModification( UUID studyUuid, UUID targetContainerId, From 1bb3275b3640872b6c0fb1808df97e316f19bd32 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 14:31:13 +0200 Subject: [PATCH 03/21] Fix notification handling --- .../server/controller/StudyController.java | 1 + .../server/service/RebuildNodeService.java | 42 ++++++++----------- .../study/server/service/StudyService.java | 38 +++-------------- .../ModificationToExcludeTest.java | 4 +- 4 files changed, 26 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java index 9d9788092..d3db2890e 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -645,6 +645,7 @@ public ResponseEntity moveModification( ? networkModificationTreeService.getModificationGroupUuid(nodeUuid) : targetContainerId; rebuildNodeService.moveNetworkModification( studyUuid, + nodeUuid, resolvedTargetId, resolvedSourceId, modificationUuid, beforeUuid, userId); diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index 6f466a0ef..ec71da32f 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -75,7 +75,7 @@ private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, try { UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); - studyService.moveNetworkModifications(studyUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); + studyService.moveNetworkModifications(studyUuid, targetNodeUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); } finally { studyService.unblockNodeTree(studyUuid, originNodeUuid); if (isTargetInDifferentNodeTree) { @@ -86,33 +86,25 @@ private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, public void moveNetworkModification( UUID studyUuid, + UUID nodeUuid, UUID targetContainerId, UUID sourceContainerId, UUID modificationUuid, UUID beforeUuid, String userId) { - UUID targetNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(targetContainerId); - if (targetNodeUuid != null) { - handleRebuildNode(studyUuid, targetNodeUuid, userId, - () -> { - studyService.invalidateNodeTreeWhenMoveModification(studyUuid, targetNodeUuid); - try { - studyService.moveNetworkModifications( - studyUuid, - targetContainerId, - sourceContainerId, - List.of(modificationUuid), beforeUuid, - false, userId); - } finally { - studyService.unblockNodeTree(studyUuid, targetNodeUuid); - } - }); - } else { - studyService.moveNetworkModifications( - studyUuid, - targetContainerId, - sourceContainerId, - List.of(modificationUuid), beforeUuid, - false, userId); - } + handleRebuildNode(studyUuid, nodeUuid, userId, + () -> { + studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); + try { + studyService.moveNetworkModifications( + studyUuid, + nodeUuid, + targetContainerId, + sourceContainerId, + List.of(modificationUuid), beforeUuid, + false, userId); + } finally { + studyService.unblockNodeTree(studyUuid, nodeUuid); + } + }); } private void handleRebuildNode(UUID studyUuid, UUID nodeUuid, String userId, Runnable action) { diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 8939c4185..41cac854d 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2223,25 +2223,21 @@ public RootNetworkIndexationStatus getRootNetworkIndexationStatus(UUID studyUuid @Transactional public void moveNetworkModifications( @NonNull UUID studyUuid, + @NonNull UUID targetNodeUuid, @NonNull UUID targetContainerId, @NonNull UUID sourceContainerId, List modificationUuidList, UUID beforeUuid, boolean isTargetInDifferentNodeTree, String userId) { - UUID targetNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(targetContainerId); UUID originNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(sourceContainerId); - List childrenUuids = List.of(); List originNodeChildrenUuids = List.of(); - if(targetNodeUuid != null) { - childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); - - notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); - if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { - originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); - notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); - } + List childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); + notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); + if (isTargetInDifferentNodeTree) { + originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); + notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); } try { @@ -2278,28 +2274,6 @@ public void moveNetworkModifications( notificationService.emitElementUpdated(studyUuid, userId); } - public void moveSubModification( - @NonNull UUID studyUuid, - @NonNull UUID nodeUuid, - UUID sourceCompositeUuid, - UUID targetCompositeUuid, - @NonNull UUID modificationUuid, - UUID beforeUuid, - String userId) { - - List childrenUuids = networkModificationTreeService.getChildrenUuids(nodeUuid); - try { - notificationService.emitStartModificationEquipmentNotification(studyUuid, nodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); - checkStudyContainsNode(studyUuid, nodeUuid); - UUID groupUuid = networkModificationTreeService.getModificationGroupUuid(nodeUuid); - networkModificationService.moveSubModification( - groupUuid, sourceCompositeUuid, targetCompositeUuid, modificationUuid, beforeUuid); - } finally { - notificationService.emitEndModificationEquipmentNotification(studyUuid, nodeUuid, childrenUuids); - } - notificationService.emitElementUpdated(studyUuid, userId); - } - private void emitNetworkModificationImpactsForAllRootNetworks(List> modificationResults, StudyEntity studyEntity, UUID impactedNode) { int index = 0; List rootNetworkEntities = studyEntity.getRootNetworks(); diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index 7b47cf868..baad8a076 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -784,7 +784,7 @@ void testMoveExcludedModification() { // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); - assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, firstNodeUuid, modificationsToMove, null, false, USER_ID)); + assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, firstNodeUuid, firstNodeUuid, modificationsToMove, null, false, USER_ID)); // assert origin node still have all excluded modifications Set excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); @@ -802,7 +802,7 @@ void testMoveExcludedModification() { .lastGroupApplicationStatus(NetworkModificationResult.ApplicationStatus.ALL_OK) .networkImpacts(List.of()) .build())))).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); - studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, null, true, USER_ID); + studyService.moveNetworkModifications(studyUuid, firstNodeUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, null, true, USER_ID); // assert origin node still have all excluded modifications, except the moved one excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); From 92e688c729acc2ea52ec61de97fb2bca1cb775ee Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 16:57:10 +0200 Subject: [PATCH 04/21] Lint --- .../study/server/service/StudyService.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 41cac854d..2c4c78d13 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2243,12 +2243,10 @@ public void moveNetworkModifications( try { List applicationContexts = List.of(); StudyEntity studyEntity = getStudy(studyUuid); - if (targetNodeUuid != null) { - checkStudyContainsNode(studyUuid, targetNodeUuid); - applicationContexts = studyEntity.getRootNetworks().stream() - .map(rn -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rn.getId(), targetNodeUuid, rn.getNetworkUuid())) - .toList(); - } + checkStudyContainsNode(studyUuid, targetNodeUuid); + applicationContexts = studyEntity.getRootNetworks().stream() + .map(rn -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rn.getId(), targetNodeUuid, rn.getNetworkUuid())) + .toList(); NetworkModificationsResult result = networkModificationService.moveModifications( sourceContainerId, @@ -2257,19 +2255,18 @@ public void moveNetworkModifications( Pair.of(modificationUuidList, applicationContexts), isTargetInDifferentNodeTree); - if (originNodeUuid != null && targetNodeUuid != null) { + if (originNodeUuid != null) { rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, result.modificationUuids()); } if (isTargetInDifferentNodeTree) { emitNetworkModificationImpactsForAllRootNetworks(result.modificationResults(), studyEntity, targetNodeUuid); } } finally { - if(targetNodeUuid != null) { - notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); - if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { - notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); - } + notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); + if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { + notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); } + } notificationService.emitElementUpdated(studyUuid, userId); } From cae9f8a4796fc40024de081a5575d8cc6740e282 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 17:10:27 +0200 Subject: [PATCH 05/21] Fix rebuild tests --- .../study/server/service/RebuildNodeService.java | 14 ++++++++++++++ .../study/server/RebuildNodeServiceTest.java | 10 +++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index ec71da32f..3725df9c9 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -65,6 +65,20 @@ public void restoreNetworkModifications(UUID studyUuid, UUID nodeUuid, List studyService.restoreNetworkModifications(studyUuid, nodeUuid, modificationsUuids, userId)); } + public void moveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { + handleRebuildNode(studyUuid, nodeUuid, userId, + () -> handleMoveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId)); + } + + private void handleMoveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { + studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); + try { + studyService.moveNetworkModifications(studyUuid, nodeUuid, nodeUuid, nodeUuid, List.of(modificationUuid), beforeUuid, false, userId); + } finally { + studyService.unblockNodeTree(studyUuid, nodeUuid); + } + } + public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { handleRebuildNode(studyUuid, targetNodeUuid, originNodeUuid, userId, () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 13a6e4b14..16f020e71 100644 --- a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java +++ b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java @@ -57,7 +57,7 @@ void testRebuildSingleNode() { Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); + rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); } @@ -71,7 +71,7 @@ void testRebuildMultipleNodes() { Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node2Uuid); - //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetworkUuid, userId); @@ -86,7 +86,7 @@ void testRebuildMultipleRootNetworks() { ) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - //rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetwork2Uuid, userId); @@ -108,7 +108,7 @@ void testRebuildMultipleRootNetworksAndNodes() { ) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node2Uuid); - //rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); + rebuildNodeService.moveNetworkModifications(studyUuid, node1Uuid, node2Uuid, List.of(), userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetwork2Uuid, userId); @@ -123,7 +123,7 @@ void testRebuildConstructionNode() { doReturn(true).when(networkModificationTreeService).isRootOrConstructionNode(any()); - //rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); + rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); verify(studyService, times(0)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); } From fe69c22fc743799b8764a85b03e72f41b7e24b40 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 17:11:17 +0200 Subject: [PATCH 06/21] Checkstyle --- .../org/gridsuite/study/server/service/RebuildNodeService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index 3725df9c9..c5367d9a7 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -67,7 +67,7 @@ public void restoreNetworkModifications(UUID studyUuid, UUID nodeUuid, List handleMoveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId)); + () -> handleMoveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId)); } private void handleMoveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { From 99ddc98911d87d4fb946325784466b46f26e9086 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 17:17:43 +0200 Subject: [PATCH 07/21] Remove unused path --- .../server/service/RebuildNodeService.java | 14 ----------- .../study/server/RebuildNodeServiceTest.java | 25 ------------------- .../StudyControllerRebuildNodeTest.java | 10 +------- 3 files changed, 1 insertion(+), 48 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index c5367d9a7..ec71da32f 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -65,20 +65,6 @@ public void restoreNetworkModifications(UUID studyUuid, UUID nodeUuid, List studyService.restoreNetworkModifications(studyUuid, nodeUuid, modificationsUuids, userId)); } - public void moveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { - handleRebuildNode(studyUuid, nodeUuid, userId, - () -> handleMoveNetworkModification(studyUuid, nodeUuid, modificationUuid, beforeUuid, userId)); - } - - private void handleMoveNetworkModification(UUID studyUuid, UUID nodeUuid, UUID modificationUuid, UUID beforeUuid, String userId) { - studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); - try { - studyService.moveNetworkModifications(studyUuid, nodeUuid, nodeUuid, nodeUuid, List.of(modificationUuid), beforeUuid, false, userId); - } finally { - studyService.unblockNodeTree(studyUuid, nodeUuid); - } - } - public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { handleRebuildNode(studyUuid, targetNodeUuid, originNodeUuid, userId, () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 16f020e71..20db1e638 100644 --- a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java +++ b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java @@ -51,17 +51,6 @@ void setUp() { doReturn(false).when(networkModificationTreeService).isRootOrConstructionNode(any()); } - @Test - void testRebuildSingleNode() { - doReturn( - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) - ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - - rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); - - verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); - } - @Test void testRebuildMultipleNodes() { doReturn( @@ -113,18 +102,4 @@ void testRebuildMultipleRootNetworksAndNodes() { verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetwork2Uuid, userId); } - - @Test - void testRebuildConstructionNode() { - doReturn( - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)), - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.NOT_BUILT)) - ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); - - doReturn(true).when(networkModificationTreeService).isRootOrConstructionNode(any()); - - rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), userId); - - verify(studyService, times(0)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); - } } diff --git a/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java b/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java index e98bed63a..65a4dd52f 100644 --- a/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java +++ b/src/test/java/org/gridsuite/study/server/studycontroller/StudyControllerRebuildNodeTest.java @@ -73,21 +73,13 @@ void testCreateNetworkModification() { verify(studyService, times(1)).buildNode(eq(studyUuid), eq(nodeUuid), any(), eq(userId)); } - @Test - void testMoveNetworkModification() { - //studyController.moveModification(studyUuid, nodeUuid, modificationUuid, null, userId); - - //verify(rebuildNodeService, times(1)).moveNetworkModification(eq(studyUuid), eq(nodeUuid), eq(modificationUuid), isNull(), eq(userId)); - verify(studyService, times(1)).buildNode(eq(studyUuid), eq(nodeUuid), any(), eq(userId)); - } - @Test void testMoveNetworkModifications() { List modificationUuids = List.of(UUID.randomUUID()); UUID originNodeUuid = UUID.randomUUID(); studyController.moveOrCopyModifications(studyUuid, nodeUuid, StudyConstants.ModificationsActionType.MOVE, studyUuid, originNodeUuid, modificationUuids, userId); - //verify(rebuildNodeService, times(1)).moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationUuids, userId); + verify(rebuildNodeService, times(1)).moveNetworkModifications(studyUuid, nodeUuid, originNodeUuid, modificationUuids, userId); verify(studyService, times(1)).buildNode(eq(studyUuid), eq(nodeUuid), any(), eq(userId)); } From d28f0a18e383a843db36bf06f172ee6e0b4b37d5 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 18:14:36 +0200 Subject: [PATCH 08/21] Fix exclusion tests --- .../gridsuite/study/server/service/RebuildNodeService.java | 4 +--- .../org/gridsuite/study/server/service/StudyService.java | 7 +++++++ .../server/rootnetworks/ModificationToExcludeTest.java | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index ec71da32f..732c50895 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -73,9 +73,7 @@ public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID o private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { boolean isTargetInDifferentNodeTree = studyService.invalidateNodeTreeWhenMoveModifications(studyUuid, targetNodeUuid, originNodeUuid); try { - UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); - UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); - studyService.moveNetworkModifications(studyUuid, targetNodeUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); + studyService.moveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, isTargetInDifferentNodeTree, userId); } finally { studyService.unblockNodeTree(studyUuid, originNodeUuid); if (isTargetInDifferentNodeTree) { diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 2c4c78d13..5e2fe8564 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2220,6 +2220,13 @@ public RootNetworkIndexationStatus getRootNetworkIndexationStatus(UUID studyUuid return rootNetwork.getIndexationStatus(); } + @Transactional + public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, boolean isTargetInDifferentNodeTree, String userId) { + UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); + UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); + moveNetworkModifications(studyUuid, targetNodeUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); + } + @Transactional public void moveNetworkModifications( @NonNull UUID studyUuid, diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index baad8a076..79a6aa50c 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -784,7 +784,7 @@ void testMoveExcludedModification() { // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); - assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, firstNodeUuid, firstNodeUuid, modificationsToMove, null, false, USER_ID)); + assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, firstNodeUuid, modificationsToMove, false, USER_ID)); // assert origin node still have all excluded modifications Set excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); @@ -802,7 +802,7 @@ void testMoveExcludedModification() { .lastGroupApplicationStatus(NetworkModificationResult.ApplicationStatus.ALL_OK) .networkImpacts(List.of()) .build())))).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); - studyService.moveNetworkModifications(studyUuid, firstNodeUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, null, true, USER_ID); + studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, true, USER_ID); // assert origin node still have all excluded modifications, except the moved one excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); From bbbdd0b305c5719595f19f58957d6a5586fca776 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 28 May 2026 18:36:54 +0200 Subject: [PATCH 09/21] Fix modifications tests --- .../study/server/NetworkModificationTest.java | 84 +++++++++++-------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index e1c911055..24e15a59c 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -2008,7 +2008,8 @@ void testReorderModification() throws Exception { .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))).getId(); // switch the 2 modifications order (modification1 is set at the end, after modification2) - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationID}", + // Same-container reorder: no source/target params -> controller resolves both to the node's group. + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationID}", studyNameUserIdUuid, modificationNodeUuid, modification1).header(USER_ID_HEADER, "userId")) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyNameUserIdUuid, modificationNodeUuid, output); @@ -2026,8 +2027,10 @@ void testReorderModification() throws Exception { expectedBodyStr); // switch back the 2 modifications order (modification1 is set before modification2) - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationID}?beforeUuid={modificationID2}", - studyNameUserIdUuid, modificationNodeUuid, modification1, modification2).header(USER_ID_HEADER, "userId")) + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationID}", + studyNameUserIdUuid, modificationNodeUuid, modification1) + .queryParam("beforeUuid", modification2.toString()) + .header(USER_ID_HEADER, "userId")) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyNameUserIdUuid, modificationNodeUuid, output); checkEquipmentUpdatingMessagesReceived(studyNameUserIdUuid, modificationNodeUuid); @@ -3162,7 +3165,7 @@ private void checkElementUpdatedMessageSent(UUID elementUuid, String userId) { } @Test - void testMoveSubModification() throws Exception { + void testMoveCompositeSubmodification() throws Exception { String userId = "userId"; StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, "UCTE"); UUID studyUuid = studyEntity.getId(); @@ -3170,23 +3173,29 @@ void testMoveSubModification() throws Exception { NetworkModificationNode node = createNetworkModificationNode(studyUuid, rootNodeUuid, UUID.randomUUID(), VARIANT_ID, "node 1", userId); UUID nodeUuid = node.getId(); + UUID nodeGroupUuid = node.getModificationGroupUuid(); UUID modificationUuid = UUID.randomUUID(); - UUID sourceCompositeUuid = UUID.randomUUID(); - UUID targetCompositeUuid = UUID.randomUUID(); + UUID sourceContainerId = UUID.randomUUID(); + UUID targetContainerId = UUID.randomUUID(); UUID beforeUuid = UUID.randomUUID(); - String moveSubModifUrlPattern = "/v1/network-composite-modifications/groups/" + node.getModificationGroupUuid() - + "/sub-modifications/" + modificationUuid; + // The study-server now forwards every move to the modification-server's group endpoint: + // PUT /v1/groups/{targetContainerId}?action=MOVE&originGroupUuid={source}&build={bool}[&before={before}] + // The target container is carried in the PATH (GROUP_PATH = "groups/{groupUuid}", no suffix); + // action / originGroupUuid / build / before are query params. - // --- Case 1: move between two composites with a beforeUuid --- - wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveSubModifUrlPattern)) - .willReturn(WireMock.ok())); + // --- Case 1: move between two explicit containers, with a beforeUuid --- + String moveUrlCase1 = "/v1/groups/" + targetContainerId; + wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveUrlCase1)) + .willReturn(WireMock.ok() + .withHeader("Content-Type", "application/json") + .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/composite-sub-modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) - .queryParam("sourceCompositeUuid", sourceCompositeUuid.toString()) - .queryParam("targetCompositeUuid", targetCompositeUuid.toString()) + .queryParam("sourceContainerId", sourceContainerId.toString()) + .queryParam("targetContainerId", targetContainerId.toString()) .queryParam("beforeUuid", beforeUuid.toString()) .header(USER_ID_HEADER, userId)) .andExpect(status().isOk()); @@ -3194,42 +3203,51 @@ void testMoveSubModification() throws Exception { checkEquipmentUpdatingMessagesReceived(studyUuid, nodeUuid); checkEquipmentUpdatingFinishedMessagesReceived(studyUuid, nodeUuid); checkElementUpdatedMessageSent(studyUuid, userId); - WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveSubModifUrlPattern, false, Map.of( - "sourceCompositeUuid", WireMock.equalTo(sourceCompositeUuid.toString()), - "targetCompositeUuid", WireMock.equalTo(targetCompositeUuid.toString()), - "beforeUuid", WireMock.equalTo(beforeUuid.toString())), null); - - // --- Case 2: move from root level into a composite (no sourceCompositeUuid) --- - wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveSubModifUrlPattern)) - .willReturn(WireMock.ok())); + WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlCase1, false, Map.of( + "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), + "originGroupUuid", WireMock.equalTo(sourceContainerId.toString()), + "before", WireMock.equalTo(beforeUuid.toString())), null); + + // --- Case 2: target container omitted -> controller resolves it to the node's group --- + // Target is in the path, so the downstream URL now targets the node's group. + String moveUrlNodeGroup = "/v1/groups/" + nodeGroupUuid; + wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveUrlNodeGroup)) + .willReturn(WireMock.ok() + .withHeader("Content-Type", "application/json") + .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/composite-sub-modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) - .queryParam("targetCompositeUuid", targetCompositeUuid.toString()) + .queryParam("sourceContainerId", sourceContainerId.toString()) .header(USER_ID_HEADER, userId)) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyUuid, nodeUuid, output); checkEquipmentUpdatingMessagesReceived(studyUuid, nodeUuid); checkEquipmentUpdatingFinishedMessagesReceived(studyUuid, nodeUuid); checkElementUpdatedMessageSent(studyUuid, userId); - WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveSubModifUrlPattern, false, Map.of( - "targetCompositeUuid", WireMock.equalTo(targetCompositeUuid.toString())), null); + WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlNodeGroup, false, Map.of( + "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), + "originGroupUuid", WireMock.equalTo(sourceContainerId.toString())), null); - // --- Case 3: move from inside a composite to root level (no targetCompositeUuid) --- - wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveSubModifUrlPattern)) - .willReturn(WireMock.ok())); + // --- Case 3: source container omitted -> controller resolves originGroupUuid to the node's group --- + String moveUrlCase3 = "/v1/groups/" + targetContainerId; + wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveUrlCase3)) + .willReturn(WireMock.ok() + .withHeader("Content-Type", "application/json") + .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/composite-sub-modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) - .queryParam("sourceCompositeUuid", sourceCompositeUuid.toString()) + .queryParam("targetContainerId", targetContainerId.toString()) .header(USER_ID_HEADER, userId)) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyUuid, nodeUuid, output); checkEquipmentUpdatingMessagesReceived(studyUuid, nodeUuid); checkEquipmentUpdatingFinishedMessagesReceived(studyUuid, nodeUuid); checkElementUpdatedMessageSent(studyUuid, userId); - WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveSubModifUrlPattern, false, Map.of( - "sourceCompositeUuid", WireMock.equalTo(sourceCompositeUuid.toString())), null); + WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlCase3, false, Map.of( + "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), + "originGroupUuid", WireMock.equalTo(nodeGroupUuid.toString())), null); } @Test From 88f0a058456e500ad805a27987648d4af11664e9 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 29 May 2026 09:13:40 +0200 Subject: [PATCH 10/21] Revert endpoint resource label --- .../study/server/controller/StudyController.java | 2 +- .../study/server/NetworkModificationTest.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java index d3db2890e..381be0377 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -625,7 +625,7 @@ public ResponseEntity getAllMapData( return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(studyService.getAllMapData(studyUuid, nodeUuid, rootNetworkUuid, substationsIds)); } - @PutMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}") + @PutMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}") @Operation(summary = "Move a modification within or between containers (groups or composites)") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The modification order has been updated")}) public ResponseEntity moveModification( diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 24e15a59c..0fe12b7c3 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -2009,7 +2009,7 @@ void testReorderModification() throws Exception { // switch the 2 modifications order (modification1 is set at the end, after modification2) // Same-container reorder: no source/target params -> controller resolves both to the node's group. - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationID}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationID}", studyNameUserIdUuid, modificationNodeUuid, modification1).header(USER_ID_HEADER, "userId")) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyNameUserIdUuid, modificationNodeUuid, output); @@ -2027,7 +2027,7 @@ void testReorderModification() throws Exception { expectedBodyStr); // switch back the 2 modifications order (modification1 is set before modification2) - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationID}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationID}", studyNameUserIdUuid, modificationNodeUuid, modification1) .queryParam("beforeUuid", modification2.toString()) .header(USER_ID_HEADER, "userId")) @@ -3192,7 +3192,7 @@ void testMoveCompositeSubmodification() throws Exception { .withHeader("Content-Type", "application/json") .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) .queryParam("sourceContainerId", sourceContainerId.toString()) .queryParam("targetContainerId", targetContainerId.toString()) @@ -3216,7 +3216,7 @@ void testMoveCompositeSubmodification() throws Exception { .withHeader("Content-Type", "application/json") .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) .queryParam("sourceContainerId", sourceContainerId.toString()) .header(USER_ID_HEADER, userId)) @@ -3236,7 +3236,7 @@ void testMoveCompositeSubmodification() throws Exception { .withHeader("Content-Type", "application/json") .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); - mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) .queryParam("targetContainerId", targetContainerId.toString()) .header(USER_ID_HEADER, userId)) From 58d8335367d7e99eb476f8ba9e3aa79df6df68cd Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 11:43:54 +0200 Subject: [PATCH 11/21] Coderabbit suggestions --- .../org/gridsuite/study/server/service/StudyService.java | 7 +++---- .../server/rootnetworks/ModificationToExcludeTest.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 638f3765a..4ec84e77c 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2265,10 +2265,9 @@ public void moveNetworkModifications( } try { - List applicationContexts = List.of(); StudyEntity studyEntity = getStudy(studyUuid); checkStudyContainsNode(studyUuid, targetNodeUuid); - applicationContexts = studyEntity.getRootNetworks().stream() + List applicationContexts = studyEntity.getRootNetworks().stream() .map(rn -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rn.getId(), targetNodeUuid, rn.getNetworkUuid())) .toList(); @@ -2279,10 +2278,10 @@ public void moveNetworkModifications( Pair.of(modificationUuidList, applicationContexts), isTargetInDifferentNodeTree); - if (originNodeUuid != null) { + if (result != null && originNodeUuid != null) { rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, result.modificationUuids()); } - if (isTargetInDifferentNodeTree) { + if (result != null && isTargetInDifferentNodeTree) { emitNetworkModificationImpactsForAllRootNetworks(result.modificationResults(), studyEntity, targetNodeUuid); } } finally { diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index 79a6aa50c..bec2d49f0 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -784,7 +784,7 @@ void testMoveExcludedModification() { // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); - assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, firstNodeUuid, modificationsToMove, false, USER_ID)); + assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, false, USER_ID)); // assert origin node still have all excluded modifications Set excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); From 0064243ce91a106dab532d81a254980160bc18d9 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 11:56:57 +0200 Subject: [PATCH 12/21] Fix test --- .../study/server/rootnetworks/ModificationToExcludeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index bec2d49f0..55d9e00b6 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -782,7 +782,7 @@ void testMoveExcludedModification() { rootNetworkNodeInfoRepository.save(rootNetwork0NodeInfo1Entity); // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications - Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); + Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(false)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, false, USER_ID)); From fd07c3f67ab90f057b420bb28c95d385e838dcd9 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 12:00:26 +0200 Subject: [PATCH 13/21] Coderabbit suggestion --- .../study/server/NetworkModificationTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 0fe12b7c3..05c1ec394 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -3203,10 +3203,15 @@ void testMoveCompositeSubmodification() throws Exception { checkEquipmentUpdatingMessagesReceived(studyUuid, nodeUuid); checkEquipmentUpdatingFinishedMessagesReceived(studyUuid, nodeUuid); checkElementUpdatedMessageSent(studyUuid, userId); + Pair, List> expectedMoveBody = + Pair.of(List.of(modificationUuid), + List.of(rootNetworkNodeInfoService.getNetworkModificationApplicationContext( + studyTestUtils.getOneRootNetworkUuid(studyUuid), nodeUuid, NETWORK_UUID))); + String expectedMoveBodyJson = mapper.writeValueAsString(expectedMoveBody); WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlCase1, false, Map.of( "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), "originGroupUuid", WireMock.equalTo(sourceContainerId.toString()), - "before", WireMock.equalTo(beforeUuid.toString())), null); + "before", WireMock.equalTo(beforeUuid.toString())), expectedMoveBodyJson); // --- Case 2: target container omitted -> controller resolves it to the node's group --- // Target is in the path, so the downstream URL now targets the node's group. @@ -3227,7 +3232,7 @@ void testMoveCompositeSubmodification() throws Exception { checkElementUpdatedMessageSent(studyUuid, userId); WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlNodeGroup, false, Map.of( "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), - "originGroupUuid", WireMock.equalTo(sourceContainerId.toString())), null); + "originGroupUuid", WireMock.equalTo(sourceContainerId.toString())), expectedMoveBodyJson); // --- Case 3: source container omitted -> controller resolves originGroupUuid to the node's group --- String moveUrlCase3 = "/v1/groups/" + targetContainerId; @@ -3247,7 +3252,7 @@ void testMoveCompositeSubmodification() throws Exception { checkElementUpdatedMessageSent(studyUuid, userId); WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlCase3, false, Map.of( "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), - "originGroupUuid", WireMock.equalTo(nodeGroupUuid.toString())), null); + "originGroupUuid", WireMock.equalTo(nodeGroupUuid.toString())), expectedMoveBodyJson); } @Test From 6ec4a7806506be7db93d9573235c77dc0111772d Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 12:31:19 +0200 Subject: [PATCH 14/21] Sonar transaction issue --- .../gridsuite/study/server/service/RebuildNodeService.java | 4 +++- .../org/gridsuite/study/server/service/StudyService.java | 7 ------- .../server/rootnetworks/ModificationToExcludeTest.java | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index 732c50895..ec71da32f 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -73,7 +73,9 @@ public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID o private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { boolean isTargetInDifferentNodeTree = studyService.invalidateNodeTreeWhenMoveModifications(studyUuid, targetNodeUuid, originNodeUuid); try { - studyService.moveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, isTargetInDifferentNodeTree, userId); + UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); + UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); + studyService.moveNetworkModifications(studyUuid, targetNodeUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); } finally { studyService.unblockNodeTree(studyUuid, originNodeUuid); if (isTargetInDifferentNodeTree) { diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 4ec84e77c..3011c3a01 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2237,13 +2237,6 @@ public RootNetworkIndexationStatus getRootNetworkIndexationStatus(UUID studyUuid return rootNetwork.getIndexationStatus(); } - @Transactional - public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, boolean isTargetInDifferentNodeTree, String userId) { - UUID sourceContainerId = networkModificationTreeService.getModificationGroupUuid(originNodeUuid); - UUID targetContainerId = networkModificationTreeService.getModificationGroupUuid(targetNodeUuid); - moveNetworkModifications(studyUuid, targetNodeUuid, targetContainerId, sourceContainerId, modificationsToCopyUuidList, null, isTargetInDifferentNodeTree, userId); - } - @Transactional public void moveNetworkModifications( @NonNull UUID studyUuid, diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index 55d9e00b6..0d81d7ebf 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -784,7 +784,7 @@ void testMoveExcludedModification() { // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(false)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); - assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, false, USER_ID)); + assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, secondNode.getModificationGroupUuid(), firstNode.getModificationGroupUuid(), modificationsToMove, null, false, USER_ID)); // assert origin node still have all excluded modifications Set excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); @@ -802,7 +802,7 @@ void testMoveExcludedModification() { .lastGroupApplicationStatus(NetworkModificationResult.ApplicationStatus.ALL_OK) .networkImpacts(List.of()) .build())))).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); - studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, modificationsToMove, true, USER_ID); + studyService.moveNetworkModifications(studyUuid, secondNodeUuid, secondNode.getModificationGroupUuid(), firstNode.getModificationGroupUuid(), modificationsToMove, null, true, USER_ID); // assert origin node still have all excluded modifications, except the moved one excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); From 20bbbd9e6b76ee116c05e0bc10bd67449302e41a Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 12:45:08 +0200 Subject: [PATCH 15/21] Sonar issues --- .../gridsuite/study/server/NetworkModificationTest.java | 5 ----- .../server/rootnetworks/ModificationToExcludeTest.java | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 05c1ec394..97f34dc2a 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -3180,11 +3180,6 @@ void testMoveCompositeSubmodification() throws Exception { UUID targetContainerId = UUID.randomUUID(); UUID beforeUuid = UUID.randomUUID(); - // The study-server now forwards every move to the modification-server's group endpoint: - // PUT /v1/groups/{targetContainerId}?action=MOVE&originGroupUuid={source}&build={bool}[&before={before}] - // The target container is carried in the PATH (GROUP_PATH = "groups/{groupUuid}", no suffix); - // action / originGroupUuid / build / before are query params. - // --- Case 1: move between two explicit containers, with a beforeUuid --- String moveUrlCase1 = "/v1/groups/" + targetContainerId; wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo(moveUrlCase1)) diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index 0d81d7ebf..1d0499c60 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -767,6 +767,7 @@ void testMoveExcludedModification() { StudyEntity studyEntity = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); studyRepository.save(studyEntity); UUID studyUuid = studyEntity.getId(); + assertNotNull(studyUuid); List rootNetworkBasicInfos = studyService.getExistingBasicRootNetworkInfos(studyUuid); // create root node and a network modification node - it will create a RootNetworkNodeInfoEntity @@ -784,7 +785,9 @@ void testMoveExcludedModification() { // try to execute modification move - if modification move fails, they should not be moved from a node to another in exludedModifications Mockito.doThrow(new RuntimeException()).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(false)); List modificationsToMove = List.of(MODIFICATIONS_TO_EXCLUDE_RN_1.stream().findFirst().orElseThrow()); - assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, secondNode.getModificationGroupUuid(), firstNode.getModificationGroupUuid(), modificationsToMove, null, false, USER_ID)); + var secondNodeGroupUuid = secondNode.getModificationGroupUuid(); + var firstNodeGroupUuid = firstNode.getModificationGroupUuid(); + assertThrows(RuntimeException.class, () -> studyService.moveNetworkModifications(studyUuid, firstNodeUuid, secondNodeGroupUuid, firstNodeGroupUuid, modificationsToMove, null, false, USER_ID)); // assert origin node still have all excluded modifications Set excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); @@ -802,7 +805,7 @@ void testMoveExcludedModification() { .lastGroupApplicationStatus(NetworkModificationResult.ApplicationStatus.ALL_OK) .networkImpacts(List.of()) .build())))).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); - studyService.moveNetworkModifications(studyUuid, secondNodeUuid, secondNode.getModificationGroupUuid(), firstNode.getModificationGroupUuid(), modificationsToMove, null, true, USER_ID); + studyService.moveNetworkModifications(studyUuid, secondNodeUuid, secondNodeGroupUuid, firstNodeGroupUuid, modificationsToMove, null, true, USER_ID); // assert origin node still have all excluded modifications, except the moved one excludedModifications = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNodeUuid, rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")).getModificationsUuidsToExclude(); From 4150a18660a88a695a20a806f5bf33c086fa3578 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 15:11:57 +0200 Subject: [PATCH 16/21] Restore tests --- .../study/server/RebuildNodeServiceTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 20db1e638..1a6a92205 100644 --- a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java +++ b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java @@ -51,6 +51,17 @@ void setUp() { doReturn(false).when(networkModificationTreeService).isRootOrConstructionNode(any()); } + @Test + void testRebuildSingleNode() { + doReturn( + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) + ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); + + rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), null, userId); + + verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); + } + @Test void testRebuildMultipleNodes() { doReturn( @@ -102,4 +113,18 @@ void testRebuildMultipleRootNetworksAndNodes() { verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); verify(studyService, times(1)).buildNode(studyUuid, node2Uuid, rootNetwork2Uuid, userId); } + + @Test + void testRebuildConstructionNode() { + doReturn( + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)), + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.NOT_BUILT)) + ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); + + doReturn(true).when(networkModificationTreeService).isRootOrConstructionNode(any()); + + rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), null, userId); + + verify(studyService, times(0)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); + } } From fe0bc869c007dd448077fe8987cd5436b8f55568 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 2 Jun 2026 15:12:51 +0200 Subject: [PATCH 17/21] Lint --- .../org/gridsuite/study/server/RebuildNodeServiceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 1a6a92205..066125b2f 100644 --- a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java +++ b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java @@ -54,7 +54,7 @@ void setUp() { @Test void testRebuildSingleNode() { doReturn( - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); rebuildNodeService.moveNetworkModification(studyUuid, node1Uuid, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), null, userId); @@ -117,8 +117,8 @@ void testRebuildMultipleRootNetworksAndNodes() { @Test void testRebuildConstructionNode() { doReturn( - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)), - Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.NOT_BUILT)) + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.BUILT)), + Map.of(rootNetworkUuid, NodeBuildStatus.from(BuildStatus.NOT_BUILT)) ).when(studyService).getNodeBuildStatusByRootNetwork(studyUuid, node1Uuid); doReturn(true).when(networkModificationTreeService).isRootOrConstructionNode(any()); From d0bee1d999ca585fcfc6e7f09643959112cd6ba8 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 3 Jun 2026 10:14:31 +0200 Subject: [PATCH 18/21] Restore check --- .../org/gridsuite/study/server/service/StudyService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 1fe3def2f..b4175143d 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2248,11 +2248,12 @@ public void moveNetworkModifications( boolean isTargetInDifferentNodeTree, String userId) { UUID originNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(sourceContainerId); + boolean isTargetDifferentNode = !targetNodeUuid.equals(originNodeUuid); List originNodeChildrenUuids = List.of(); List childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); - if (isTargetInDifferentNodeTree) { + if (isTargetDifferentNode && originNodeUuid != null) { originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); } @@ -2279,7 +2280,7 @@ public void moveNetworkModifications( } } finally { notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); - if (originNodeUuid != null && !originNodeUuid.equals(targetNodeUuid)) { + if (isTargetDifferentNode && originNodeUuid != null) { notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); } From 793dff2ad09ca8331bcc2ab8c18d1cbadddd3a2d Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 3 Jun 2026 10:15:49 +0200 Subject: [PATCH 19/21] Restore statement --- .../java/org/gridsuite/study/server/service/StudyService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index b4175143d..21c812c2e 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2250,8 +2250,8 @@ public void moveNetworkModifications( UUID originNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(sourceContainerId); boolean isTargetDifferentNode = !targetNodeUuid.equals(originNodeUuid); - List originNodeChildrenUuids = List.of(); List childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); + List originNodeChildrenUuids = new ArrayList<>(); notificationService.emitStartModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); if (isTargetDifferentNode && originNodeUuid != null) { originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); From 985d236462298d4b74f828bf13611703d036014c Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 10 Jun 2026 10:36:08 +0200 Subject: [PATCH 20/21] Restore missing function --- .../server/service/RebuildNodeService.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java index ea5dcda34..3fd174ed1 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -71,6 +71,23 @@ public void moveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID o () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); } + public UUID assembleModificationsIntoComposite(UUID studyUuid, UUID nodeUuid, List modificationsUuids, String userId) { + return handleRebuildNodeWithReturn( + studyUuid, + nodeUuid, + userId, + () -> { + studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); + UUID compositeUuid; + try { + compositeUuid = studyService.assembleModificationsIntoComposite(studyUuid, nodeUuid, modificationsUuids, userId); + } finally { + studyService.unblockNodeTree(studyUuid, nodeUuid); + } + return compositeUuid; + }); + } + private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, UUID originNodeUuid, List modificationsToCopyUuidList, String userId) { boolean isTargetInDifferentNodeTree = studyService.invalidateNodeTreeWhenMoveModifications(studyUuid, targetNodeUuid, originNodeUuid); try { From cfe6a0805a9d170251fac613874a9cbe3e482651 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 18 Jun 2026 11:41:38 +0200 Subject: [PATCH 21/21] Fix test --- .../gridsuite/study/server/NetworkModificationTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 2d7684c32..f04211904 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -3309,6 +3309,11 @@ void testMoveCompositeSubmodification() throws Exception { .withHeader("Content-Type", "application/json") .withBody("{\"modificationUuids\":[],\"modificationResults\":[]}"))); + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/network-composite-modifications/children-uuids")) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of())) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))); + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationUuid}", studyUuid, nodeUuid, modificationUuid) .queryParam("targetContainerId", targetContainerId.toString()) @@ -3321,6 +3326,8 @@ void testMoveCompositeSubmodification() throws Exception { WireMockUtilsCriteria.verifyPutRequest(wireMockServer, moveUrlCase3, false, Map.of( "action", WireMock.equalTo(StudyConstants.ModificationsActionType.MOVE.name()), "originGroupUuid", WireMock.equalTo(nodeGroupUuid.toString())), expectedMoveBodyJson); + WireMockUtilsCriteria.verifyGetRequest(wireMockServer, "/v1/network-composite-modifications/children-uuids", Map.of( + "uuids", WireMock.matching(".*")), 1); } @Test