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 b3f4be47c..375b1afee 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; @@ -627,35 +626,28 @@ public ResponseEntity getAllMapData( } @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( + @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, + nodeUuid, + resolvedTargetId, + resolvedSourceId, modificationUuid, beforeUuid, userId); 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 d38dbff80..ab0a8ac86 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 a54e19a98..b66f95adc 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java @@ -1341,4 +1341,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 b29ae2624..3fd174ed1 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; @@ -67,38 +66,9 @@ 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( - 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)); + () -> handleMoveNetworkModifications(studyUuid, targetNodeUuid, originNodeUuid, modificationsToCopyUuidList, userId)); } public UUID assembleModificationsIntoComposite(UUID studyUuid, UUID nodeUuid, List modificationsUuids, String userId) { @@ -118,27 +88,12 @@ public UUID assembleModificationsIntoComposite(UUID studyUuid, UUID nodeUuid, Li }); } - 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); + 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) { @@ -147,6 +102,29 @@ private void handleMoveNetworkModifications(UUID studyUuid, UUID targetNodeUuid, } } + public void moveNetworkModification( + UUID studyUuid, + UUID nodeUuid, + UUID targetContainerId, + UUID sourceContainerId, + UUID modificationUuid, UUID beforeUuid, String 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) { handleRebuildNode(studyUuid, nodeUuid, nodeUuid, userId, 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 5c43d9282..0a0982781 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2240,68 +2240,51 @@ public RootNetworkIndexationStatus getRootNetworkIndexationStatus(UUID studyUuid @Transactional public void moveNetworkModifications( @NonNull UUID studyUuid, - UUID targetNodeUuid, - @NonNull UUID originNodeUuid, + @NonNull UUID targetNodeUuid, + @NonNull UUID targetContainerId, + @NonNull UUID sourceContainerId, List modificationUuidList, UUID beforeUuid, boolean isTargetInDifferentNodeTree, String userId) { + UUID originNodeUuid = networkModificationTreeService.getNodeUuidByModificationGroup(sourceContainerId); 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) { + if (isTargetDifferentNode && originNodeUuid != null) { originNodeChildrenUuids = networkModificationTreeService.getChildrenUuids(originNodeUuid); notificationService.emitStartModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); } - try { - checkStudyContainsNode(studyUuid, targetNodeUuid); + try { 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(); - - NetworkModificationsResult networkModificationsResult = networkModificationService.moveModifications(originGroupUuid, targetGroupUuid, beforeUuid, Pair.of(modificationUuidList, modificationApplicationContexts), isTargetInDifferentNodeTree); - Set allMovedUuids = networkModificationService.expandToLeafUuids(networkModificationsResult.modificationUuids()); - rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, new ArrayList<>(allMovedUuids)); - - // Target node - if (isTargetInDifferentNodeTree) { - emitNetworkModificationImpactsForAllRootNetworks(networkModificationsResult.modificationResults(), studyEntity, targetNodeUuid); + checkStudyContainsNode(studyUuid, targetNodeUuid); + List applicationContexts = studyEntity.getRootNetworks().stream() + .map(rn -> rootNetworkNodeInfoService.getNetworkModificationApplicationContext(rn.getId(), targetNodeUuid, rn.getNetworkUuid())) + .toList(); + + NetworkModificationsResult result = networkModificationService.moveModifications( + sourceContainerId, + targetContainerId, + beforeUuid, + Pair.of(modificationUuidList, applicationContexts), + isTargetInDifferentNodeTree); + + if (result != null && originNodeUuid != null) { + Set allMovedUuids = networkModificationService.expandToLeafUuids(result.modificationUuids()); + rootNetworkNodeInfoService.moveModificationsToExclude(originNodeUuid, targetNodeUuid, result.modificationUuids()); + } + if (result != null && isTargetInDifferentNodeTree) { + emitNetworkModificationImpactsForAllRootNetworks(result.modificationResults(), studyEntity, targetNodeUuid); } } finally { notificationService.emitEndModificationEquipmentNotification(studyUuid, targetNodeUuid, childrenUuids); - if (isTargetDifferentNode) { + if (isTargetDifferentNode && originNodeUuid != null) { notificationService.emitEndModificationEquipmentNotification(studyUuid, originNodeUuid, originNodeChildrenUuids); } - } - 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); } diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 8394d4b7a..f04211904 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -2016,6 +2016,7 @@ 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) + // Same-container reorder: no source/target params -> controller resolves both to the node's group. mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-modification/{modificationID}", studyNameUserIdUuid, modificationNodeUuid, modification1).header(USER_ID_HEADER, "userId")) .andExpect(status().isOk()); @@ -2034,8 +2035,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}/network-modification/{modificationID}", + studyNameUserIdUuid, modificationNodeUuid, modification1) + .queryParam("beforeUuid", modification2.toString()) + .header(USER_ID_HEADER, "userId")) .andExpect(status().isOk()); checkUpdateStatusMessagesReceived(studyNameUserIdUuid, modificationNodeUuid, output); checkEquipmentUpdatingMessagesReceived(studyNameUserIdUuid, modificationNodeUuid); @@ -3235,7 +3238,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(); @@ -3243,23 +3246,24 @@ 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; - - // --- 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}/network-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()); @@ -3267,42 +3271,63 @@ 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())); + 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())), 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. + 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}/network-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())), expectedMoveBodyJson); - // --- 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\":[]}"))); + + 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}/composite-sub-modification/{modificationUuid}", + mockMvc.perform(put("/v1/studies/{studyUuid}/nodes/{nodeUuid}/network-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())), expectedMoveBodyJson); + WireMockUtilsCriteria.verifyGetRequest(wireMockServer, "/v1/network-composite-modifications/children-uuids", Map.of( + "uuids", WireMock.matching(".*")), 1); } @Test diff --git a/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java b/src/test/java/org/gridsuite/study/server/RebuildNodeServiceTest.java index 16f020e71..066125b2f 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(), UUID.randomUUID(), null, userId); verify(studyService, times(1)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, 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(), UUID.randomUUID(), null, userId); verify(studyService, times(0)).buildNode(studyUuid, node1Uuid, rootNetworkUuid, userId); } 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 619111376..8b81de9cb 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 @@ -782,9 +783,11 @@ 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, firstNodeUuid, firstNodeUuid, 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,11 +805,12 @@ void testMoveExcludedModification() { .lastGroupApplicationStatus(NetworkModificationResult.ApplicationStatus.ALL_OK) .networkImpacts(List.of()) .build())))).when(networkModificationService).moveModifications(any(), any(), any(), any(), eq(true)); + // Expands the moved UUIDs to leaves before remapping exclusions: // stub expandToLeafUuids to return exactly the moved UUIDs (no composites involved here) Mockito.doReturn(new HashSet<>(modificationsToMove)).when(networkModificationService).expandToLeafUuids(modificationsToMove); - studyService.moveNetworkModifications(studyUuid, secondNodeUuid, firstNodeUuid, 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(); 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..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,14 +73,6 @@ 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());