From b9a0aa48c5ebaf8dd4bde6732ec3cf5974e3e457 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 28 Apr 2026 16:52:58 +0200 Subject: [PATCH 1/4] Modify actions pool which update last modification date --- .../java/org/gridsuite/study/server/service/StudyService.java | 1 + 1 file changed, 1 insertion(+) 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 0a2566e2a..ce9591cc4 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1232,6 +1232,7 @@ public void setNetworkVisualizationParametersValues(UUID studyUuid, String param StudyEntity studyEntity = getStudy(studyUuid); createOrUpdateNetworkVisualizationParameters(studyEntity, parameters); notificationService.emitNetworkVisualizationParamsChanged(studyUuid); + notificationService.emitElementUpdated(studyUuid, userId); } public void createOrUpdateNetworkVisualizationParameters(StudyEntity studyEntity, String parameters) { From 59e60dbd417538c5cfcc65af2b52d2d833db272c Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 29 Apr 2026 15:46:17 +0200 Subject: [PATCH 2/4] Add actions which trigger lastModificationDate update --- .../study/server/controller/StudyController.java | 15 +++++++++------ .../study/server/service/StudyService.java | 14 +++++++++++--- .../server/rootnetworks/RootNetworkTest.java | 2 +- 3 files changed, 21 insertions(+), 10 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 c0e5bd0f8..b165475a7 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -240,8 +240,9 @@ public ResponseEntity rootNetworkTagExists(@PathVariable("studyUuid") UUID @Operation(summary = "Delete root networks for study") @ApiResponse(responseCode = "200", description = "Root network deleted") public ResponseEntity deleteRootNetwork(@PathVariable("studyUuid") UUID studyUuid, - @RequestBody List rootNetworksUuids) { - studyService.deleteRootNetworks(studyUuid, rootNetworksUuids); + @RequestBody List rootNetworksUuids, + @RequestHeader(HEADER_USER_ID) String userId) { + studyService.deleteRootNetworks(studyUuid, rootNetworksUuids, userId); return ResponseEntity.ok().build(); } @@ -1570,8 +1571,9 @@ public ResponseEntity>> getStashedNodes(@Parame @ApiResponse(responseCode = "200", description = "The list of nodes in the trash")}) public ResponseEntity restoreNodes(@Parameter(description = "study uuid") @PathVariable("studyUuid") UUID studyUuid, @Parameter(description = "ids of nodes to restore") @RequestParam("ids") List nodeIds, - @Parameter(description = "id of node below which the node will be restored") @RequestParam("anchorNodeId") UUID anchorNodeId) { - studyService.restoreNodes(studyUuid, nodeIds, anchorNodeId); + @Parameter(description = "id of node below which the node will be restored") @RequestParam("anchorNodeId") UUID anchorNodeId, + @RequestHeader(HEADER_USER_ID) String userId) { + studyService.restoreNodes(studyUuid, nodeIds, anchorNodeId, userId); return ResponseEntity.ok().build(); } @@ -2461,9 +2463,10 @@ public ResponseEntity> getNodeAliases( @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Node aliases have been updated"), @ApiResponse(responseCode = "404", description = "Study doesn't exists")}) public ResponseEntity setNodeAliases( @PathVariable("studyUuid") UUID studyUuid, - @RequestBody List nodeAliases) { + @RequestBody List nodeAliases, + @RequestHeader(HEADER_USER_ID) String userId) { studyService.assertIsStudyExist(studyUuid); - studyService.updateNodeAliases(studyUuid, nodeAliases); + studyService.updateNodeAliases(studyUuid, nodeAliases, userId); return ResponseEntity.ok().build(); } 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 ce9591cc4..0358f6502 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -306,7 +306,7 @@ public void assertIsRootNetworkAndNodeInStudy(@NonNull final UUID studyUuid, @No } @Transactional - public void deleteRootNetworks(UUID studyUuid, List rootNetworksUuids) { + public void deleteRootNetworks(UUID studyUuid, List rootNetworksUuids, String userId) { assertIsStudyExist(studyUuid); StudyEntity studyEntity = getStudy(studyUuid); List allRootNetworkEntities = getStudyRootNetworks(studyUuid); @@ -321,6 +321,7 @@ public void deleteRootNetworks(UUID studyUuid, List rootNetworksUuids) { rootNetworkService.deleteRootNetworks(studyEntity, rootNetworksUuids.stream()); notificationService.emitRootNetworksUpdated(studyUuid); + notificationService.emitElementUpdated(studyUuid, userId); } @Transactional @@ -388,6 +389,7 @@ public void updateRootNetworkRequest(UUID studyUuid, RootNetworkInfos rootNetwor } else { updateRootNetworkBasicInfos(studyEntity.getId(), rootNetworkInfos, false); } + notificationService.emitElementUpdated(studyUuid, userId); } private void updateRootNetworkCaseInfos(UUID studyUuid, RootNetworkInfos rootNetworkInfos, String userId, RootNetworkRequestEntity rootNetworkModificationRequestEntity) { @@ -450,6 +452,7 @@ private void recreateNetwork(RootNetworkInfos rootNetworkInfos, UUID studyUuid, : importParameters; persistNetwork(rootNetworkInfos, studyUuid, null, userId, importParametersToUse, CaseImportAction.NETWORK_RECREATION); + notificationService.emitElementUpdated(studyUuid, userId); } public UUID duplicateStudy(UUID sourceStudyUuid, String userId) { @@ -2290,9 +2293,10 @@ public List> getStashedNodes(UUID studyId) { return networkModificationTreeService.getStashedNodes(studyId); } - public void restoreNodes(UUID studyId, List nodeIds, UUID anchorNodeId) { + public void restoreNodes(UUID studyId, List nodeIds, UUID anchorNodeId, String userId) { networkModificationTreeService.assertIsRootOrConstructionNode(anchorNodeId); networkModificationTreeService.restoreNode(studyId, nodeIds, anchorNodeId); + notificationService.emitElementUpdated(studyId, userId); } private void reindexRootNetwork(StudyEntity study, UUID rootNetworkUuid) { @@ -3000,6 +3004,7 @@ public void createDynamicSimulationEvent(UUID studyUuid, UUID nodeUuid, String u dynamicSimulationEventService.saveEvent(nodeUuid, event); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); + notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); } @@ -3012,6 +3017,7 @@ public void updateDynamicSimulationEvent(UUID studyUuid, UUID nodeUuid, String u dynamicSimulationEventService.saveEvent(nodeUuid, event); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); + notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); } @@ -3024,6 +3030,7 @@ public void deleteDynamicSimulationEvents(UUID studyUuid, UUID nodeUuid, String dynamicSimulationEventService.deleteEvents(eventUuids); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); + notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); } @@ -3733,7 +3740,7 @@ public List getNodeAliases(UUID studyUuid) { } @Transactional - public void updateNodeAliases(UUID studyUuid, List nodeAliases) { + public void updateNodeAliases(UUID studyUuid, List nodeAliases, String userId) { StudyEntity studyEntity = getStudy(studyUuid); //Reset alias values for given study to keep data in sync studyEntity.setNodeAliases(null); @@ -3750,6 +3757,7 @@ public void updateNodeAliases(UUID studyUuid, List nodeAliases) { studyEntity.setNodeAliases(newNodeAliases); } notificationService.emitSpreadsheetNodeAliasesChanged(studyUuid); + notificationService.emitElementUpdated(studyUuid, userId); } public UUID createColumn(UUID studyUuid, UUID configUuid, String columnInfos) { diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java index b0b2674d7..a2fe0ff19 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java @@ -835,7 +835,7 @@ void getOrderedRootNetworksFromService() { assertEquals("dummyRootNetwork2", result.get(2).name()); // delete the 2nd root network - studyService.deleteRootNetworks(studyEntity.getId(), List.of(result.get(1).rootNetworkUuid())); + studyService.deleteRootNetworks(studyEntity.getId(), List.of(result.get(1).rootNetworkUuid()), USER_ID); // check "dummyRootNetwork2" root network order have been updated correctly List resultAfterDeletion = studyService.getExistingBasicRootNetworkInfos(studyEntity.getId()); From 75c01d9b725d745dec11c846f1b55b4d5b0b9a87 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 5 May 2026 15:06:16 +0200 Subject: [PATCH 3/4] Adapt tests and add update for computations launch --- .../gridsuite/study/server/service/StudyService.java | 12 ++++++++++++ .../study/server/NetworkModificationTreeTest.java | 7 +++++++ .../study/server/studycontroller/StudyTest.java | 2 ++ 3 files changed, 21 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 44df2f944..a30fc916a 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1028,6 +1028,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 @@ -1080,6 +1081,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) { @@ -1385,6 +1387,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; } @@ -2688,6 +2691,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; } @@ -2713,6 +2717,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; } @@ -2745,6 +2750,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; } @@ -3073,6 +3079,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; } @@ -3183,6 +3190,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; } @@ -3291,6 +3299,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; } @@ -3549,6 +3558,8 @@ 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; } @@ -3573,6 +3584,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; } diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java index 65bac6da3..8f48228fb 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java @@ -998,6 +998,7 @@ private void restoreNode(UUID studyUuid, List nodeIds, UUID anchorNodeId, var message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertTrue(nodeIds.contains(message.getHeaders().get(HEADER_NEW_NODE)) || anchorNodeId.equals(message.getHeaders().get(HEADER_NEW_NODE))); } + checkElementUpdatedMessageSent(studyUuid, userId); } private static void assertChildrenEquals(Set original, List children) { @@ -1690,8 +1691,10 @@ void testNodeAliases() throws Exception { mockMvc.perform(post("/v1/studies/{studyUuid}/node-aliases", root.getStudyId()) .contentType(MediaType.APPLICATION_JSON) .content(objectWriter.writeValueAsString(aliases)) + .header("userId", userId) ).andExpect(status().isOk()); checkNodeAliasUpdateMessageReceived(root.getStudyId()); + checkElementUpdatedMessageSent(root.getStudyId(), userId); nodeAliases = objectMapper.readValue(mockMvc.perform(get("/v1/studies/{studyUuid}/node-aliases", root.getStudyId())).andExpect(status().isOk()).andReturn() .getResponse() @@ -1708,8 +1711,10 @@ void testNodeAliases() throws Exception { mockMvc.perform(post("/v1/studies/{studyUuid}/node-aliases", root.getStudyId()) .contentType(MediaType.APPLICATION_JSON) .content(objectWriter.writeValueAsString(aliases)) + .header("userId", userId) ).andExpect(status().isOk()); checkNodeAliasUpdateMessageReceived(root.getStudyId()); + checkElementUpdatedMessageSent(root.getStudyId(), userId); nodeAliases = objectMapper.readValue(mockMvc.perform(get("/v1/studies/{studyUuid}/node-aliases", root.getStudyId())).andExpect(status().isOk()).andReturn() .getResponse() @@ -1742,8 +1747,10 @@ void testNodeAliases() throws Exception { mockMvc.perform(post("/v1/studies/{studyUuid}/node-aliases", root.getStudyId()) .contentType(MediaType.APPLICATION_JSON) .content(objectWriter.writeValueAsString(aliases)) + .header("userId", userId) ).andExpect(status().isOk()); checkNodeAliasUpdateMessageReceived(root.getStudyId()); + checkElementUpdatedMessageSent(root.getStudyId(), userId); // Stashing node3 (with stashChildren=true) should result in aliases no more associated to nodes node3, node4 and node5 doNothing().when(rootNetworkNodeInfoService).assertComputationNotRunning(any(), any()); diff --git a/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java b/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java index 2fad3b9b3..d8e80caf0 100644 --- a/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java +++ b/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java @@ -974,8 +974,10 @@ private void testDuplicateStudy(UUID study1Uuid, UUID rootNetworkUuid, String us mockMvc.perform(post("/v1/studies/{studyUuid}/node-aliases", study1Uuid) .contentType(MediaType.APPLICATION_JSON) .content(objectWriter.writeValueAsString(aliases)) + .header(USER_ID_HEADER, userId) ).andExpect(status().isOk()); checkNodeAliasUpdateMessageReceived(study1Uuid); + checkElementUpdatedMessageSent(study1Uuid, userId); // duplicate the study StudyEntity duplicatedStudy = duplicateStudy(study1Uuid, userId); From 65249d30bc81d2ba597af92f96190dec2eb3754c Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 5 May 2026 15:14:24 +0200 Subject: [PATCH 4/4] Move notification emission --- .../org/gridsuite/study/server/service/StudyService.java | 6 +++--- 1 file changed, 3 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 a30fc916a..0174c8754 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -3010,9 +3010,9 @@ public void createDynamicSimulationEvent(UUID studyUuid, UUID nodeUuid, String u dynamicSimulationEventService.saveEvent(nodeUuid, event); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); - notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); + notificationService.emitElementUpdated(studyUuid, userId); } @Transactional @@ -3023,9 +3023,9 @@ public void updateDynamicSimulationEvent(UUID studyUuid, UUID nodeUuid, String u dynamicSimulationEventService.saveEvent(nodeUuid, event); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); - notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); + notificationService.emitElementUpdated(studyUuid, userId); } @Transactional @@ -3036,9 +3036,9 @@ public void deleteDynamicSimulationEvents(UUID studyUuid, UUID nodeUuid, String dynamicSimulationEventService.deleteEvents(eventUuids); } finally { notificationService.emitEndEventCrudNotification(studyUuid, nodeUuid, childrenUuids); - notificationService.emitElementUpdated(studyUuid, userId); } postProcessEventCrud(studyUuid, nodeUuid); + notificationService.emitElementUpdated(studyUuid, userId); } @Transactional