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 e1fdaa6b4..74385437f 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -691,6 +691,21 @@ public ResponseEntity moveOrCopyModifications(@PathVariable("studyUuid") U return ResponseEntity.ok().build(); } + @PostMapping(value = "/studies/{studyUuid}/nodes/{nodeUuid}/composite-modification", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "For a list of network modifications passed in body, merge them into a new composite modification") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created.")}) + public ResponseEntity mergeModificationsIntoNewComposite( + @PathVariable("studyUuid") UUID studyUuid, + @PathVariable("nodeUuid") UUID nodeUuid, + @RequestBody List modificationsUuids, + @RequestHeader(HEADER_USER_ID) String userId) { + studyService.assertIsStudyAndNodeExist(studyUuid, nodeUuid); + studyService.assertCanUpdateNodeInStudy(studyUuid, nodeUuid); + studyService.assertNoBlockedNodeInStudy(studyUuid, nodeUuid); + UUID newCompositeUuid = rebuildNodeService.mergeModificationsIntoComposite(studyUuid, nodeUuid, modificationsUuids, userId); + return ResponseEntity.ok().body(newCompositeUuid); + } + /** * @param modificationsToInsert pair of the composite uuid and its name */ 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..8bea7f153 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationService.java @@ -300,6 +300,24 @@ public NetworkModificationsResult insertCompositeModifications(UUID groupUuid, ).getBody(); } + public UUID mergeModificationsIntoComposite(@NonNull List modificationsUuids) { + var path = UriComponentsBuilder.fromPath(COMPOSITE_PATH); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> httpEntity = new HttpEntity<>( + modificationsUuids, + headers + ); + + return restTemplate.exchange( + getNetworkModificationServerURI(false) + path.toUriString(), + HttpMethod.POST, + httpEntity, + UUID.class + ).getBody(); + } + private NetworkModificationsResult handleModifications(UUID groupUuid, UUID originGroupUuid, ModificationsActionType action, Pair, List> modificationContextInfos) { var path = UriComponentsBuilder.fromPath(GROUP_PATH) 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..f61c53b43 100644 --- a/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java +++ b/src/main/java/org/gridsuite/study/server/service/RebuildNodeService.java @@ -15,6 +15,7 @@ import java.util.Set; import java.util.UUID; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Collectors; @Service @@ -100,6 +101,23 @@ public void moveSubModification( modificationUuid, beforeUuid, userId)); } + public UUID mergeModificationsIntoComposite(UUID studyUuid, UUID nodeUuid, List modificationsUuids, String userId) { + return handleRebuildNodeSupply( + studyUuid, + nodeUuid, + userId, + () -> { + studyService.invalidateNodeTreeWhenMoveModification(studyUuid, nodeUuid); + UUID compositeUuid; + try { + compositeUuid = studyService.mergeModificationsIntoComposite(studyUuid, nodeUuid, modificationsUuids, userId); + } finally { + studyService.unblockNodeTree(studyUuid, nodeUuid); + } + return compositeUuid; + }); + } + private void handleMoveNetworkSubmodification(@NonNull UUID studyUuid, @NonNull UUID nodeUuid, UUID sourceCompositeUuid, @@ -133,14 +151,24 @@ private void handleRebuildNode(UUID studyUuid, UUID nodeUuid, String userId, Run handleRebuildNode(studyUuid, nodeUuid, nodeUuid, userId, action); } + private T handleRebuildNodeSupply(UUID studyUuid, UUID nodeUuid, String userId, Supplier action) { + return handleRebuildNodeSupply(studyUuid, nodeUuid, nodeUuid, userId, action); + } + private void handleRebuildNode(UUID studyUuid, UUID node1Uuid, UUID node2Uuid, String userId, Runnable action) { + handleRebuildNodeSupply(studyUuid, node1Uuid, node2Uuid, userId, () -> { + action.run(); + return null; + }); + } + + private T handleRebuildNodeSupply(UUID studyUuid, UUID node1Uuid, UUID node2Uuid, String userId, Supplier action) { // if node 1 and 2 are in the same "subtree", rebuild only the highest one - otherwise, rebuild both List nodesToReBuild = networkModificationTreeService.getHighestNodeUuids(node1Uuid, node2Uuid).stream() .filter(Predicate.not(networkModificationTreeService::isRootOrConstructionNode)).toList(); if (nodesToReBuild.isEmpty()) { - action.run(); - return; + return action.get(); } Map> rootNetworkUuidsByNodeBuilt = nodesToReBuild.stream().collect(Collectors.toMap( @@ -148,7 +176,7 @@ private void handleRebuildNode(UUID studyUuid, UUID node1Uuid, UUID node2Uuid, S nodeUuid -> getRootNetworkWhereNodeIsBuilt(studyUuid, nodeUuid) )); - action.run(); + T result = action.get(); rootNetworkUuidsByNodeBuilt.forEach((nodeUuid, rootNetworkUuids) -> rootNetworkUuids.stream().forEach(rootNetworkUuid -> @@ -160,6 +188,8 @@ private void handleRebuildNode(UUID studyUuid, UUID node1Uuid, UUID node2Uuid, S ) ) ); + + return result; } private Set getRootNetworkWhereNodeIsBuilt(UUID studyUuid, UUID nodeUuid) { 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 7f1002192..8e7bdf6bd 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2449,6 +2449,25 @@ public void duplicateNetworkModifications( userId); } + @Transactional + public UUID mergeModificationsIntoComposite( + UUID targetStudyUuid, + UUID targetNodeUuid, + List modificationsUuids, + String userId) { + UUID newCompositeUuid; + List childrenUuids = networkModificationTreeService.getChildrenUuids(targetNodeUuid); + notificationService.emitStartModificationEquipmentNotification(targetStudyUuid, targetNodeUuid, childrenUuids, NotificationService.MODIFICATIONS_UPDATING_IN_PROGRESS); + try { + checkStudyContainsNode(targetStudyUuid, targetNodeUuid); + newCompositeUuid = networkModificationService.mergeModificationsIntoComposite(modificationsUuids); + } finally { + notificationService.emitEndModificationEquipmentNotification(targetStudyUuid, targetNodeUuid, childrenUuids); + } + notificationService.emitElementUpdated(targetStudyUuid, userId); + return newCompositeUuid; + } + @Transactional public void insertCompositeNetworkModifications( UUID targetStudyUuid, diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index e1c911055..24d818e8f 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -2116,6 +2116,53 @@ void testInsertComposite() throws Exception { expectedBody); } + @Test + void testMergeModificationsIntoNewComposite() throws Exception { + String userId = "userId"; + StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, "UCTE"); + UUID studyUuid = studyEntity.getId(); + UUID rootNodeUuid = getRootNode(studyUuid).getId(); + + NetworkModificationNode node1 = createNetworkModificationNode(studyUuid, rootNodeUuid, + UUID.randomUUID(), VARIANT_ID, "New node 1", userId); + UUID nodeUuid1 = node1.getId(); + + UUID modification1 = UUID.randomUUID(); + UUID modification2 = UUID.randomUUID(); + List modificationUuids = List.of(modification1, modification2); + String modificationsData = mapper.writeValueAsString(modificationUuids); + + UUID newCompositeUuid = UUID.randomUUID(); + + wireMockServer.stubFor(WireMock.post(WireMock.urlPathMatching("/v1/network-composite-modifications/composite-modification")) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(newCompositeUuid)) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))); + + MvcResult mvcResult = mockMvc.perform(post("/v1/studies/{studyUuid}/nodes/{nodeUuid}/composite-modification", + studyUuid, nodeUuid1) + .contentType(MediaType.APPLICATION_JSON) + .content(modificationsData) + .header(USER_ID_HEADER, userId)) + .andExpect(status().isOk()) + .andReturn(); + + UUID resultUuid = mapper.readValue(mvcResult.getResponse().getContentAsString(), UUID.class); + assertEquals(newCompositeUuid, resultUuid); + + checkUpdateStatusMessagesReceived(studyUuid, nodeUuid1, output); + checkEquipmentUpdatingMessagesReceived(studyUuid, nodeUuid1); + checkEquipmentUpdatingFinishedMessagesReceived(studyUuid, nodeUuid1); + checkElementUpdatedMessageSent(studyUuid, userId); + + WireMockUtilsCriteria.verifyPostRequest( + wireMockServer, + "/v1/network-composite-modifications/composite-modification", + Map.of(), + 1 + ); + } + @Test void testDuplicateModification() throws Exception { String userId = "userId";