From ac8f4d068212a3ec6be24ea32d325283c4dfbb34 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 10:46:00 +0200 Subject: [PATCH 01/13] Add unmount study endpoint --- .../controller/SupervisionController.java | 19 +++++++--- .../server/service/RootNetworkService.java | 37 +++++++++++++------ .../study/server/service/StudyService.java | 18 ++++++++- .../server/service/SupervisionService.java | 20 +++++++++- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java b/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java index 30645dbfea..eb7a4f588f 100644 --- a/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java +++ b/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java @@ -14,20 +14,19 @@ import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.gridsuite.study.server.StudyApi; +import org.gridsuite.study.server.dto.ComputationType; import org.gridsuite.study.server.dto.supervision.SupervisionStudyInfos; +import org.gridsuite.study.server.elasticsearch.EquipmentInfosService; import org.gridsuite.study.server.service.RootNetworkService; import org.gridsuite.study.server.service.StudyService; import org.gridsuite.study.server.service.SupervisionService; - -import java.util.List; -import java.util.UUID; - -import org.gridsuite.study.server.dto.ComputationType; -import org.gridsuite.study.server.elasticsearch.EquipmentInfosService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.UUID; + /** * @author Hugo Marcellin */ @@ -180,4 +179,12 @@ public ResponseEntity invalidateAllNodesBuilds(@PathVariable("studyUuid") return ResponseEntity.ok().build(); } + @DeleteMapping(value = "/studies/{studyUuid}/unmount") + @Operation(summary = "Invalidate nodes builds and delete root node network") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "study has been unmounted")}) + public ResponseEntity unmountStudy(@PathVariable("studyUuid") UUID studyUuid) { + supervisionService.unmountStudy(studyUuid); + return ResponseEntity.ok().build(); + } + } diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java index 3c1e7b84a9..01ef560f24 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java @@ -10,17 +10,17 @@ import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.network.store.model.VariantInfos; import lombok.NonNull; -import org.gridsuite.study.server.error.StudyException; import org.gridsuite.study.server.dto.CaseInfos; import org.gridsuite.study.server.dto.NetworkInfos; import org.gridsuite.study.server.dto.RootNetworkAction; import org.gridsuite.study.server.dto.RootNetworkInfos; import org.gridsuite.study.server.elasticsearch.EquipmentInfosService; +import org.gridsuite.study.server.error.StudyException; import org.gridsuite.study.server.repository.StudyEntity; -import org.gridsuite.study.server.repository.rootnetwork.RootNetworkRequestEntity; -import org.gridsuite.study.server.repository.rootnetwork.RootNetworkRequestRepository; import org.gridsuite.study.server.repository.rootnetwork.RootNetworkEntity; import org.gridsuite.study.server.repository.rootnetwork.RootNetworkRepository; +import org.gridsuite.study.server.repository.rootnetwork.RootNetworkRequestEntity; +import org.gridsuite.study.server.repository.rootnetwork.RootNetworkRequestRepository; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,7 +30,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.gridsuite.study.server.error.StudyBusinessErrorCode.*; +import static org.gridsuite.study.server.error.StudyBusinessErrorCode.MAXIMUM_ROOT_NETWORK_BY_STUDY_REACHED; +import static org.gridsuite.study.server.error.StudyBusinessErrorCode.MAXIMUM_TAG_LENGTH_EXCEEDED; +import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_ALLOWED; +import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND; /** * @author Le Saulnier Kevin @@ -235,18 +238,28 @@ public void deleteRootNetworks(StudyEntity studyEntity, Stream rootNetwork } public void deleteRootNetworks(StudyEntity studyEntity, List rootNetworksInfos) { - deleteRootNetworkRemoteInfos(rootNetworksInfos); + deleteRootNetworkRemoteInfos(rootNetworksInfos, true); studyEntity.deleteRootNetworks(rootNetworksInfos.stream().map(RootNetworkInfos::getId).collect(Collectors.toSet())); } - public void deleteRootNetworkRemoteInfos(List rootNetworkInfos) { - CompletableFuture.allOf( - // delete remote data ids set in root network - studyServerExecutionService.runAsync(() -> reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList())), - studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()).filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes)), - studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()).filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork)), - studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()).filter(Objects::nonNull).forEach(caseService::deleteCase)) + public void deleteRootNetworkRemoteInfos(List rootNetworkInfos, boolean deleteCase) { + // delete remote data ids set in root network + CompletableFuture.allOf(Stream.of( + studyServerExecutionService.runAsync(() -> + reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList())), + studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) + .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes)), + studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) + .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork)), + !deleteCase ? null : studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()) + .filter(Objects::nonNull).forEach(caseService::deleteCase)) + ) + .filter(Objects::nonNull) + .toArray(CompletableFuture[]::new) ); // delete remote data ids set in root network node infos 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 c7ff1557e4..16ce873112 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -610,7 +610,7 @@ public void deleteStudyIfNotCreationInProgress(UUID studyUuid) { startTime.set(System.nanoTime()); // delete all distant resources linked to rootNetworks - rootNetworkService.deleteRootNetworkRemoteInfos(deleteStudyInfos.getRootNetworkInfosList()); + rootNetworkService.deleteRootNetworkRemoteInfos(deleteStudyInfos.getRootNetworkInfosList(), true); // delete all distant resources linked to nodes studyServerExecutionService.runAsync(() -> deleteStudyInfos.getModificationGroupUuids().stream().filter(Objects::nonNull).forEach(networkModificationService::deleteModifications)); @@ -3697,7 +3697,7 @@ public NetworkModificationNode createSequence(UUID studyUuid, UUID parentNodeUui return newParentNode; } - private List getStudyRootNetworks(UUID studyUuid) { + public List getStudyRootNetworks(UUID studyUuid) { StudyEntity studyEntity = getStudy(studyUuid); return studyEntity.getRootNetworks(); } @@ -3912,4 +3912,18 @@ private List getCurrentLimitViolations(UUID nodeUuid .map(l -> new CurrentLimitViolationInfos(l.getSubjectId(), null)) .toList(); } + + @Transactional + public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { + StudyEntity study = getStudy(studyUuid); + RootNetworkEntity rootNetwork = rootNetworkService.getRootNetwork(rootNetworkUuid) + .orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); + + invalidateNodeTree(studyUuid, networkModificationTreeService.getStudyRootNodeUuid(studyUuid), + rootNetworkUuid, InvalidateNodeTreeParameters.ALL_WITH_BLOCK_NODES); + rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); + updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); + + notificationService.emitRootNetworksUpdated(studyUuid); + } } diff --git a/src/main/java/org/gridsuite/study/server/service/SupervisionService.java b/src/main/java/org/gridsuite/study/server/service/SupervisionService.java index 2871a5a5f6..d5d0e6794a 100644 --- a/src/main/java/org/gridsuite/study/server/service/SupervisionService.java +++ b/src/main/java/org/gridsuite/study/server/service/SupervisionService.java @@ -13,6 +13,7 @@ import org.gridsuite.study.server.elasticsearch.EquipmentInfosService; import org.gridsuite.study.server.elasticsearch.StudyInfosService; import org.gridsuite.study.server.networkmodificationtree.entities.RootNetworkNodeInfoEntity; +import org.gridsuite.study.server.notification.NotificationService; import org.gridsuite.study.server.repository.StudyEntity; import org.gridsuite.study.server.repository.StudyRepository; import org.gridsuite.study.server.repository.rootnetwork.RootNetworkEntity; @@ -84,6 +85,10 @@ public class SupervisionService { private final RootNetworkService rootNetworkService; + private final NotificationService notificationService; + + private static final String SUPERVISION_USER = "Supervision"; + public SupervisionService(StudyService studyService, NetworkModificationTreeService networkModificationTreeService, RootNetworkNodeInfoRepository rootNetworkNodeInfoRepository, @@ -102,7 +107,8 @@ public SupervisionService(StudyService studyService, ElasticsearchOperations elasticsearchOperations, StudyInfosService studyInfosService, RootNetworkService rootNetworkService, - StudyRepository studyRepository) { + StudyRepository studyRepository, + NotificationService notificationService) { this.studyService = studyService; this.networkModificationTreeService = networkModificationTreeService; this.rootNetworkNodeInfoRepository = rootNetworkNodeInfoRepository; @@ -122,6 +128,7 @@ public SupervisionService(StudyService studyService, this.studyInfosService = studyInfosService; this.rootNetworkService = rootNetworkService; this.studyRepository = studyRepository; + this.notificationService = notificationService; } @Transactional @@ -363,6 +370,17 @@ public void unbuildAllNodes(UUID studyUuid) { LOGGER.trace("Nodes builds deletion for study {} in : {} seconds", studyUuid, TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime.get())); } + @Transactional + public void unmountStudy(UUID studyUuid) { + AtomicReference startTime = new AtomicReference<>(); + startTime.set(System.nanoTime()); + studyService.getStudyRootNetworks(studyUuid).forEach(rootNetwork -> + studyService.unmountStudyRootNetwork(studyUuid, rootNetwork.getId()) + ); + notificationService.emitElementUpdated(studyUuid, SUPERVISION_USER); + LOGGER.trace("Study {} nodes builds deleted and root node unmounted in : {} seconds", studyUuid, TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime.get())); + } + @Transactional public void recreateStudyIndices() { recreateIndex(CreatedStudyBasicInfos.class); From 34f6be55166e8f085473732a01541352b9f0b036 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 11:57:48 +0200 Subject: [PATCH 02/13] Reuse code --- .../study/server/service/StudyService.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 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 16ce873112..d3a03403d1 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1890,11 +1890,17 @@ public void unbuildStudyNode(@NonNull UUID studyUuid, @NonNull UUID nodeUuid, @N @Transactional public void unbuildNodeTree(@NonNull UUID studyUuid, UUID rootNodeUuid, boolean withBlockNodes) { - InvalidateNodeTreeParameters invalidateNodeTreeParameters = withBlockNodes ? InvalidateNodeTreeParameters.ALL_WITH_BLOCK_NODES : InvalidateNodeTreeParameters.ALL; - List> futures = getStudy(studyUuid).getRootNetworks().stream().map(rn -> - studyServerExecutionService.runAsync(() -> invalidateNodeTree(studyUuid, rootNodeUuid, rn.getId(), invalidateNodeTreeParameters)) - ).toList(); + doUnbuildNodeTree(studyUuid, rootNodeUuid, withBlockNodes); + } + private void doUnbuildNodeTree(UUID studyUuid, UUID rootNodeUuid, boolean withBlockNodes) { + InvalidateNodeTreeParameters params = withBlockNodes + ? InvalidateNodeTreeParameters.ALL_WITH_BLOCK_NODES + : InvalidateNodeTreeParameters.ALL; + List> futures = getStudy(studyUuid).getRootNetworks().stream() + .map(rn -> studyServerExecutionService.runAsync(() -> + invalidateNodeTree(studyUuid, rootNodeUuid, rn.getId(), params))) + .toList(); CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } @@ -3919,8 +3925,7 @@ public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { RootNetworkEntity rootNetwork = rootNetworkService.getRootNetwork(rootNetworkUuid) .orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - invalidateNodeTree(studyUuid, networkModificationTreeService.getStudyRootNodeUuid(studyUuid), - rootNetworkUuid, InvalidateNodeTreeParameters.ALL_WITH_BLOCK_NODES); + doUnbuildNodeTree(studyUuid, rootNetworkUuid, true); rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); From d9d666627ebcb29a55830a7fed2e31bef730be85 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 13:26:08 +0200 Subject: [PATCH 03/13] Add integration test --- .../study/server/service/StudyService.java | 2 +- .../study/server/SupervisionControllerTest.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 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 d3a03403d1..3d1703e0d4 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -3925,7 +3925,7 @@ public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { RootNetworkEntity rootNetwork = rootNetworkService.getRootNetwork(rootNetworkUuid) .orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - doUnbuildNodeTree(studyUuid, rootNetworkUuid, true); + doUnbuildNodeTree(studyUuid, networkModificationTreeService.getStudyRootNodeUuid(studyUuid), true); rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 70c885e9aa..686d71f628 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -302,4 +302,19 @@ void testSupervisionStudiesBasicData() throws Exception { assertEquals(1, infos.get(0).getCaseUuids().size()); assertEquals(1, infos.get(0).getRootNetworkInfos().size()); } + + @Test + void testUnmountStudy() throws Exception { + initStudy(); + + Mockito.doNothing().when(networkService).deleteVariants(eq(NETWORK_UUID), any()); + + mockMvc.perform(delete("/v1/supervision/studies/{studyUuid}/unmount", STUDY_UUID)) + .andExpect(status().isOk()); + + Mockito.verify(networkService, Mockito.times(1)).deleteVariants(eq(NETWORK_UUID), any()); + Mockito.verify(rootNetworkService, Mockito.times(1)) + .deleteRootNetworkRemoteInfos(any(), eq(false)); + assertIndexationStatus(STUDY_UUID, RootNetworkIndexationStatus.NOT_INDEXED.name()); + } } From 5a68950f917b3074158c178f250cfa7572fde1eb Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 13:28:53 +0200 Subject: [PATCH 04/13] Add comments --- .../gridsuite/study/server/SupervisionControllerTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 686d71f628..3efe1855ba 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -312,9 +312,14 @@ void testUnmountStudy() throws Exception { mockMvc.perform(delete("/v1/supervision/studies/{studyUuid}/unmount", STUDY_UUID)) .andExpect(status().isOk()); + // Tree was unbuilt: variants dropped from network store Mockito.verify(networkService, Mockito.times(1)).deleteVariants(eq(NETWORK_UUID), any()); + + // Remote root-network data was deleted, but the case was preserved (deleteCase = false) Mockito.verify(rootNetworkService, Mockito.times(1)) .deleteRootNetworkRemoteInfos(any(), eq(false)); + + // Indexation flipped to NOT_INDEXED so the auto-detect path will reimport on reopen assertIndexationStatus(STUDY_UUID, RootNetworkIndexationStatus.NOT_INDEXED.name()); } } From 5a91b11f5d30b06a316a85c87fb9f786ab059284 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 14:00:02 +0200 Subject: [PATCH 05/13] Unblock nodes after unmounting study --- .../gridsuite/study/server/service/StudyService.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 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 3d1703e0d4..9daaa007b4 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -3925,9 +3925,14 @@ public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { RootNetworkEntity rootNetwork = rootNetworkService.getRootNetwork(rootNetworkUuid) .orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - doUnbuildNodeTree(studyUuid, networkModificationTreeService.getStudyRootNodeUuid(studyUuid), true); - rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); - updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); + var rootNode = networkModificationTreeService.getStudyRootNodeUuid(studyUuid); + try { + doUnbuildNodeTree(studyUuid, rootNode, true); + rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); + updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); + } finally { + unblockNodeTree(studyUuid, rootNode); + } notificationService.emitRootNetworksUpdated(studyUuid); } From ea29a3540792505341492be18d0e2bc4b4ef841f Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 14:03:02 +0200 Subject: [PATCH 06/13] Tweak --- .../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 9daaa007b4..b6847a7d5c 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -3931,7 +3931,7 @@ public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); } finally { - unblockNodeTree(studyUuid, rootNode); + networkModificationTreeService.unblockNodeTree(rootNetworkUuid, rootNode); } notificationService.emitRootNetworksUpdated(studyUuid); From 9e311186f283d39c6f4f7ea692f94020399e67e7 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 Apr 2026 14:18:52 +0200 Subject: [PATCH 07/13] Add notification element updated when launching computations --- .../gridsuite/study/server/service/StudyService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 b6847a7d5c..792969e741 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1027,6 +1027,7 @@ public void rerunLoadflow(UUID studyUuid, UUID nodeUuid, UUID rootNetworkUuid, U networkModificationTreeService.blockNode(rootNetworkUuid, nodeUuid); handleLoadflowRequest(studyEntity, nodeUuid, rootNetworkUuid, loadflowResultUuid, withRatioTapChangers, userId); } + notificationService.emitElementUpdated(studyEntity.getId(), userId); } @Transactional @@ -1079,6 +1080,7 @@ private void handleLoadflowRequest(StudyEntity studyEntity, UUID nodeUuid, UUID rootNetworkNodeInfoService.updateLoadflowResultUuid(nodeUuid, rootNetworkUuid, result, withRatioTapChangers); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, LOAD_FLOW.getUpdateStatusType()); + notificationService.emitElementUpdated(studyEntity.getId(), userId); } public UUID exportNetwork(UUID studyUuid, UUID nodeUuid, UUID rootNetworkUuid, NodeExportInfos exportInfos, String format, String userId, String parametersJson) { @@ -1383,6 +1385,7 @@ private UUID handleSecurityAnalysisRequest(StudyEntity study, UUID nodeUuid, UUI new ReportInfos(saReportUuid, nodeUuid), receiver, userId); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, SECURITY_ANALYSIS); notificationService.emitStudyChanged(study.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_SECURITY_ANALYSIS_STATUS); + notificationService.emitElementUpdated(study.getId(), userId); return result; } @@ -2691,6 +2694,7 @@ private UUID handleSensitivityAnalysisRequest(StudyEntity study, UUID nodeUuid, updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, SENSITIVITY_ANALYSIS); notificationService.emitStudyChanged(study.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); + notificationService.emitElementUpdated(study.getId(), userId); return result; } @@ -2716,6 +2720,7 @@ private UUID handleShortCircuitRequest(StudyEntity studyEntity, UUID nodeUuid, U updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, computationType); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, busId.isEmpty() ? NotificationService.UPDATE_TYPE_SHORT_CIRCUIT_STATUS : NotificationService.UPDATE_TYPE_ONE_BUS_SHORT_CIRCUIT_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -2748,6 +2753,7 @@ private UUID handleVoltageInitRequest(StudyEntity studyEntity, UUID nodeUuid, UU updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, VOLTAGE_INITIALIZATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_VOLTAGE_INIT_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -3084,6 +3090,7 @@ private UUID handleDynamicSimulationRequest(StudyEntity studyEntity, UUID nodeUu // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicSimulationResultUuid, DYNAMIC_SIMULATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_SIMULATION_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicSimulationResultUuid; } @@ -3194,6 +3201,7 @@ private UUID handleDynamicSecurityAnalysisRequest(StudyEntity studyEntity, UUID // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicSecurityAnalysisResultUuid, DYNAMIC_SECURITY_ANALYSIS); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_SECURITY_ANALYSIS_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicSecurityAnalysisResultUuid; } @@ -3302,6 +3310,7 @@ private UUID handleDynamicMarginCalculationRequest(StudyEntity studyEntity, UUID // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicMarginCalculationResultUuid, DYNAMIC_MARGIN_CALCULATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_MARGIN_CALCULATION_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicMarginCalculationResultUuid; } @@ -3560,6 +3569,7 @@ private UUID handleStateEstimationRequest(StudyEntity studyEntity, UUID nodeUuid UUID result = stateEstimationService.runStateEstimation(networkUuid, variantId, studyEntity.getStateEstimationParametersUuid(), new ReportInfos(reportUuid, nodeUuid), receiver, userId, debug); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, STATE_ESTIMATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_STATE_ESTIMATION_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -3584,6 +3594,7 @@ private UUID handlePccMinRequest(StudyEntity studyEntity, UUID nodeUuid, UUID ro UUID result = pccMinService.runPccMin(networkUuid, variantId, runPccMinParametersInfos, new ReportInfos(reportUuid, nodeUuid), receiver, userId); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, PCC_MIN); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_PCC_MIN_STATUS); + notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } From 2eeeccbcb72f66accabdb488c0ce1d8ce291a733 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 16 Apr 2026 17:43:53 +0200 Subject: [PATCH 08/13] Rework deleteRootNetworkRemoteInfos --- .../server/service/RootNetworkService.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java index 01ef560f24..ed9636fb94 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java @@ -245,25 +245,28 @@ public void deleteRootNetworks(StudyEntity studyEntity, List r public void deleteRootNetworkRemoteInfos(List rootNetworkInfos, boolean deleteCase) { // delete remote data ids set in root network - CompletableFuture.allOf(Stream.of( - studyServerExecutionService.runAsync(() -> - reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList())), - studyServerExecutionService.runAsync(() -> - rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes)), - studyServerExecutionService.runAsync(() -> - rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork)), - !deleteCase ? null : studyServerExecutionService.runAsync(() -> - rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()) - .filter(Objects::nonNull).forEach(caseService::deleteCase)) - ) - .filter(Objects::nonNull) - .toArray(CompletableFuture[]::new) - ); + List> futures = new ArrayList<>(); + futures.add(studyServerExecutionService.runAsync(() -> + reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList()))); + futures.add(studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) + .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes))); + futures.add(studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) + .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork))); + if (deleteCase) { + futures.add(studyServerExecutionService.runAsync(() -> + rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()) + .filter(Objects::nonNull).forEach(caseService::deleteCase))); + } + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); // delete remote data ids set in root network node infos - rootNetworkNodeInfoService.deleteRootNetworkNodeRemoteInfos(rootNetworkInfos.stream().map(RootNetworkInfos::getRootNetworkNodeInfos).filter(Objects::nonNull).flatMap(Collection::stream).toList()); + rootNetworkNodeInfoService.deleteRootNetworkNodeRemoteInfos(rootNetworkInfos.stream() + .map(RootNetworkInfos::getRootNetworkNodeInfos) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .toList()); } public Optional getRootNetworkRequest(UUID rootNetworkUuid) { From 39712b7bec00946e071c984e3315a1483aa55503 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 16 Apr 2026 17:45:12 +0200 Subject: [PATCH 09/13] Reword API operation desc --- .../study/server/controller/SupervisionController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java b/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java index eb7a4f588f..babab43106 100644 --- a/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java +++ b/src/main/java/org/gridsuite/study/server/controller/SupervisionController.java @@ -180,7 +180,7 @@ public ResponseEntity invalidateAllNodesBuilds(@PathVariable("studyUuid") } @DeleteMapping(value = "/studies/{studyUuid}/unmount") - @Operation(summary = "Invalidate nodes builds and delete root node network") + @Operation(summary = "Invalidate built nodes and delete root node network") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "study has been unmounted")}) public ResponseEntity unmountStudy(@PathVariable("studyUuid") UUID studyUuid) { supervisionService.unmountStudy(studyUuid); From 2ac9bc213e20144c7df85c78764ba00e7ada6393 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 17 Apr 2026 17:01:07 +0200 Subject: [PATCH 10/13] Remove completableFuture allOf --- .../server/service/RootNetworkService.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java index ed9636fb94..4a06796431 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java @@ -26,7 +26,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; -import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -245,21 +244,20 @@ public void deleteRootNetworks(StudyEntity studyEntity, List r public void deleteRootNetworkRemoteInfos(List rootNetworkInfos, boolean deleteCase) { // delete remote data ids set in root network - List> futures = new ArrayList<>(); - futures.add(studyServerExecutionService.runAsync(() -> - reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList()))); - futures.add(studyServerExecutionService.runAsync(() -> + studyServerExecutionService.runAsync(() -> + reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList())); + studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes))); - futures.add(studyServerExecutionService.runAsync(() -> + .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes)); + studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork))); + .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork)); if (deleteCase) { - futures.add(studyServerExecutionService.runAsync(() -> + studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()) - .filter(Objects::nonNull).forEach(caseService::deleteCase))); + .filter(Objects::nonNull).forEach(caseService::deleteCase)); } - CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); + // delete remote data ids set in root network node infos rootNetworkNodeInfoService.deleteRootNetworkNodeRemoteInfos(rootNetworkInfos.stream() From 711c6344906c11b5c6d78e99013bec7cd6ed0006 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 21 Apr 2026 11:04:38 +0200 Subject: [PATCH 11/13] Remove new modifications date update, to be done later --- .../gridsuite/study/server/service/StudyService.java | 11 ----------- 1 file changed, 11 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 792969e741..b6847a7d5c 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1027,7 +1027,6 @@ public void rerunLoadflow(UUID studyUuid, UUID nodeUuid, UUID rootNetworkUuid, U networkModificationTreeService.blockNode(rootNetworkUuid, nodeUuid); handleLoadflowRequest(studyEntity, nodeUuid, rootNetworkUuid, loadflowResultUuid, withRatioTapChangers, userId); } - notificationService.emitElementUpdated(studyEntity.getId(), userId); } @Transactional @@ -1080,7 +1079,6 @@ private void handleLoadflowRequest(StudyEntity studyEntity, UUID nodeUuid, UUID rootNetworkNodeInfoService.updateLoadflowResultUuid(nodeUuid, rootNetworkUuid, result, withRatioTapChangers); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, LOAD_FLOW.getUpdateStatusType()); - notificationService.emitElementUpdated(studyEntity.getId(), userId); } public UUID exportNetwork(UUID studyUuid, UUID nodeUuid, UUID rootNetworkUuid, NodeExportInfos exportInfos, String format, String userId, String parametersJson) { @@ -1385,7 +1383,6 @@ private UUID handleSecurityAnalysisRequest(StudyEntity study, UUID nodeUuid, UUI new ReportInfos(saReportUuid, nodeUuid), receiver, userId); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, SECURITY_ANALYSIS); notificationService.emitStudyChanged(study.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_SECURITY_ANALYSIS_STATUS); - notificationService.emitElementUpdated(study.getId(), userId); return result; } @@ -2694,7 +2691,6 @@ private UUID handleSensitivityAnalysisRequest(StudyEntity study, UUID nodeUuid, updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, SENSITIVITY_ANALYSIS); notificationService.emitStudyChanged(study.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); - notificationService.emitElementUpdated(study.getId(), userId); return result; } @@ -2720,7 +2716,6 @@ private UUID handleShortCircuitRequest(StudyEntity studyEntity, UUID nodeUuid, U updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, computationType); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, busId.isEmpty() ? NotificationService.UPDATE_TYPE_SHORT_CIRCUIT_STATUS : NotificationService.UPDATE_TYPE_ONE_BUS_SHORT_CIRCUIT_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -2753,7 +2748,6 @@ private UUID handleVoltageInitRequest(StudyEntity studyEntity, UUID nodeUuid, UU updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, VOLTAGE_INITIALIZATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_VOLTAGE_INIT_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -3090,7 +3084,6 @@ private UUID handleDynamicSimulationRequest(StudyEntity studyEntity, UUID nodeUu // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicSimulationResultUuid, DYNAMIC_SIMULATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_SIMULATION_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicSimulationResultUuid; } @@ -3201,7 +3194,6 @@ private UUID handleDynamicSecurityAnalysisRequest(StudyEntity studyEntity, UUID // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicSecurityAnalysisResultUuid, DYNAMIC_SECURITY_ANALYSIS); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_SECURITY_ANALYSIS_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicSecurityAnalysisResultUuid; } @@ -3310,7 +3302,6 @@ private UUID handleDynamicMarginCalculationRequest(StudyEntity studyEntity, UUID // update result uuid and notification updateComputationResultUuid(nodeUuid, rootNetworkUuid, dynamicMarginCalculationResultUuid, DYNAMIC_MARGIN_CALCULATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_DYNAMIC_MARGIN_CALCULATION_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return dynamicMarginCalculationResultUuid; } @@ -3569,7 +3560,6 @@ private UUID handleStateEstimationRequest(StudyEntity studyEntity, UUID nodeUuid UUID result = stateEstimationService.runStateEstimation(networkUuid, variantId, studyEntity.getStateEstimationParametersUuid(), new ReportInfos(reportUuid, nodeUuid), receiver, userId, debug); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, STATE_ESTIMATION); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_STATE_ESTIMATION_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } @@ -3594,7 +3584,6 @@ private UUID handlePccMinRequest(StudyEntity studyEntity, UUID nodeUuid, UUID ro UUID result = pccMinService.runPccMin(networkUuid, variantId, runPccMinParametersInfos, new ReportInfos(reportUuid, nodeUuid), receiver, userId); updateComputationResultUuid(nodeUuid, rootNetworkUuid, result, PCC_MIN); notificationService.emitStudyChanged(studyEntity.getId(), nodeUuid, rootNetworkUuid, NotificationService.UPDATE_TYPE_PCC_MIN_STATUS); - notificationService.emitElementUpdated(studyEntity.getId(), userId); return result; } From 0d487920b066d0d2730cd9765693d7c26ae73610 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 22 Apr 2026 11:26:07 +0200 Subject: [PATCH 12/13] Clean up and add blocking mode to remote data deletion --- .../server/service/RootNetworkService.java | 26 ++++++++++++------- .../study/server/service/StudyService.java | 13 ++++++---- .../server/SupervisionControllerTest.java | 3 ++- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java index 4a06796431..338db64ab6 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java @@ -26,6 +26,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -237,25 +238,30 @@ public void deleteRootNetworks(StudyEntity studyEntity, Stream rootNetwork } public void deleteRootNetworks(StudyEntity studyEntity, List rootNetworksInfos) { - deleteRootNetworkRemoteInfos(rootNetworksInfos, true); + deleteRootNetworkRemoteInfos(rootNetworksInfos, true, false); studyEntity.deleteRootNetworks(rootNetworksInfos.stream().map(RootNetworkInfos::getId).collect(Collectors.toSet())); } - public void deleteRootNetworkRemoteInfos(List rootNetworkInfos, boolean deleteCase) { + public void deleteRootNetworkRemoteInfos(List rootNetworkInfos, boolean deleteCase, boolean blocking) { // delete remote data ids set in root network - studyServerExecutionService.runAsync(() -> - reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList())); - studyServerExecutionService.runAsync(() -> + List> futures = new ArrayList<>(); + futures.add(studyServerExecutionService.runAsync(() -> + reportService.deleteReports(rootNetworkInfos.stream().map(RootNetworkInfos::getReportUuid).toList()))); + futures.add(studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes)); - studyServerExecutionService.runAsync(() -> + .filter(Objects::nonNull).forEach(equipmentInfosService::deleteEquipmentIndexes))); + futures.add(studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getNetworkInfos().getNetworkUuid()) - .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork)); + .filter(Objects::nonNull).forEach(networkStoreService::deleteNetwork))); if (deleteCase) { - studyServerExecutionService.runAsync(() -> + futures.add(studyServerExecutionService.runAsync(() -> rootNetworkInfos.stream().map(rni -> rni.getCaseInfos().getCaseUuid()) - .filter(Objects::nonNull).forEach(caseService::deleteCase)); + .filter(Objects::nonNull).forEach(caseService::deleteCase))); + } + + if (blocking) { + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } 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 b6847a7d5c..8d4df2f39b 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -610,7 +610,7 @@ public void deleteStudyIfNotCreationInProgress(UUID studyUuid) { startTime.set(System.nanoTime()); // delete all distant resources linked to rootNetworks - rootNetworkService.deleteRootNetworkRemoteInfos(deleteStudyInfos.getRootNetworkInfosList(), true); + rootNetworkService.deleteRootNetworkRemoteInfos(deleteStudyInfos.getRootNetworkInfosList(), true, false); // delete all distant resources linked to nodes studyServerExecutionService.runAsync(() -> deleteStudyInfos.getModificationGroupUuids().stream().filter(Objects::nonNull).forEach(networkModificationService::deleteModifications)); @@ -3925,13 +3925,16 @@ public void unmountStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid) { RootNetworkEntity rootNetwork = rootNetworkService.getRootNetwork(rootNetworkUuid) .orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - var rootNode = networkModificationTreeService.getStudyRootNodeUuid(studyUuid); + var rootNodeUuid = networkModificationTreeService.getStudyRootNodeUuid(studyUuid); try { - doUnbuildNodeTree(studyUuid, rootNode, true); - rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false); + // First we unbuild all nodes + doUnbuildNodeTree(studyUuid, rootNodeUuid, true); + + // Then we erase data linked to root node on all root networks + rootNetworkService.deleteRootNetworkRemoteInfos(List.of(rootNetwork.toDto()), false, true); updateRootNetworkIndexationStatus(study, rootNetwork, RootNetworkIndexationStatus.NOT_INDEXED); } finally { - networkModificationTreeService.unblockNodeTree(rootNetworkUuid, rootNode); + networkModificationTreeService.unblockNodeTree(rootNetworkUuid, rootNodeUuid); } notificationService.emitRootNetworksUpdated(studyUuid); diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 3efe1855ba..7c82c89ca6 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -317,9 +317,10 @@ void testUnmountStudy() throws Exception { // Remote root-network data was deleted, but the case was preserved (deleteCase = false) Mockito.verify(rootNetworkService, Mockito.times(1)) - .deleteRootNetworkRemoteInfos(any(), eq(false)); + .deleteRootNetworkRemoteInfos(any(), eq(false), eq(true)); // Indexation flipped to NOT_INDEXED so the auto-detect path will reimport on reopen assertIndexationStatus(STUDY_UUID, RootNetworkIndexationStatus.NOT_INDEXED.name()); + assertIndexationCount(0, 0); } } From 7e3ff097a83a9597d94f5a042b5b46eefa4da50b Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 22 Apr 2026 11:28:10 +0200 Subject: [PATCH 13/13] Checkstyle --- .../org/gridsuite/study/server/service/RootNetworkService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java index 338db64ab6..74fdfd838a 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkService.java @@ -264,7 +264,6 @@ public void deleteRootNetworkRemoteInfos(List rootNetworkInfos CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } - // delete remote data ids set in root network node infos rootNetworkNodeInfoService.deleteRootNetworkNodeRemoteInfos(rootNetworkInfos.stream() .map(RootNetworkInfos::getRootNetworkNodeInfos)