From e948dcfec53090826ab10744ecca7a791da29dcf Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 24 Feb 2026 16:45:24 +0100 Subject: [PATCH 01/34] extract step execution to commons Signed-off-by: benrejebmoh --- .../commons/steps/ReportPublisher.java | 16 ++++ .../commons/steps/StepExecutionInterface.java | 95 +++++++++++++++++++ .../commons/steps/StepStatusPublisher.java | 20 ++++ .../server/services/StepExecutionService.java | 95 +++++++++---------- 4 files changed, 175 insertions(+), 51 deletions(-) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java new file mode 100644 index 00000000..bb119d8a --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +/** + * @author Mohamed Ben-rejeb + */ +@FunctionalInterface +public interface ReportPublisher { + + void sendReport(R reportInfos); +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java new file mode 100644 index 00000000..d232b114 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.StepStatus; + +import java.time.Instant; +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +public interface StepExecutionInterface { + + UUID getExecutionId(); + + void setExecutionId(UUID executionId); + + StepStatusPublisher getStepStatusPublisher(); + + ReportPublisher getReportPublisher(); + + default void skipStep(UUID stepExecutionId, String stepTypeName, int stepOrder, Instant startedAt) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.SKIPPED, + null, + null, + null, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + } + + default void executeStep(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + R reportInfos, + ResultInfos resultInfos, + Runnable stepExecution) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.RUNNING, + null, + null, + reportUuid, + startedAt, + null + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + + try { + stepExecution.run(); + getReportPublisher().sendReport(reportInfos); + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.COMPLETED); + } catch (Exception e) { + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.FAILED); + throw e; + } + } + + default void updateStepStatus(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + ResultInfos resultInfos, + StepStatus status) { + ProcessExecutionStep updated = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + status, + resultInfos != null ? resultInfos.resultUUID() : null, + resultInfos != null ? resultInfos.resultType() : null, + reportUuid, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), updated); + } +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java new file mode 100644 index 00000000..622903b1 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; + +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +@FunctionalInterface +public interface StepStatusPublisher { + + void updateStepStatus(UUID executionId, ProcessExecutionStep processExecutionStep); +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 0d57ffe8..5a08035f 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,77 +6,70 @@ */ package org.gridsuite.monitor.worker.server.services; -import lombok.RequiredArgsConstructor; +import lombok.Getter; +import lombok.Setter; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.ProcessExecutionStep; -import org.gridsuite.monitor.commons.StepStatus; +import org.gridsuite.monitor.commons.steps.ReportPublisher; +import org.gridsuite.monitor.commons.steps.StepExecutionInterface; +import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; +import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.springframework.stereotype.Service; -import java.time.Instant; +import java.util.Objects; +import java.util.UUID; /** * @author Antoine Bouhours */ @Service -@RequiredArgsConstructor -public class StepExecutionService { +public class StepExecutionService implements StepExecutionInterface { - private final NotificationService notificationService; - private final ReportService reportService; + @Getter + @Setter + private UUID executionId; - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( + private final StepStatusPublisher stepStatusPublisher; + private final ReportPublisher reportPublisher; + + public StepExecutionService(NotificationService notificationService, ReportService reportService) { + this.stepStatusPublisher = notificationService::updateStepStatus; + this.reportPublisher = reportService::sendReport; + } + + @Override + public StepStatusPublisher getStepStatusPublisher() { + return stepStatusPublisher; + } + + @Override + public ReportPublisher getReportPublisher() { + return reportPublisher; + } + + public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { + setExecutionId(context.getProcessExecutionId()); + skipStep( context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), - StepStatus.SKIPPED, - null, - null, - null, - context.getStartedAt(), - Instant.now() + context.getStartedAt() ); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - StepStatus.RUNNING, - null, - null, - context.getReportInfos().reportUuid(), - context.getStartedAt(), - null + setExecutionId(context.getProcessExecutionId()); + executeStep( + context.getStepExecutionId(), + step.getType().getName(), + context.getStepOrder(), + context.getStartedAt(), + Objects.requireNonNull(context.getReportInfos()).reportUuid(), + context.getReportInfos(), + context.getResultInfos(), + () -> step.execute(context) ); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); - - try { - step.execute(context); - reportService.sendReport(context.getReportInfos()); - updateStepStatus(context, StepStatus.COMPLETED, step); - } catch (Exception e) { - updateStepStatus(context, StepStatus.FAILED, step); - throw e; - } } - private void updateStepStatus(ProcessStepExecutionContext context, StepStatus status, ProcessStep step) { - ProcessExecutionStep updated = new ProcessExecutionStep( - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - status, - context.getResultInfos() != null ? context.getResultInfos().resultUUID() : null, - context.getResultInfos() != null ? context.getResultInfos().resultType() : null, - context.getReportInfos().reportUuid(), - context.getStartedAt(), - Instant.now() - ); - notificationService.updateStepStatus(context.getProcessExecutionId(), updated); - } } From 2cb29681608f343edd582c3c5e0e569f0c76ccc1 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 24 Feb 2026 16:45:24 +0100 Subject: [PATCH 02/34] extract step execution to commons Signed-off-by: benrejebmoh --- monitor-commons/pom.xml | 4 + .../monitor/commons}/ReportInfos.java | 2 +- .../commons/steps/ReportPublisher.java | 18 +++ .../monitor/commons/steps/StepExecution.java | 117 ++++++++++++++++++ .../commons/steps/StepStatusPublisher.java | 20 +++ .../core/ProcessStepExecutionContext.java | 2 +- .../worker/server/services/ReportService.java | 2 +- .../server/services/StepExecutionService.java | 85 +++++-------- .../steps/ApplyModificationsStepTest.java | 2 +- .../commons/steps/LoadNetworkStepTest.java | 2 +- ...ecurityAnalysisRunComputationStepTest.java | 2 +- .../server/services/ReportServiceTest.java | 2 +- .../services/StepExecutionServiceTest.java | 2 +- 13 files changed, 201 insertions(+), 59 deletions(-) rename {monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/dto => monitor-commons/src/main/java/org/gridsuite/monitor/commons}/ReportInfos.java (90%) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java diff --git a/monitor-commons/pom.xml b/monitor-commons/pom.xml index a09e1147..cb20fb46 100644 --- a/monitor-commons/pom.xml +++ b/monitor-commons/pom.xml @@ -23,5 +23,9 @@ com.fasterxml.jackson.core jackson-annotations + + com.powsybl + powsybl-commons + diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/dto/ReportInfos.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ReportInfos.java similarity index 90% rename from monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/dto/ReportInfos.java rename to monitor-commons/src/main/java/org/gridsuite/monitor/commons/ReportInfos.java index d12c59f7..2251372d 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/dto/ReportInfos.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ReportInfos.java @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.gridsuite.monitor.worker.server.dto; +package org.gridsuite.monitor.commons; import com.powsybl.commons.report.ReportNode; diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java new file mode 100644 index 00000000..c1600c80 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ReportInfos; + +/** + * @author Mohamed Ben-rejeb + */ +@FunctionalInterface +public interface ReportPublisher { + + void sendReport(ReportInfos reportInfos); +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java new file mode 100644 index 00000000..ab3a4cbc --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ReportInfos; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.StepStatus; + +import java.time.Instant; +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +public interface StepExecution { + + StepStatusPublisher getStepStatusPublisher(); + + ReportPublisher getReportPublisher(); + + default void skipStep( + UUID processExecutionId, + UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt + ) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.SKIPPED, + null, + null, + null, + startedAt, + Instant.now()); + getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + } + + default void executeStep( + UUID processExecutionId, + UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + ReportInfos reportInfos, + ResultInfos resultInfos, + Runnable stepExecution + ) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.RUNNING, + null, + null, + reportUuid, + startedAt, + null); + getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + + try { + stepExecution.run(); + getReportPublisher().sendReport(reportInfos); + updateStepStatus( + processExecutionId, + stepExecutionId, + stepTypeName, + stepOrder, + startedAt, + reportUuid, + resultInfos, + StepStatus.COMPLETED); + } catch (Exception e) { + updateStepStatus( + processExecutionId, + stepExecutionId, + stepTypeName, + stepOrder, + startedAt, + reportUuid, + resultInfos, + StepStatus.FAILED); + throw e; + } + } + + default void updateStepStatus( + UUID processExecutionId, + UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + ResultInfos resultInfos, + StepStatus status + ) { + ProcessExecutionStep updated = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + status, + resultInfos != null ? resultInfos.resultUUID() : null, + resultInfos != null ? resultInfos.resultType() : null, + reportUuid, + startedAt, + Instant.now()); + getStepStatusPublisher().updateStepStatus(processExecutionId, updated); + } +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java new file mode 100644 index 00000000..622903b1 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepStatusPublisher.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; + +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +@FunctionalInterface +public interface StepStatusPublisher { + + void updateStepStatus(UUID executionId, ProcessExecutionStep processExecutionStep); +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java index 29e44819..6fdb52b9 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java @@ -11,8 +11,8 @@ import lombok.Getter; import lombok.Setter; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.commons.ResultInfos; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import java.time.Instant; import java.util.UUID; diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ReportService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ReportService.java index 8bbeb857..6d7c3b4a 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ReportService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ReportService.java @@ -11,7 +11,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; import lombok.Setter; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; +import org.gridsuite.monitor.commons.ReportInfos; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpEntity; diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 0d57ffe8..367e37a1 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,77 +6,60 @@ */ package org.gridsuite.monitor.worker.server.services; -import lombok.RequiredArgsConstructor; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.ProcessExecutionStep; -import org.gridsuite.monitor.commons.StepStatus; +import org.gridsuite.monitor.commons.steps.ReportPublisher; +import org.gridsuite.monitor.commons.steps.StepExecution; +import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.springframework.stereotype.Service; -import java.time.Instant; +import java.util.Objects; /** * @author Antoine Bouhours */ @Service -@RequiredArgsConstructor -public class StepExecutionService { +public class StepExecutionService implements StepExecution { - private final NotificationService notificationService; - private final ReportService reportService; + private final StepStatusPublisher stepStatusPublisher; + private final ReportPublisher reportPublisher; - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( + public StepExecutionService(NotificationService notificationService, ReportService reportService) { + this.stepStatusPublisher = notificationService::updateStepStatus; + this.reportPublisher = reportService::sendReport; + } + + @Override + public StepStatusPublisher getStepStatusPublisher() { + return stepStatusPublisher; + } + + @Override + public ReportPublisher getReportPublisher() { + return reportPublisher; + } + + public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { + skipStep(context.getProcessExecutionId(), context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), - StepStatus.SKIPPED, - null, - null, - null, - context.getStartedAt(), - Instant.now() + context.getStartedAt() ); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - StepStatus.RUNNING, - null, - null, - context.getReportInfos().reportUuid(), - context.getStartedAt(), - null + executeStep(context.getProcessExecutionId(), + context.getStepExecutionId(), + step.getType().getName(), + context.getStepOrder(), + context.getStartedAt(), + Objects.requireNonNull(context.getReportInfos()).reportUuid(), + context.getReportInfos(), + context.getResultInfos(), + () -> step.execute(context) ); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); - - try { - step.execute(context); - reportService.sendReport(context.getReportInfos()); - updateStepStatus(context, StepStatus.COMPLETED, step); - } catch (Exception e) { - updateStepStatus(context, StepStatus.FAILED, step); - throw e; - } } - private void updateStepStatus(ProcessStepExecutionContext context, StepStatus status, ProcessStep step) { - ProcessExecutionStep updated = new ProcessExecutionStep( - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - status, - context.getResultInfos() != null ? context.getResultInfos().resultUUID() : null, - context.getResultInfos() != null ? context.getResultInfos().resultType() : null, - context.getReportInfos().reportUuid(), - context.getStartedAt(), - Instant.now() - ); - notificationService.updateStepStatus(context.getProcessExecutionId(), updated); - } } diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index 1316d04c..388b4e16 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,8 +14,8 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.FilterService; import org.gridsuite.monitor.worker.server.services.NetworkModificationRestService; import org.gridsuite.monitor.worker.server.services.NetworkModificationService; diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/LoadNetworkStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/LoadNetworkStepTest.java index 607f3339..6216c080 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/LoadNetworkStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/LoadNetworkStepTest.java @@ -10,8 +10,8 @@ import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.NetworkConversionService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/steps/SecurityAnalysisRunComputationStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/steps/SecurityAnalysisRunComputationStepTest.java index fd07eb36..78b2f9e2 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/steps/SecurityAnalysisRunComputationStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/steps/SecurityAnalysisRunComputationStepTest.java @@ -10,10 +10,10 @@ import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.security.SecurityAnalysisResult; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.commons.ResultType; import org.gridsuite.monitor.commons.SecurityAnalysisConfig; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.SecurityAnalysisService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ReportServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ReportServiceTest.java index 73811849..fde96cc2 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ReportServiceTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ReportServiceTest.java @@ -9,8 +9,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.commons.report.ReportNode; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.config.MonitorWorkerConfig; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/StepExecutionServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/StepExecutionServiceTest.java index c437cd38..16c2b0e0 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/StepExecutionServiceTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/StepExecutionServiceTest.java @@ -9,11 +9,11 @@ import com.powsybl.commons.report.ReportNode; import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.commons.StepStatus; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.gridsuite.monitor.worker.server.core.ProcessStepType; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; From b9ad2ddc8afe9fc9320541a1776a557f09ebb4f5 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 10:38:17 +0100 Subject: [PATCH 03/34] use abstract class intead of interface Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- ...{StepExecution.java => AbstractStepExecutor.java} | 12 ++++++------ .../worker/server/services/StepExecutionService.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) rename monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/{StepExecution.java => AbstractStepExecutor.java} (92%) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java similarity index 92% rename from monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java rename to monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index ab3a4cbc..4f836c21 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -17,13 +17,13 @@ /** * @author Mohamed Ben-rejeb */ -public interface StepExecution { +public abstract class AbstractStepExecutor { - StepStatusPublisher getStepStatusPublisher(); + protected abstract StepStatusPublisher getStepStatusPublisher(); - ReportPublisher getReportPublisher(); + protected abstract ReportPublisher getReportPublisher(); - default void skipStep( + public void skipStep( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, @@ -43,7 +43,7 @@ default void skipStep( getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); } - default void executeStep( + public void executeStep( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, @@ -92,7 +92,7 @@ default void executeStep( } } - default void updateStepStatus( + private void updateStepStatus( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 367e37a1..50256341 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -8,7 +8,7 @@ import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.commons.steps.ReportPublisher; -import org.gridsuite.monitor.commons.steps.StepExecution; +import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; @@ -20,7 +20,7 @@ * @author Antoine Bouhours */ @Service -public class StepExecutionService implements StepExecution { +public class StepExecutionService extends AbstractStepExecutor { private final StepStatusPublisher stepStatusPublisher; private final ReportPublisher reportPublisher; From 092d18847856ea97ef4482527e3176f14337ef4f Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 10:43:08 +0100 Subject: [PATCH 04/34] merge with main Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../processes/commons/steps/ApplyModificationsStepTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index bc583f9a..4aa694c6 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,8 +14,8 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From e345a58028486eef6fbc16f12200e1a8bfc25e0e Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 17:12:10 +0100 Subject: [PATCH 05/34] resolve change request Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 16 ++++++++++------ .../server/services/StepExecutionService.java | 15 --------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 4f836c21..57eaef8c 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,9 +19,13 @@ */ public abstract class AbstractStepExecutor { - protected abstract StepStatusPublisher getStepStatusPublisher(); + public AbstractStepExecutor() { + StepStatusPublisher stepStatusPublisher; + ReportPublisher reportPublisher; + } - protected abstract ReportPublisher getReportPublisher(); + protected StepStatusPublisher stepStatusPublisher; + protected ReportPublisher reportPublisher; public void skipStep( UUID processExecutionId, @@ -40,7 +44,7 @@ public void skipStep( null, startedAt, Instant.now()); - getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); } public void executeStep( @@ -64,11 +68,11 @@ public void executeStep( reportUuid, startedAt, null); - getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); try { stepExecution.run(); - getReportPublisher().sendReport(reportInfos); + reportPublisher.sendReport(reportInfos); updateStepStatus( processExecutionId, stepExecutionId, @@ -112,6 +116,6 @@ private void updateStepStatus( reportUuid, startedAt, Instant.now()); - getStepStatusPublisher().updateStepStatus(processExecutionId, updated); + stepStatusPublisher.updateStepStatus(processExecutionId, updated); } } diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 50256341..0a1e46de 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -7,9 +7,7 @@ package org.gridsuite.monitor.worker.server.services; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.steps.ReportPublisher; import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; -import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.springframework.stereotype.Service; @@ -22,24 +20,11 @@ @Service public class StepExecutionService extends AbstractStepExecutor { - private final StepStatusPublisher stepStatusPublisher; - private final ReportPublisher reportPublisher; - public StepExecutionService(NotificationService notificationService, ReportService reportService) { this.stepStatusPublisher = notificationService::updateStepStatus; this.reportPublisher = reportService::sendReport; } - @Override - public StepStatusPublisher getStepStatusPublisher() { - return stepStatusPublisher; - } - - @Override - public ReportPublisher getReportPublisher() { - return reportPublisher; - } - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { skipStep(context.getProcessExecutionId(), context.getStepExecutionId(), From d9df308afbc7f5c48dd20b7a984d0853411e3df5 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 09:36:45 +0100 Subject: [PATCH 06/34] merge with main Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 57 +++++++++---------- .../steps/ApplyModificationsStepTest.java | 2 +- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 57eaef8c..0aa7cd5c 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -34,16 +34,14 @@ public void skipStep( int stepOrder, Instant startedAt ) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.SKIPPED, - null, - null, - null, - startedAt, - Instant.now()); + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(StepStatus.SKIPPED) + .startedAt(startedAt) + .completedAt(Instant.now()) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); } @@ -58,16 +56,14 @@ public void executeStep( ResultInfos resultInfos, Runnable stepExecution ) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.RUNNING, - null, - null, - reportUuid, - startedAt, - null); + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(StepStatus.RUNNING) + .reportId(reportUuid) + .startedAt(startedAt) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); try { @@ -106,16 +102,17 @@ private void updateStepStatus( ResultInfos resultInfos, StepStatus status ) { - ProcessExecutionStep updated = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - status, - resultInfos != null ? resultInfos.resultUUID() : null, - resultInfos != null ? resultInfos.resultType() : null, - reportUuid, - startedAt, - Instant.now()); + ProcessExecutionStep updated = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(status) + .resultId(resultInfos != null ? resultInfos.resultUUID() : null) + .resultType(resultInfos != null ? resultInfos.resultType() : null) + .reportId(reportUuid) + .startedAt(startedAt) + .completedAt(Instant.now()) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, updated); } } diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index 0e5be278..5c4f3bd5 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,8 +14,8 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ModifyingProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From f21a5fffc761c2883f58331603c125042a5d078f Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 15:14:56 +0100 Subject: [PATCH 07/34] add unit tests Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- monitor-commons/pom.xml | 7 + .../commons/steps/AbstractStepExecutor.java | 2 - .../commons/steps/ReportPublisherTest.java | 33 ++++ .../commons/steps/StepExecutorTest.java | 156 ++++++++++++++++++ .../steps/StepStatusPublisherTest.java | 40 +++++ 5 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java diff --git a/monitor-commons/pom.xml b/monitor-commons/pom.xml index cb20fb46..5d8bb2cf 100644 --- a/monitor-commons/pom.xml +++ b/monitor-commons/pom.xml @@ -27,5 +27,12 @@ com.powsybl powsybl-commons + + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 0aa7cd5c..1d52e894 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -20,8 +20,6 @@ public abstract class AbstractStepExecutor { public AbstractStepExecutor() { - StepStatusPublisher stepStatusPublisher; - ReportPublisher reportPublisher; } protected StepStatusPublisher stepStatusPublisher; diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java new file mode 100644 index 00000000..526cbf20 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import com.powsybl.commons.report.ReportNode; +import org.gridsuite.monitor.commons.ReportInfos; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @author Mohamed Ben-rejeb + */ +class ReportPublisherTest { + + @Test + void sendReportShouldForwardReportInfosToImplementation() { + AtomicReference published = new AtomicReference<>(); + ReportPublisher reportPublisher = published::set; + ReportInfos reportInfos = new ReportInfos(UUID.randomUUID(), ReportNode.NO_OP); + + reportPublisher.sendReport(reportInfos); + + assertSame(reportInfos, published.get()); + } +} diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java new file mode 100644 index 00000000..b181af18 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ReportInfos; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.ResultType; +import org.gridsuite.monitor.commons.StepStatus; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Mohamed Ben-rejeb + */ +class StepExecutorTest { + + @Test + void skipStepShouldPublishSkippedStatus() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + executor.skipStep(processExecutionId, stepExecutionId, "LOAD_NETWORK", 1, startedAt); + + assertEquals(1, executor.publishedSteps().size()); + PublishedStep published = executor.publishedSteps().get(0); + assertEquals(processExecutionId, published.processExecutionId()); + assertEquals(stepExecutionId, published.step().getId()); + assertEquals("LOAD_NETWORK", published.step().getStepType()); + assertEquals(1, published.step().getStepOrder()); + assertEquals(StepStatus.SKIPPED, published.step().getStatus()); + assertNull(published.step().getResultId()); + assertNull(published.step().getResultType()); + assertNull(published.step().getReportId()); + assertEquals(startedAt, published.step().getStartedAt()); + assertNotNull(published.step().getCompletedAt()); + assertTrue(executor.publishedReports().isEmpty()); + } + + @Test + void executeStepShouldPublishRunningThenCompletedAndSendReport() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + UUID reportId = UUID.randomUUID(); + UUID resultId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + ResultInfos resultInfos = new ResultInfos(resultId, ResultType.SECURITY_ANALYSIS); + ReportInfos reportInfos = new ReportInfos(reportId, null); + AtomicInteger executionCount = new AtomicInteger(0); + + executor.executeStep( + processExecutionId, + stepExecutionId, + "RUN_SECURITY_ANALYSIS", + 2, + startedAt, + reportId, + reportInfos, + resultInfos, + executionCount::incrementAndGet + ); + + assertEquals(1, executionCount.get()); + assertEquals(List.of(reportInfos), executor.publishedReports()); + assertEquals(2, executor.publishedSteps().size()); + + PublishedStep running = executor.publishedSteps().get(0); + assertEquals(processExecutionId, running.processExecutionId()); + assertEquals(StepStatus.RUNNING, running.step().getStatus()); + assertEquals(reportId, running.step().getReportId()); + assertNull(running.step().getCompletedAt()); + + PublishedStep completed = executor.publishedSteps().get(1); + assertEquals(processExecutionId, completed.processExecutionId()); + assertEquals(StepStatus.COMPLETED, completed.step().getStatus()); + assertEquals(resultId, completed.step().getResultId()); + assertEquals(ResultType.SECURITY_ANALYSIS, completed.step().getResultType()); + assertEquals(reportId, completed.step().getReportId()); + assertNotNull(completed.step().getCompletedAt()); + } + + @Test + void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + UUID reportId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + RuntimeException failure = new RuntimeException("boom"); + + RuntimeException thrown = assertThrows(RuntimeException.class, () -> executor.executeStep( + processExecutionId, + stepExecutionId, + "FAILING_STEP", + 3, + startedAt, + reportId, + new ReportInfos(reportId, null), + null, + () -> { + throw failure; + } + )); + + assertSame(failure, thrown); + assertTrue(executor.publishedReports().isEmpty()); + assertEquals(2, executor.publishedSteps().size()); + + PublishedStep running = executor.publishedSteps().get(0); + assertEquals(StepStatus.RUNNING, running.step().getStatus()); + + PublishedStep failed = executor.publishedSteps().get(1); + assertEquals(StepStatus.FAILED, failed.step().getStatus()); + assertNull(failed.step().getResultId()); + assertNull(failed.step().getResultType()); + assertNotNull(failed.step().getCompletedAt()); + } + + private static final class TestStepExecutor extends AbstractStepExecutor { + private final List publishedSteps = new ArrayList<>(); + private final List publishedReports = new ArrayList<>(); + + private TestStepExecutor() { + this.stepStatusPublisher = (executionId, processExecutionStep) -> + publishedSteps.add(new PublishedStep(executionId, processExecutionStep)); + this.reportPublisher = publishedReports::add; + } + + List publishedSteps() { + return publishedSteps; + } + + List publishedReports() { + return publishedReports; + } + } + + private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { + } +} diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java new file mode 100644 index 00000000..a2f3a857 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @author Mohamed Ben-rejeb + */ +class StepStatusPublisherTest { + + @Test + void updateStepStatusShouldForwardExecutionIdAndStepToImplementation() { + AtomicReference publishedExecutionId = new AtomicReference<>(); + AtomicReference publishedStep = new AtomicReference<>(); + StepStatusPublisher stepStatusPublisher = (executionId, processExecutionStep) -> { + publishedExecutionId.set(executionId); + publishedStep.set(processExecutionStep); + }; + + UUID executionId = UUID.randomUUID(); + ProcessExecutionStep step = ProcessExecutionStep.builder().stepType("step").build(); + + stepStatusPublisher.updateStepStatus(executionId, step); + + assertEquals(executionId, publishedExecutionId.get()); + assertSame(step, publishedStep.get()); + } +} From e0a4e9b058f852d290baf15f542e64d27364a113 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 15:28:46 +0100 Subject: [PATCH 08/34] add unit tests and fix sonar issues Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 2 +- .../commons/steps/StepExecutorTest.java | 195 +++++++++++++----- 2 files changed, 142 insertions(+), 55 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 1d52e894..2e4d6506 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,7 +19,7 @@ */ public abstract class AbstractStepExecutor { - public AbstractStepExecutor() { + protected AbstractStepExecutor() { } protected StepStatusPublisher stepStatusPublisher; diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java index b181af18..8bf6e9a7 100644 --- a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -35,19 +35,43 @@ void skipStepShouldPublishSkippedStatus() { executor.skipStep(processExecutionId, stepExecutionId, "LOAD_NETWORK", 1, startedAt); - assertEquals(1, executor.publishedSteps().size()); - PublishedStep published = executor.publishedSteps().get(0); + assertEquals( + 1, + executor.publishedSteps() + .size()); + PublishedStep published = executor.publishedSteps() + .get(0); assertEquals(processExecutionId, published.processExecutionId()); - assertEquals(stepExecutionId, published.step().getId()); - assertEquals("LOAD_NETWORK", published.step().getStepType()); - assertEquals(1, published.step().getStepOrder()); - assertEquals(StepStatus.SKIPPED, published.step().getStatus()); - assertNull(published.step().getResultId()); - assertNull(published.step().getResultType()); - assertNull(published.step().getReportId()); - assertEquals(startedAt, published.step().getStartedAt()); - assertNotNull(published.step().getCompletedAt()); - assertTrue(executor.publishedReports().isEmpty()); + assertEquals( + stepExecutionId, + published.step() + .getId()); + assertEquals( + "LOAD_NETWORK", + published.step() + .getStepType()); + assertEquals( + 1, + published.step() + .getStepOrder()); + assertEquals( + StepStatus.SKIPPED, + published.step() + .getStatus()); + assertNull(published.step() + .getResultId()); + assertNull(published.step() + .getResultType()); + assertNull(published.step() + .getReportId()); + assertEquals( + startedAt, + published.step() + .getStartedAt()); + assertNotNull(published.step() + .getCompletedAt()); + assertTrue(executor.publishedReports() + .isEmpty()); } @Test @@ -72,26 +96,50 @@ void executeStepShouldPublishRunningThenCompletedAndSendReport() { reportId, reportInfos, resultInfos, - executionCount::incrementAndGet - ); + executionCount::incrementAndGet); assertEquals(1, executionCount.get()); assertEquals(List.of(reportInfos), executor.publishedReports()); - assertEquals(2, executor.publishedSteps().size()); + assertEquals( + 2, + executor.publishedSteps() + .size()); - PublishedStep running = executor.publishedSteps().get(0); + PublishedStep running = executor.publishedSteps() + .get(0); assertEquals(processExecutionId, running.processExecutionId()); - assertEquals(StepStatus.RUNNING, running.step().getStatus()); - assertEquals(reportId, running.step().getReportId()); - assertNull(running.step().getCompletedAt()); + assertEquals( + StepStatus.RUNNING, + running.step() + .getStatus()); + assertEquals( + reportId, + running.step() + .getReportId()); + assertNull(running.step() + .getCompletedAt()); - PublishedStep completed = executor.publishedSteps().get(1); + PublishedStep completed = executor.publishedSteps() + .get(1); assertEquals(processExecutionId, completed.processExecutionId()); - assertEquals(StepStatus.COMPLETED, completed.step().getStatus()); - assertEquals(resultId, completed.step().getResultId()); - assertEquals(ResultType.SECURITY_ANALYSIS, completed.step().getResultType()); - assertEquals(reportId, completed.step().getReportId()); - assertNotNull(completed.step().getCompletedAt()); + assertEquals( + StepStatus.COMPLETED, + completed.step() + .getStatus()); + assertEquals( + resultId, + completed.step() + .getResultId()); + assertEquals( + ResultType.SECURITY_ANALYSIS, + completed.step() + .getResultType()); + assertEquals( + reportId, + completed.step() + .getReportId()); + assertNotNull(completed.step() + .getCompletedAt()); } @Test @@ -102,43 +150,62 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { UUID reportId = UUID.randomUUID(); Instant startedAt = Instant.now(); - RuntimeException failure = new RuntimeException("boom"); - - RuntimeException thrown = assertThrows(RuntimeException.class, () -> executor.executeStep( - processExecutionId, - stepExecutionId, - "FAILING_STEP", - 3, - startedAt, - reportId, - new ReportInfos(reportId, null), - null, - () -> { - throw failure; - } - )); + ReportInfos reportInfos = new ReportInfos(reportId, null); + RuntimeException failure = new RuntimeException("exp"); + + Runnable failingExecution = () -> { + throw failure; + }; + + RuntimeException thrown = assertThrows( + RuntimeException.class, + () -> executeFailingStep( + executor, + processExecutionId, + stepExecutionId, + startedAt, + reportId, + reportInfos, + failingExecution)); assertSame(failure, thrown); - assertTrue(executor.publishedReports().isEmpty()); - assertEquals(2, executor.publishedSteps().size()); - - PublishedStep running = executor.publishedSteps().get(0); - assertEquals(StepStatus.RUNNING, running.step().getStatus()); - - PublishedStep failed = executor.publishedSteps().get(1); - assertEquals(StepStatus.FAILED, failed.step().getStatus()); - assertNull(failed.step().getResultId()); - assertNull(failed.step().getResultType()); - assertNotNull(failed.step().getCompletedAt()); + assertTrue(executor.publishedReports() + .isEmpty()); + assertEquals( + 2, + executor.publishedSteps() + .size()); + + PublishedStep running = executor.publishedSteps() + .get(0); + assertEquals( + StepStatus.RUNNING, + running.step() + .getStatus()); + + PublishedStep failed = executor.publishedSteps() + .get(1); + assertEquals( + StepStatus.FAILED, + failed.step() + .getStatus()); + assertNull(failed.step() + .getResultId()); + assertNull(failed.step() + .getResultType()); + assertNotNull(failed.step() + .getCompletedAt()); } private static final class TestStepExecutor extends AbstractStepExecutor { + private final List publishedSteps = new ArrayList<>(); private final List publishedReports = new ArrayList<>(); private TestStepExecutor() { - this.stepStatusPublisher = (executionId, processExecutionStep) -> - publishedSteps.add(new PublishedStep(executionId, processExecutionStep)); + this.stepStatusPublisher = (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( + executionId, + processExecutionStep)); this.reportPublisher = publishedReports::add; } @@ -151,6 +218,26 @@ List publishedReports() { } } - private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { + private static void executeFailingStep( + TestStepExecutor executor, + UUID processExecutionId, + UUID stepExecutionId, + Instant startedAt, + UUID reportId, + ReportInfos reportInfos, + Runnable failingExecution + ) { + executor.executeStep( + processExecutionId, + stepExecutionId, + "FAILING_STEP", + 3, + startedAt, + reportId, + reportInfos, + null, + failingExecution); } + + private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { } } From 939a427862363bb7d38b524dd47c586e180612f1 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 10:28:17 +0100 Subject: [PATCH 09/34] set Step and report publishers in the constructor of the parent class Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 10 +++++---- .../commons/steps/StepExecutorTest.java | 21 ++++++++++++------- .../server/services/StepExecutionService.java | 3 +-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 2e4d6506..4ea0db2a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,11 +19,13 @@ */ public abstract class AbstractStepExecutor { - protected AbstractStepExecutor() { - } + protected final StepStatusPublisher stepStatusPublisher; + protected final ReportPublisher reportPublisher; - protected StepStatusPublisher stepStatusPublisher; - protected ReportPublisher reportPublisher; + protected AbstractStepExecutor(StepStatusPublisher stepStatusPublisher, ReportPublisher reportPublisher) { + this.stepStatusPublisher = stepStatusPublisher; + this.reportPublisher = reportPublisher; + } public void skipStep( UUID processExecutionId, diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java index 8bf6e9a7..fbfd4ada 100644 --- a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -158,8 +158,7 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { }; RuntimeException thrown = assertThrows( - RuntimeException.class, - () -> executeFailingStep( + RuntimeException.class, () -> executeFailingStep( executor, processExecutionId, stepExecutionId, @@ -199,14 +198,20 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { private static final class TestStepExecutor extends AbstractStepExecutor { - private final List publishedSteps = new ArrayList<>(); - private final List publishedReports = new ArrayList<>(); + private final List publishedSteps; + private final List publishedReports; private TestStepExecutor() { - this.stepStatusPublisher = (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( - executionId, - processExecutionStep)); - this.reportPublisher = publishedReports::add; + this(new ArrayList<>(), new ArrayList<>()); + } + + private TestStepExecutor(List publishedSteps, List publishedReports) { + super( + (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( + executionId, + processExecutionStep)), publishedReports::add); + this.publishedSteps = publishedSteps; + this.publishedReports = publishedReports; } List publishedSteps() { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 0a1e46de..e9baac9e 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -21,8 +21,7 @@ public class StepExecutionService extends AbstractStepExecutor { public StepExecutionService(NotificationService notificationService, ReportService reportService) { - this.stepStatusPublisher = notificationService::updateStepStatus; - this.reportPublisher = reportService::sendReport; + super(notificationService::updateStepStatus, reportService::sendReport); } public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { From 52e6c9eee9ce121c77fc63d727461f6d835f70ab Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:21:15 +0100 Subject: [PATCH 10/34] activate gpg Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../org/gridsuite/monitor/commons/steps/ReportPublisher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index c1600c80..8d6e250a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -15,4 +15,5 @@ public interface ReportPublisher { void sendReport(ReportInfos reportInfos); + } From b5225367bcfa7b68a364ddcd96651a788d6f7986 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:35:24 +0100 Subject: [PATCH 11/34] retry gpg Signed-off-by: benrejebmoh --- .../org/gridsuite/monitor/commons/steps/ReportPublisher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index 8d6e250a..c1600c80 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -15,5 +15,4 @@ public interface ReportPublisher { void sendReport(ReportInfos reportInfos); - } From 19542432265da843b9601d7575d832333d1216e6 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 24 Feb 2026 16:45:24 +0100 Subject: [PATCH 12/34] extract step execution to commons Signed-off-by: benrejebmoh --- .../commons/steps/ReportPublisher.java | 6 +- .../commons/steps/StepExecutionInterface.java | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index c1600c80..bb119d8a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,13 +6,11 @@ */ package org.gridsuite.monitor.commons.steps; -import org.gridsuite.monitor.commons.ReportInfos; - /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { - void sendReport(ReportInfos reportInfos); + void sendReport(R reportInfos); } diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java new file mode 100644 index 00000000..d232b114 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.StepStatus; + +import java.time.Instant; +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +public interface StepExecutionInterface { + + UUID getExecutionId(); + + void setExecutionId(UUID executionId); + + StepStatusPublisher getStepStatusPublisher(); + + ReportPublisher getReportPublisher(); + + default void skipStep(UUID stepExecutionId, String stepTypeName, int stepOrder, Instant startedAt) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.SKIPPED, + null, + null, + null, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + } + + default void executeStep(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + R reportInfos, + ResultInfos resultInfos, + Runnable stepExecution) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.RUNNING, + null, + null, + reportUuid, + startedAt, + null + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + + try { + stepExecution.run(); + getReportPublisher().sendReport(reportInfos); + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.COMPLETED); + } catch (Exception e) { + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.FAILED); + throw e; + } + } + + default void updateStepStatus(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + ResultInfos resultInfos, + StepStatus status) { + ProcessExecutionStep updated = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + status, + resultInfos != null ? resultInfos.resultUUID() : null, + resultInfos != null ? resultInfos.resultType() : null, + reportUuid, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), updated); + } +} From 6ba3d440a75e5300bf82335669052a3182b86118 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:21:15 +0100 Subject: [PATCH 13/34] activate gpg Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../gridsuite/monitor/commons/steps/ReportPublisher.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index bb119d8a..8d6e250a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,11 +6,14 @@ */ package org.gridsuite.monitor.commons.steps; +import org.gridsuite.monitor.commons.ReportInfos; + /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { + + void sendReport(ReportInfos reportInfos); - void sendReport(R reportInfos); } From 4fc49c6206b7e9c1eb66a5ea506e95877c1c2f80 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 14:26:51 +0100 Subject: [PATCH 14/34] finishing rebase properly Signed-off-by: benrejebmoh --- .../commons/steps/StepExecutionInterface.java | 95 ------------------- 1 file changed, 95 deletions(-) delete mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java deleted file mode 100644 index d232b114..00000000 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2026, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.gridsuite.monitor.commons.steps; - -import org.gridsuite.monitor.commons.ProcessExecutionStep; -import org.gridsuite.monitor.commons.ResultInfos; -import org.gridsuite.monitor.commons.StepStatus; - -import java.time.Instant; -import java.util.UUID; - -/** - * @author Mohamed Ben-rejeb - */ -public interface StepExecutionInterface { - - UUID getExecutionId(); - - void setExecutionId(UUID executionId); - - StepStatusPublisher getStepStatusPublisher(); - - ReportPublisher getReportPublisher(); - - default void skipStep(UUID stepExecutionId, String stepTypeName, int stepOrder, Instant startedAt) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.SKIPPED, - null, - null, - null, - startedAt, - Instant.now() - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); - } - - default void executeStep(UUID stepExecutionId, - String stepTypeName, - int stepOrder, - Instant startedAt, - UUID reportUuid, - R reportInfos, - ResultInfos resultInfos, - Runnable stepExecution) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.RUNNING, - null, - null, - reportUuid, - startedAt, - null - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); - - try { - stepExecution.run(); - getReportPublisher().sendReport(reportInfos); - updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.COMPLETED); - } catch (Exception e) { - updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.FAILED); - throw e; - } - } - - default void updateStepStatus(UUID stepExecutionId, - String stepTypeName, - int stepOrder, - Instant startedAt, - UUID reportUuid, - ResultInfos resultInfos, - StepStatus status) { - ProcessExecutionStep updated = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - status, - resultInfos != null ? resultInfos.resultUUID() : null, - resultInfos != null ? resultInfos.resultType() : null, - reportUuid, - startedAt, - Instant.now() - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), updated); - } -} From ac06bc1cf9d84326535987b6d5f3f7a523641f49 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 24 Feb 2026 16:45:24 +0100 Subject: [PATCH 15/34] extract step execution to commons Signed-off-by: benrejebmoh --- .../commons/steps/ReportPublisher.java | 6 +- .../commons/steps/StepExecutionInterface.java | 95 +++++++++++++++++++ .../server/services/StepExecutionService.java | 22 +++-- 3 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index c1600c80..bb119d8a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,13 +6,11 @@ */ package org.gridsuite.monitor.commons.steps; -import org.gridsuite.monitor.commons.ReportInfos; - /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { - void sendReport(ReportInfos reportInfos); + void sendReport(R reportInfos); } diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java new file mode 100644 index 00000000..d232b114 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.StepStatus; + +import java.time.Instant; +import java.util.UUID; + +/** + * @author Mohamed Ben-rejeb + */ +public interface StepExecutionInterface { + + UUID getExecutionId(); + + void setExecutionId(UUID executionId); + + StepStatusPublisher getStepStatusPublisher(); + + ReportPublisher getReportPublisher(); + + default void skipStep(UUID stepExecutionId, String stepTypeName, int stepOrder, Instant startedAt) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.SKIPPED, + null, + null, + null, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + } + + default void executeStep(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + R reportInfos, + ResultInfos resultInfos, + Runnable stepExecution) { + ProcessExecutionStep executionStep = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + StepStatus.RUNNING, + null, + null, + reportUuid, + startedAt, + null + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); + + try { + stepExecution.run(); + getReportPublisher().sendReport(reportInfos); + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.COMPLETED); + } catch (Exception e) { + updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.FAILED); + throw e; + } + } + + default void updateStepStatus(UUID stepExecutionId, + String stepTypeName, + int stepOrder, + Instant startedAt, + UUID reportUuid, + ResultInfos resultInfos, + StepStatus status) { + ProcessExecutionStep updated = new ProcessExecutionStep( + stepExecutionId, + stepTypeName, + stepOrder, + status, + resultInfos != null ? resultInfos.resultUUID() : null, + resultInfos != null ? resultInfos.resultType() : null, + reportUuid, + startedAt, + Instant.now() + ); + getStepStatusPublisher().updateStepStatus(getExecutionId(), updated); + } +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 367e37a1..5a08035f 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,24 +6,32 @@ */ package org.gridsuite.monitor.worker.server.services; +import lombok.Getter; +import lombok.Setter; import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.commons.steps.ReportPublisher; -import org.gridsuite.monitor.commons.steps.StepExecution; +import org.gridsuite.monitor.commons.steps.StepExecutionInterface; import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; +import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.springframework.stereotype.Service; import java.util.Objects; +import java.util.UUID; /** * @author Antoine Bouhours */ @Service -public class StepExecutionService implements StepExecution { +public class StepExecutionService implements StepExecutionInterface { + + @Getter + @Setter + private UUID executionId; private final StepStatusPublisher stepStatusPublisher; - private final ReportPublisher reportPublisher; + private final ReportPublisher reportPublisher; public StepExecutionService(NotificationService notificationService, ReportService reportService) { this.stepStatusPublisher = notificationService::updateStepStatus; @@ -36,12 +44,13 @@ public StepStatusPublisher getStepStatusPublisher() { } @Override - public ReportPublisher getReportPublisher() { + public ReportPublisher getReportPublisher() { return reportPublisher; } public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - skipStep(context.getProcessExecutionId(), + setExecutionId(context.getProcessExecutionId()); + skipStep( context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), @@ -50,7 +59,8 @@ public void skipStep(ProcessStepExecutionContext context, ProcessStep step } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - executeStep(context.getProcessExecutionId(), + setExecutionId(context.getProcessExecutionId()); + executeStep( context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), From 76fbd0e6c2e86c604946e2288a6953161771e7d1 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 10:38:17 +0100 Subject: [PATCH 16/34] use abstract class intead of interface Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- ...ecution.java => AbstractStepExecutor.java} | 12 +++++----- .../server/services/StepExecutionService.java | 22 +++++-------------- 2 files changed, 12 insertions(+), 22 deletions(-) rename monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/{StepExecution.java => AbstractStepExecutor.java} (92%) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java similarity index 92% rename from monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java rename to monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index ab3a4cbc..4f836c21 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecution.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -17,13 +17,13 @@ /** * @author Mohamed Ben-rejeb */ -public interface StepExecution { +public abstract class AbstractStepExecutor { - StepStatusPublisher getStepStatusPublisher(); + protected abstract StepStatusPublisher getStepStatusPublisher(); - ReportPublisher getReportPublisher(); + protected abstract ReportPublisher getReportPublisher(); - default void skipStep( + public void skipStep( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, @@ -43,7 +43,7 @@ default void skipStep( getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); } - default void executeStep( + public void executeStep( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, @@ -92,7 +92,7 @@ default void executeStep( } } - default void updateStepStatus( + private void updateStepStatus( UUID processExecutionId, UUID stepExecutionId, String stepTypeName, diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 5a08035f..50256341 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,32 +6,24 @@ */ package org.gridsuite.monitor.worker.server.services; -import lombok.Getter; -import lombok.Setter; import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.commons.steps.ReportPublisher; -import org.gridsuite.monitor.commons.steps.StepExecutionInterface; +import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.springframework.stereotype.Service; import java.util.Objects; -import java.util.UUID; /** * @author Antoine Bouhours */ @Service -public class StepExecutionService implements StepExecutionInterface { - - @Getter - @Setter - private UUID executionId; +public class StepExecutionService extends AbstractStepExecutor { private final StepStatusPublisher stepStatusPublisher; - private final ReportPublisher reportPublisher; + private final ReportPublisher reportPublisher; public StepExecutionService(NotificationService notificationService, ReportService reportService) { this.stepStatusPublisher = notificationService::updateStepStatus; @@ -44,13 +36,12 @@ public StepStatusPublisher getStepStatusPublisher() { } @Override - public ReportPublisher getReportPublisher() { + public ReportPublisher getReportPublisher() { return reportPublisher; } public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - setExecutionId(context.getProcessExecutionId()); - skipStep( + skipStep(context.getProcessExecutionId(), context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), @@ -59,8 +50,7 @@ public void skipStep(ProcessStepExecutionContext context, ProcessStep step } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - setExecutionId(context.getProcessExecutionId()); - executeStep( + executeStep(context.getProcessExecutionId(), context.getStepExecutionId(), step.getType().getName(), context.getStepOrder(), From 260bca00a6600f87c5876a844ab2d2663eaa4bf3 Mon Sep 17 00:00:00 2001 From: klesaulnier <42617371+klesaulnier@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:15:11 +0100 Subject: [PATCH 17/34] Add debug mode to process execution (#40) Signed-off-by: LE SAULNIER Kevin --- .../monitor/commons/ProcessRunMessage.java | 3 +- monitor-server/pom.xml | 6 + .../server/config/S3Configuration.java | 35 +++++ .../server/controllers/MonitorController.java | 21 ++- .../entities/ProcessExecutionEntity.java | 4 +- .../server/services/MonitorService.java | 39 +++++- .../server/services/NotificationService.java | 4 +- .../server/services/S3RestService.java | 89 ++++++++++++ .../monitor/server/utils/S3PathResolver.java | 35 +++++ .../src/main/resources/application-local.yaml | 4 + .../changesets/changelog_20260212T082157Z.xml | 8 ++ .../db/changelog/db.changelog-master.yaml | 4 +- .../server/MonitorIntegrationTest.java | 11 +- .../controllers/MonitorControllerTest.java | 68 +++++++-- .../ProcessExecutionRepositoryTest.java | 4 + .../server/services/MonitorServiceTest.java | 89 ++++++++++-- .../services/NotificationServiceTest.java | 6 +- .../server/services/S3RestServiceTest.java | 129 ++++++++++++++++++ .../src/test/resources/application.yaml | 4 + monitor-worker-server/pom.xml | 6 + .../worker/server/config/S3Configuration.java | 35 +++++ .../server/core/ProcessExecutionContext.java | 4 +- .../core/ProcessStepExecutionContext.java | 4 + .../commons/steps/ApplyModificationsStep.java | 42 +++++- .../services/ProcessExecutionService.java | 3 +- .../worker/server/services/S3RestService.java | 45 ++++++ .../worker/server/services/S3Service.java | 67 +++++++++ .../worker/server/utils/S3PathResolver.java | 24 ++++ .../src/main/resources/application-local.yaml | 4 + .../core/ProcessExecutionContextTest.java | 7 +- .../core/ProcessStepExecutionContextTest.java | 2 + .../steps/ApplyModificationsStepTest.java | 63 +++++++-- .../server/services/ConsumerServiceTest.java | 2 +- .../services/ProcessExecutionServiceTest.java | 11 +- .../server/services/S3RestServiceTest.java | 90 ++++++++++++ .../worker/server/services/S3ServiceTest.java | 113 +++++++++++++++ 36 files changed, 1019 insertions(+), 66 deletions(-) create mode 100644 monitor-server/src/main/java/org/gridsuite/monitor/server/config/S3Configuration.java create mode 100644 monitor-server/src/main/java/org/gridsuite/monitor/server/services/S3RestService.java create mode 100644 monitor-server/src/main/java/org/gridsuite/monitor/server/utils/S3PathResolver.java create mode 100644 monitor-server/src/main/resources/db/changelog/changesets/changelog_20260212T082157Z.xml create mode 100644 monitor-server/src/test/java/org/gridsuite/monitor/server/services/S3RestServiceTest.java create mode 100644 monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/config/S3Configuration.java create mode 100644 monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3RestService.java create mode 100644 monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3Service.java create mode 100644 monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/utils/S3PathResolver.java create mode 100644 monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3RestServiceTest.java create mode 100644 monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3ServiceTest.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessRunMessage.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessRunMessage.java index 95d5c71c..8873ab16 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessRunMessage.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessRunMessage.java @@ -21,7 +21,8 @@ public record ProcessRunMessage( @JsonSubTypes({ @JsonSubTypes.Type(value = SecurityAnalysisConfig.class, name = "SECURITY_ANALYSIS") }) - T config + T config, + String debugFileLocation ) { public ProcessType processType() { return config.processType(); diff --git a/monitor-server/pom.xml b/monitor-server/pom.xml index 1a53beb3..55e6a136 100644 --- a/monitor-server/pom.xml +++ b/monitor-server/pom.xml @@ -14,6 +14,7 @@ org.gridsuite.monitor.server + 3.3.1 @@ -122,6 +123,11 @@ postgresql runtime + + io.awspring.cloud + spring-cloud-aws-starter-s3 + ${spring-cloud-aws-starter-s3} + diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/config/S3Configuration.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/config/S3Configuration.java new file mode 100644 index 00000000..63712374 --- /dev/null +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/config/S3Configuration.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.config; + +import org.gridsuite.monitor.server.services.S3RestService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.services.s3.S3Client; + +/** + * @author Kevin Le Saulnier + */ +@Configuration +public class S3Configuration { + private static final Logger LOGGER = LoggerFactory.getLogger(S3Configuration.class); + private final String bucketName; + + public S3Configuration(@Value("${spring.cloud.aws.bucket:ws-bucket}") String bucketName) { + this.bucketName = bucketName; + } + + @SuppressWarnings("checkstyle:MethodName") + @Bean + public S3RestService s3RestService(S3Client s3Client) { + LOGGER.info("Configuring S3Service with bucket: {}", bucketName); + return new S3RestService(s3Client, bucketName); + } +} diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java index 65ec8bc0..41933f59 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java @@ -17,6 +17,8 @@ import org.gridsuite.monitor.server.dto.ProcessExecution; import org.gridsuite.monitor.server.dto.ReportPage; import org.gridsuite.monitor.server.services.MonitorService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -44,9 +46,10 @@ public MonitorController(MonitorService monitorService) { @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis execution has been started")}) public ResponseEntity executeSecurityAnalysis( @RequestParam UUID caseUuid, + @RequestParam(required = false, defaultValue = "false") boolean isDebug, @RequestBody SecurityAnalysisConfig securityAnalysisConfig, @RequestHeader(HEADER_USER_ID) String userId) { - UUID executionId = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig); + UUID executionId = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig, isDebug); return ResponseEntity.ok(executionId); } @@ -83,6 +86,22 @@ public ResponseEntity> getStepsInfos(@Parameter(descr .orElseGet(() -> ResponseEntity.notFound().build()); } + @GetMapping("/executions/{executionId}/debug-infos") + @Operation(summary = "Get execution debug file") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Debug file downloaded"), + @ApiResponse(responseCode = "404", description = "execution id was not found")}) + public ResponseEntity getDebugInfos(@Parameter(description = "Execution UUID") @PathVariable UUID executionId) { + return monitorService.getDebugInfos(executionId) + .map(bytes -> ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"archive.zip\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .contentLength(bytes.length) + .body(bytes)) + .orElseGet(() -> ResponseEntity.notFound().build()); + } + @DeleteMapping("/executions/{executionId}") @Operation(summary = "Delete an execution") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Execution was deleted"), diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionEntity.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionEntity.java index eaaeaa1c..698137b8 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionEntity.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionEntity.java @@ -27,7 +27,6 @@ public class ProcessExecutionEntity { @Id - @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @Column @@ -55,6 +54,9 @@ public class ProcessExecutionEntity { @Column private String userId; + @Column + private String debugFileLocation; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name = "execution_id", foreignKey = @ForeignKey(name = "processExecutionStep_processExecution_fk")) @OrderBy("stepOrder ASC") diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java index d0722e50..d299114e 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java @@ -6,11 +6,13 @@ */ package org.gridsuite.monitor.server.services; +import com.powsybl.commons.PowsyblException; import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.commons.ProcessExecutionStep; import org.gridsuite.monitor.commons.ProcessStatus; import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.server.utils.S3PathResolver; import org.gridsuite.monitor.server.dto.ProcessExecution; import org.gridsuite.monitor.server.dto.ReportPage; import org.gridsuite.monitor.server.entities.ProcessExecutionEntity; @@ -21,11 +23,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; /** * @author Antoine Bouhours @@ -37,29 +37,40 @@ public class MonitorService { private final NotificationService notificationService; private final ReportService reportService; private final ResultService resultService; + private final S3RestService s3RestService; + private final S3PathResolver s3PathResolver; public MonitorService(ProcessExecutionRepository executionRepository, NotificationService notificationService, ReportService reportService, - ResultService resultService) { + ResultService resultService, + S3RestService s3RestService, + S3PathResolver s3PathResolver) { this.executionRepository = executionRepository; this.notificationService = notificationService; this.reportService = reportService; this.resultService = resultService; + this.s3RestService = s3RestService; + this.s3PathResolver = s3PathResolver; } @Transactional - public UUID executeProcess(UUID caseUuid, String userId, ProcessConfig processConfig) { + public UUID executeProcess(UUID caseUuid, String userId, ProcessConfig processConfig, boolean isDebug) { + UUID executionId = UUID.randomUUID(); ProcessExecutionEntity execution = ProcessExecutionEntity.builder() + .id(executionId) .type(processConfig.processType().name()) .caseUuid(caseUuid) .status(ProcessStatus.SCHEDULED) .scheduledAt(Instant.now()) .userId(userId) .build(); + if (isDebug) { + execution.setDebugFileLocation(s3PathResolver.toDebugLocation(processConfig.processType().name(), executionId)); + } executionRepository.save(execution); - notificationService.sendProcessRunMessage(caseUuid, processConfig, execution.getId()); + notificationService.sendProcessRunMessage(caseUuid, processConfig, execution.getId(), execution.getDebugFileLocation()); return execution.getId(); } @@ -163,6 +174,20 @@ public List getResults(UUID executionId) { .toList(); } + @Transactional(readOnly = true) + public Optional getDebugInfos(UUID executionId) { + return executionRepository.findById(executionId) + .map(ProcessExecutionEntity::getDebugFileLocation) + .filter(Objects::nonNull) + .map(debugFileLocation -> { + try { + return s3RestService.downloadDirectoryAsZip(debugFileLocation); + } catch (IOException e) { + throw new PowsyblException("An error occurred while downloading debug files", e); + } + }); + } + private List getResultInfos(UUID executionId) { return executionRepository.findById(executionId) .map(execution -> Optional.ofNullable(execution.getSteps()).orElse(List.of()).stream() diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java index a6ca9558..49202af8 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java @@ -23,11 +23,11 @@ public class NotificationService { private final StreamBridge publisher; - public void sendProcessRunMessage(UUID caseUuid, ProcessConfig processConfig, UUID executionId) { + public void sendProcessRunMessage(UUID caseUuid, ProcessConfig processConfig, UUID executionId, String debugFileLocation) { String bindingName = switch (processConfig.processType()) { case SECURITY_ANALYSIS -> "publishRunSecurityAnalysis-out-0"; }; - ProcessRunMessage message = new ProcessRunMessage<>(executionId, caseUuid, processConfig); + ProcessRunMessage message = new ProcessRunMessage<>(executionId, caseUuid, processConfig, debugFileLocation); publisher.send(bindingName, message); } } diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/S3RestService.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/S3RestService.java new file mode 100644 index 00000000..0c5b62d5 --- /dev/null +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/S3RestService.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * @author Kevin Le Saulnier + */ +public class S3RestService { + private final S3Client s3Client; + + private final String bucketName; + + public S3RestService(S3Client s3Client, String bucketName) { + this.s3Client = s3Client; + this.bucketName = bucketName; + } + + /** + * We did not use downloadDirectory from s3 methods here because it downloads all files on device directly instead of letting us redirect the stream into zip stream + */ + public byte[] downloadDirectoryAsZip(String directoryKey) throws IOException { + List filesKeys = getFilesKeysInDirectory(directoryKey); + + return buildZip( + directoryKey, + filesKeys, + key -> s3Client.getObject(GetObjectRequest.builder() + .bucket(bucketName) + .key(key) + .build()) + ); + } + + byte[] buildZip(String directoryKey, List filesS3Keys, Function s3ObjectFetcher) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { + for (String fileS3Key : filesS3Keys) { + if (fileS3Key.endsWith("/")) { + // s3 files with key endpoint with "/" are most of the time DIROBJ, empty s3 objects that simulate directory for users + // we ignore them here to prevent errors + continue; + } + String zipEntryName = fileS3Key.substring(directoryKey.length() + 1); + zipOutputStream.putNextEntry(new ZipEntry(zipEntryName)); + try (InputStream in = s3ObjectFetcher.apply(fileS3Key)) { + in.transferTo(zipOutputStream); + } + zipOutputStream.closeEntry(); + } + } + + return outputStream.toByteArray(); + } + + List getFilesKeysInDirectory(String directoryKey) { + List filesS3Keys = new ArrayList<>(); + ListObjectsV2Request request = ListObjectsV2Request.builder() + .bucket(bucketName) + .prefix(directoryKey) + .build(); + ListObjectsV2Response response; + do { + response = s3Client.listObjectsV2(request); + response.contents().forEach(obj -> filesS3Keys.add(obj.key())); + + request = request.toBuilder() + .continuationToken(response.nextContinuationToken()) + .build(); + } while (Boolean.TRUE.equals(response.isTruncated())); // S3 pagination, this loop ends if this is the last page + + return filesS3Keys; + } +} diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/utils/S3PathResolver.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/utils/S3PathResolver.java new file mode 100644 index 00000000..7ab08471 --- /dev/null +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/utils/S3PathResolver.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +/** + * @author Kevin Le Saulnier + */ +@Component +public final class S3PathResolver { + private final String s3RootPath; + + public S3PathResolver(@Value("${powsybl-ws.s3.subpath.prefix:}${debug-subpath:debug}") String s3RootPath) { + this.s3RootPath = s3RootPath; + } + + /** + * Builds root path used to build debug file location + * @param processType + * @param executionId + * @return {executionEnvName}_debug/process/{processType}/{executionId} + */ + public String toDebugLocation(String processType, UUID executionId) { + String s3Delimiter = "/"; + return String.join(s3Delimiter, s3RootPath, "process", processType, executionId.toString()); + } +} diff --git a/monitor-server/src/main/resources/application-local.yaml b/monitor-server/src/main/resources/application-local.yaml index a72bc10d..248fecfc 100644 --- a/monitor-server/src/main/resources/application-local.yaml +++ b/monitor-server/src/main/resources/application-local.yaml @@ -4,6 +4,10 @@ server: spring: rabbitmq: addresses: localhost + cloud: + aws: + s3: + endpoint: http://localhost:19000 powsybl-ws: database: diff --git a/monitor-server/src/main/resources/db/changelog/changesets/changelog_20260212T082157Z.xml b/monitor-server/src/main/resources/db/changelog/changesets/changelog_20260212T082157Z.xml new file mode 100644 index 00000000..bad7ffdc --- /dev/null +++ b/monitor-server/src/main/resources/db/changelog/changesets/changelog_20260212T082157Z.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml b/monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml index 44dd6086..e8f62f7e 100644 --- a/monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml @@ -8,4 +8,6 @@ databaseChangeLog: - include: file: changesets/changelog_20260130T160426Z.xml relativeToChangelogFile: true - + - include: + file: changesets/changelog_20260212T082157Z.xml + relativeToChangelogFile: true diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java index 3111c7c3..8f051fa9 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java @@ -14,11 +14,7 @@ import org.gridsuite.monitor.server.entities.ProcessExecutionEntity; import org.gridsuite.monitor.server.repositories.ProcessConfigRepository; import org.gridsuite.monitor.server.repositories.ProcessExecutionRepository; -import org.gridsuite.monitor.server.services.ConsumerService; -import org.gridsuite.monitor.server.services.ProcessConfigService; -import org.gridsuite.monitor.server.services.ReportService; -import org.gridsuite.monitor.server.services.MonitorService; -import org.gridsuite.monitor.server.services.ResultService; +import org.gridsuite.monitor.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -83,6 +79,9 @@ class MonitorIntegrationTest { @MockitoBean private ResultService resultService; + @MockitoBean + private S3RestService s3RestService; + private UUID caseUuid; private String userId; @@ -102,7 +101,7 @@ void securityAnalysisProcessIT() throws Exception { UUID.randomUUID(), List.of("contingency1", "contingency2"), List.of(UUID.randomUUID())); - UUID executionId = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig); + UUID executionId = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig, false); // Verify message was published Message sentMessage = outputDestination.receive(1000, PROCESS_SA_RUN_DESTINATION); diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java index 18c6a10d..905e0bbc 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java @@ -18,11 +18,16 @@ import org.gridsuite.monitor.server.dto.Severity; import org.gridsuite.monitor.server.services.MonitorService; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.time.Instant; import java.util.List; @@ -54,8 +59,10 @@ class MonitorControllerTest { @MockitoBean private MonitorService monitorService; - @Test - void executeSecurityAnalysisShouldReturnExecutionId() throws Exception { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + @NullSource + void executeSecurityAnalysisShouldReturnExecutionId(Boolean isDebug) throws Exception { UUID caseUuid = UUID.randomUUID(); UUID parametersUuid = UUID.randomUUID(); UUID modificationUuid = UUID.randomUUID(); @@ -65,20 +72,27 @@ void executeSecurityAnalysisShouldReturnExecutionId() throws Exception { List.of("contingency1", "contingency2"), List.of(modificationUuid) ); + boolean expectedDebugValue = Boolean.TRUE.equals(isDebug); - when(monitorService.executeProcess(any(UUID.class), any(String.class), any(SecurityAnalysisConfig.class))) + when(monitorService.executeProcess(any(UUID.class), any(String.class), any(SecurityAnalysisConfig.class), eq(expectedDebugValue))) .thenReturn(executionId); - mockMvc.perform(post("/v1/execute/security-analysis") - .param("caseUuid", caseUuid.toString()) - .header("userId", "user1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(config))) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$").value(executionId.toString())); + MockHttpServletRequestBuilder request = post("/v1/execute/security-analysis") + .param("caseUuid", caseUuid.toString()) + .header("userId", "user1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(config)); + + if (isDebug != null) { + request.param("isDebug", isDebug.toString()); + } + + mockMvc.perform(request) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$").value(executionId.toString())); - verify(monitorService).executeProcess(eq(caseUuid), any(String.class), any(SecurityAnalysisConfig.class)); + verify(monitorService).executeProcess(eq(caseUuid), any(String.class), any(SecurityAnalysisConfig.class), eq(expectedDebugValue)); } @Test @@ -208,4 +222,34 @@ void deleteExecutionShouldReturnFalse() throws Exception { verify(monitorService).deleteExecution(executionId); } + + @Test + void getDebugFilesReturnsOK() throws Exception { + UUID executionId = UUID.randomUUID(); + byte[] zipContent = "dummy-zip-content".getBytes(); + + when(monitorService.getDebugInfos(executionId)) + .thenReturn(Optional.of(zipContent)); + + mockMvc.perform(get("/v1/executions/{executionId}/debug-infos", executionId)) + .andExpect(status().isOk()) + .andExpect(header().string( + HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"archive.zip\"")) + .andExpect(header().string( + HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andExpect(header().longValue( + HttpHeaders.CONTENT_LENGTH, + zipContent.length)) + .andExpect(content().bytes(zipContent)); + } + + @Test + void getDebugFilesReturnsNotFound() throws Exception { + when(monitorService.getDebugInfos(any())).thenReturn(Optional.empty()); + + mockMvc.perform(get("/v1/executions/{executionId}/debug-infos", UUID.randomUUID())) + .andExpect(status().isNotFound()); + } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/repositories/ProcessExecutionRepositoryTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/repositories/ProcessExecutionRepositoryTest.java index 4a62c4e8..ba1eb9f1 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/repositories/ProcessExecutionRepositoryTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/repositories/ProcessExecutionRepositoryTest.java @@ -39,6 +39,7 @@ class ProcessExecutionRepositoryTest { @Test void stepsShouldBeOrderedByStepOrder() { ProcessExecutionEntity execution = ProcessExecutionEntity.builder() + .id(UUID.randomUUID()) .type("SECURITY_ANALYSIS") .caseUuid(UUID.randomUUID()) .status(ProcessStatus.RUNNING) @@ -86,6 +87,7 @@ void securityAnalysisLaunchedProcesses() { Instant startedAt1 = Instant.now().minusSeconds(30); Instant completedAt1 = Instant.now(); ProcessExecutionEntity execution1 = ProcessExecutionEntity.builder() + .id(UUID.randomUUID()) .type(ProcessType.SECURITY_ANALYSIS.name()) .caseUuid(case1Uuid) .status(ProcessStatus.COMPLETED) @@ -100,6 +102,7 @@ void securityAnalysisLaunchedProcesses() { Instant scheduledAt2 = Instant.now().minusSeconds(90); Instant startedAt2 = Instant.now().minusSeconds(20); ProcessExecutionEntity execution2 = ProcessExecutionEntity.builder() + .id(UUID.randomUUID()) .type(ProcessType.SECURITY_ANALYSIS.name()) .caseUuid(case2Uuid) .status(ProcessStatus.RUNNING) @@ -112,6 +115,7 @@ void securityAnalysisLaunchedProcesses() { UUID case3Uuid = UUID.randomUUID(); Instant scheduledAt3 = Instant.now().minusSeconds(90); ProcessExecutionEntity execution3 = ProcessExecutionEntity.builder() + .id(UUID.randomUUID()) .type(ProcessType.SECURITY_ANALYSIS.name()) .caseUuid(case3Uuid) .status(ProcessStatus.SCHEDULED) diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java index fd1c7129..f3a8e9c3 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java @@ -6,7 +6,9 @@ */ package org.gridsuite.monitor.server.services; +import com.powsybl.commons.PowsyblException; import org.gridsuite.monitor.commons.*; +import org.gridsuite.monitor.server.utils.S3PathResolver; import org.gridsuite.monitor.server.dto.ProcessExecution; import org.gridsuite.monitor.server.dto.ReportLog; import org.gridsuite.monitor.server.dto.ReportPage; @@ -21,6 +23,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -28,6 +31,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -49,6 +53,12 @@ class MonitorServiceTest { @Mock private ResultService resultService; + @Mock + private S3RestService s3RestService; + + @Mock + private S3PathResolver s3PathResolver; + @InjectMocks private MonitorService monitorService; @@ -71,15 +81,10 @@ void setUp() { @Test void executeProcessCreateExecutionAndSendNotification() { - UUID expectedExecutionId = UUID.randomUUID(); - when(executionRepository.save(any(ProcessExecutionEntity.class))) - .thenAnswer(invocation -> { - ProcessExecutionEntity entity = invocation.getArgument(0); - entity.setId(expectedExecutionId); // mock id generation - return entity; - }); + String debugFileLocation = "debug/file/location"; + when(s3PathResolver.toDebugLocation(eq(ProcessType.SECURITY_ANALYSIS.name()), any(UUID.class))).thenReturn(debugFileLocation); - UUID result = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig); + UUID result = monitorService.executeProcess(caseUuid, userId, securityAnalysisConfig, true); assertThat(result).isNotNull(); verify(executionRepository).save(argThat(execution -> @@ -94,8 +99,10 @@ void executeProcessCreateExecutionAndSendNotification() { verify(notificationService).sendProcessRunMessage( caseUuid, securityAnalysisConfig, - result + result, + debugFileLocation ); + verify(s3PathResolver).toDebugLocation(eq(ProcessType.SECURITY_ANALYSIS.name()), any(UUID.class)); } @Test @@ -551,4 +558,68 @@ void deleteExecutionShouldReturnFalseWhenExecutionNotFound() { verifyNoInteractions(resultService); verify(executionRepository, never()).deleteById(executionId); } + + @Test + void getExistingDebugInfo() throws Exception { + ProcessExecutionEntity execution = new ProcessExecutionEntity(); + execution.setDebugFileLocation("debug/file/location"); + byte[] expectedBytes = "zip-content".getBytes(); + + when(executionRepository.findById(executionId)).thenReturn(Optional.of(execution)); + when(s3RestService.downloadDirectoryAsZip("debug/file/location")).thenReturn(expectedBytes); + + Optional result = monitorService.getDebugInfos(executionId); + + assertThat(result).isPresent(); + assertThat(expectedBytes).isEqualTo(result.get()); + + verify(executionRepository).findById(executionId); + verify(s3RestService).downloadDirectoryAsZip("debug/file/location"); + } + + @Test + void getNotExistingExecutionDebugInfo() { + when(executionRepository.findById(executionId)).thenReturn(Optional.empty()); + + Optional result = monitorService.getDebugInfos(executionId); + + assertThat(result).isEmpty(); + + verify(executionRepository).findById(executionId); + verifyNoInteractions(s3RestService); + } + + @Test + void getExistingDebugInfoError() throws Exception { + ProcessExecutionEntity execution = new ProcessExecutionEntity(); + execution.setDebugFileLocation("debug/file/location"); + + when(executionRepository.findById(executionId)).thenReturn(Optional.of(execution)); + + when(s3RestService.downloadDirectoryAsZip("debug/file/location")).thenThrow(new IOException("S3 error")); + + PowsyblException exception = assertThrows( + PowsyblException.class, + () -> monitorService.getDebugInfos(executionId) + ); + + assertThat(exception.getMessage()).contains("An error occurred while downloading debug files"); + + verify(executionRepository).findById(executionId); + verify(s3RestService).downloadDirectoryAsZip("debug/file/location"); + } + + @Test + void getExecutionWithoutDebugInfo() { + ProcessExecutionEntity execution = new ProcessExecutionEntity(); + + when(executionRepository.findById(executionId)).thenReturn(Optional.of(execution)); + + Optional result = monitorService.getDebugInfos(executionId); + + assertThat(result).isEmpty(); + + verify(executionRepository).findById(executionId); + verifyNoInteractions(s3RestService); + } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/NotificationServiceTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/NotificationServiceTest.java index daed2612..c50c722b 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/NotificationServiceTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/NotificationServiceTest.java @@ -55,14 +55,16 @@ void setUp() { @Test void sendProcessRunMessage() { - notificationService.sendProcessRunMessage(caseUuid, securityAnalysisConfig, executionId); + String debugFileLocation = "debug/file/location"; + notificationService.sendProcessRunMessage(caseUuid, securityAnalysisConfig, executionId, debugFileLocation); verify(publisher).send( eq("publishRunSecurityAnalysis-out-0"), argThat((ProcessRunMessage message) -> message.executionId().equals(executionId) && message.caseUuid().equals(caseUuid) && - message.config().equals(securityAnalysisConfig)) + message.config().equals(securityAnalysisConfig) && + message.debugFileLocation().equals(debugFileLocation)) ); } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/S3RestServiceTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/S3RestServiceTest.java new file mode 100644 index 00000000..fb1c8563 --- /dev/null +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/S3RestServiceTest.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.*; + +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * @author Kevin Le Saulnier + */ +@ExtendWith(MockitoExtension.class) +class S3RestServiceTest { + @InjectMocks + private S3RestService s3RestService; + + @Mock + private S3Client s3Client; + + @Test + void buildZipWithMultipleEntries() throws Exception { + List keys = List.of( + "executionId1/debug/file1.txt", + "executionId1/debug/file2.txt" + ); + + Map fakeData = Map.of( + "executionId1/debug/file1.txt", "content1", + "executionId1/debug/file2.txt", "content2" + ); + + byte[] zipBytes = s3RestService.buildZip( + "executionId1", + keys, + key -> new ByteArrayInputStream(fakeData.get(key).getBytes()) + ); + + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry1 = zis.getNextEntry(); + assertEquals("debug/file1.txt", entry1.getName()); + assertEquals("content1", new String(zis.readAllBytes())); + + ZipEntry entry2 = zis.getNextEntry(); + assertEquals("debug/file2.txt", entry2.getName()); + assertEquals("content2", new String(zis.readAllBytes())); + } + } + + @Test + void getFilesFromS3DirectoryWithPagination() { + S3Object obj1 = S3Object.builder().key("executionId1/debug/file1.txt").build(); + S3Object obj2 = S3Object.builder().key("executionId1/debug/file2.txt").build(); + + ListObjectsV2Response firstPage = ListObjectsV2Response.builder() + .contents(obj1) + .isTruncated(true) + .nextContinuationToken("token1") + .build(); + + ListObjectsV2Response secondPage = ListObjectsV2Response.builder() + .contents(obj2) + .isTruncated(false) + .build(); + + when(s3Client.listObjectsV2(any(ListObjectsV2Request.class))) + .thenReturn(firstPage) + .thenReturn(secondPage); + + List keys = s3RestService.getFilesKeysInDirectory("debug/"); + + assertEquals(2, keys.size()); + assertTrue(keys.contains("executionId1/debug/file1.txt")); + assertTrue(keys.contains("executionId1/debug/file2.txt")); + + verify(s3Client, times(2)).listObjectsV2(any(ListObjectsV2Request.class)); + } + + @Test + void downloadS3DirectoryContentAsZip() throws Exception { + S3Object obj = S3Object.builder() + .key("executionId1/debug/file.txt") + .build(); + + ListObjectsV2Response response = ListObjectsV2Response.builder() + .contents(obj) + .isTruncated(false) + .build(); + + when(s3Client.listObjectsV2(any(ListObjectsV2Request.class))) + .thenReturn(response); + + when(s3Client.getObject(any(GetObjectRequest.class))) + .thenReturn(new ResponseInputStream<>( + GetObjectResponse.builder().build(), + AbortableInputStream.create( + new ByteArrayInputStream("byteContent".getBytes()) + ) + )); + + byte[] zipBytes = s3RestService.downloadDirectoryAsZip("executionId1"); + + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry = zis.getNextEntry(); + assertEquals("debug/file.txt", entry.getName()); + assertEquals("byteContent", new String(zis.readAllBytes())); + } + } +} diff --git a/monitor-server/src/test/resources/application.yaml b/monitor-server/src/test/resources/application.yaml index fd6fbeab..c447c2d8 100644 --- a/monitor-server/src/test/resources/application.yaml +++ b/monitor-server/src/test/resources/application.yaml @@ -7,6 +7,10 @@ spring: dialect: org.hibernate.dialect.H2Dialect hibernate.format_sql: true hibernate.generate_statistics: true + cloud: + aws: + region: + static: test logging: level: diff --git a/monitor-worker-server/pom.xml b/monitor-worker-server/pom.xml index 512af361..659926c3 100644 --- a/monitor-worker-server/pom.xml +++ b/monitor-worker-server/pom.xml @@ -14,6 +14,7 @@ 0.61.0 + 3.3.1 @@ -108,6 +109,11 @@ com.powsybl powsybl-case-datasource-client + + io.awspring.cloud + spring-cloud-aws-starter-s3 + ${spring-cloud-aws-starter-s3} + diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/config/S3Configuration.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/config/S3Configuration.java new file mode 100644 index 00000000..b3f7051f --- /dev/null +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/config/S3Configuration.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.config; + +import org.gridsuite.monitor.worker.server.services.S3RestService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.services.s3.S3Client; + +/** + * @author Kevin Le Saulnier + */ +@Configuration +public class S3Configuration { + private static final Logger LOGGER = LoggerFactory.getLogger(S3Configuration.class); + private final String bucketName; + + public S3Configuration(@Value("${spring.cloud.aws.bucket:ws-bucket}") String bucketName) { + this.bucketName = bucketName; + } + + @SuppressWarnings("checkstyle:MethodName") + @Bean + public S3RestService s3RestService(S3Client s3Client) { + LOGGER.info("Configuring S3RestService with bucket: {}", bucketName); + return new S3RestService(s3Client, bucketName); + } +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContext.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContext.java index e4c64f38..ff21d3d1 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContext.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContext.java @@ -25,12 +25,14 @@ public class ProcessExecutionContext { @Setter private Network network; private final String executionEnvName; + private final String debugFileLocation; - public ProcessExecutionContext(UUID executionId, UUID caseUuid, C config, String executionEnvName) { + public ProcessExecutionContext(UUID executionId, UUID caseUuid, C config, String executionEnvName, String debugFileLocation) { this.executionId = executionId; this.caseUuid = caseUuid; this.config = config; this.executionEnvName = executionEnvName; + this.debugFileLocation = debugFileLocation; } public ProcessStepExecutionContext createStepContext(ProcessStep step, int stepOrder) { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java index 6fdb52b9..2fc77989 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContext.java @@ -67,6 +67,10 @@ public Network getNetwork() { return processContext.getNetwork(); } + public String getDebugFileLocation() { + return processContext.getDebugFileLocation(); + } + public void setNetwork(Network network) { processContext.setNetwork(network); } diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java index 8c09d9a6..45be4f5b 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java @@ -6,6 +6,7 @@ */ package org.gridsuite.monitor.worker.server.processes.commons.steps; +import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; import com.powsybl.iidm.network.Network; import org.apache.commons.collections4.CollectionUtils; @@ -13,30 +14,39 @@ import org.gridsuite.monitor.commons.ProcessConfig; import org.gridsuite.monitor.worker.server.core.AbstractProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.services.FilterService; -import org.gridsuite.monitor.worker.server.services.NetworkModificationRestService; -import org.gridsuite.monitor.worker.server.services.NetworkModificationService; +import org.gridsuite.monitor.worker.server.services.*; +import org.gridsuite.monitor.worker.server.utils.S3PathResolver; import org.springframework.stereotype.Component; +import java.io.IOException; import java.util.List; import java.util.UUID; /** - * @author Antoine Bouhours + * Apply modifications passed in context to network passed in context
+ * If debug is enabled, resulting network will be saved into S3 + * + * @author Antoine Bouhours */ @Component public class ApplyModificationsStep extends AbstractProcessStep { private final NetworkModificationService networkModificationService; private final NetworkModificationRestService networkModificationRestService; - + private final S3Service s3Service; private final FilterService filterService; - public ApplyModificationsStep(NetworkModificationService networkModificationService, NetworkModificationRestService networkModificationRestService, + private static final String DEBUG_FILENAME_PREFIX = "debug"; + private static final String DEBUG_FILENAME_SUFFIX = ".xiidm"; + + public ApplyModificationsStep(NetworkModificationService networkModificationService, + NetworkModificationRestService networkModificationRestService, + S3Service s3Service, FilterService filterService) { super(CommonStepType.APPLY_MODIFICATIONS); this.networkModificationService = networkModificationService; this.networkModificationRestService = networkModificationRestService; + this.s3Service = s3Service; this.filterService = filterService; } @@ -47,6 +57,26 @@ public void execute(ProcessStepExecutionContext context) { if (CollectionUtils.isNotEmpty(modificationIds)) { applyModifications(modificationIds, network, context.getReportInfos().reportNode()); } + if (context.getDebugFileLocation() != null) { + try { + exportUpdatedNetworkToS3(context); + } catch (IOException e) { + throw new PowsyblException("An error occurred while saving debug file: " + e.getCause(), e); + } + } + } + + private void exportUpdatedNetworkToS3(ProcessStepExecutionContext context) throws IOException { + s3Service.exportCompressedToS3( + S3PathResolver.getProcessStepDebugFilePath( + context.getDebugFileLocation(), + context.getProcessStepType().getName(), + context.getStepOrder(), + String.join("", DEBUG_FILENAME_PREFIX, DEBUG_FILENAME_SUFFIX, ".gz")), + DEBUG_FILENAME_PREFIX, + DEBUG_FILENAME_SUFFIX, + networkFile -> context.getNetwork().write("XIIDM", null, networkFile) + ); } private void applyModifications(List modificationIds, Network network, ReportNode reportNode) { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java index 38b8c788..470ec754 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java @@ -56,7 +56,8 @@ public void executeProcess(ProcessRunMessage runMes runMessage.executionId(), runMessage.caseUuid(), runMessage.config(), - executionEnvName + executionEnvName, + runMessage.debugFileLocation() ); try { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3RestService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3RestService.java new file mode 100644 index 00000000..cf0278f3 --- /dev/null +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3RestService.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.services; + +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; + +/** + * @author Kevin Le Saulnier + */ +public class S3RestService { + public static final String METADATA_FILE_NAME = "file-name"; + + private final S3Client s3Client; + + private final String bucketName; + + public S3RestService(S3Client s3Client, String bucketName) { + this.s3Client = s3Client; + this.bucketName = bucketName; + } + + public void uploadFile(Path filePath, String s3Key, String fileName) throws IOException { + try { + PutObjectRequest putRequest = PutObjectRequest.builder() + .bucket(bucketName) + .key(s3Key) + .metadata(Map.of(METADATA_FILE_NAME, fileName)) + .build(); + s3Client.putObject(putRequest, RequestBody.fromFile(filePath)); + } catch (SdkException e) { + throw new IOException("Error occurred while uploading file to S3: " + e.getMessage(), e); + } + } +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3Service.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3Service.java new file mode 100644 index 00000000..9d22f414 --- /dev/null +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/S3Service.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.services; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.util.function.ThrowingConsumer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +/** + * @author Kevin Le Saulnier + */ +@Service +public class S3Service { + private static final Logger LOGGER = LoggerFactory.getLogger(S3Service.class); + private final S3RestService s3RestService; + + public S3Service(S3RestService s3RestService) { + this.s3RestService = s3RestService; + } + + public void exportCompressedToS3(String s3Key, String fileNamePrefix, String fileNameSuffix, ThrowingConsumer writer) throws IOException { + FileAttribute> attrs = + PosixFilePermissions.asFileAttribute( + PosixFilePermissions.fromString("rwx------")); + + Path tempDir = Files.createTempDirectory("process-debug", attrs); + + try { + Path debugFile = Files.createTempFile(tempDir, fileNamePrefix, fileNameSuffix); + Path compressedDebugFile = Files.createTempFile(tempDir, String.join("", fileNamePrefix, fileNameSuffix), ".gz"); + + writer.accept(debugFile); + + try (InputStream in = Files.newInputStream(debugFile); + OutputStream out = new GZIPOutputStream(Files.newOutputStream(compressedDebugFile))) { + in.transferTo(out); + } + + s3RestService.uploadFile(compressedDebugFile, s3Key, String.join("", fileNamePrefix, fileNameSuffix, ".gz")); + } finally { + try { + if (Files.exists(tempDir)) { + FileUtils.deleteDirectory(tempDir.toFile()); + } + } catch (IOException e) { + LOGGER.error("Error cleaning up temporary debug directory: {}", tempDir, e); + } + } + } +} diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/utils/S3PathResolver.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/utils/S3PathResolver.java new file mode 100644 index 00000000..4f2f9fdb --- /dev/null +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/utils/S3PathResolver.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.utils; + +/** + * @author Kevin Le Saulnier + */ +public final class S3PathResolver { + private S3PathResolver() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static String getProcessStepDebugFilePath(String debugFileLocation, String processStepType, Integer stepOrder, String fileName) { + String s3Delimiter = "/"; + return String.join(s3Delimiter, + debugFileLocation, + processStepType + "_" + stepOrder, + fileName); + } +} diff --git a/monitor-worker-server/src/main/resources/application-local.yaml b/monitor-worker-server/src/main/resources/application-local.yaml index 54f57ba4..1f0fceed 100644 --- a/monitor-worker-server/src/main/resources/application-local.yaml +++ b/monitor-worker-server/src/main/resources/application-local.yaml @@ -4,6 +4,10 @@ server: spring: rabbitmq: addresses: localhost + cloud: + aws: + s3: + endpoint: http://localhost:19000 powsybl: services: diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContextTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContextTest.java index 0bc37827..fc4368a2 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContextTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessExecutionContextTest.java @@ -37,18 +37,19 @@ void shouldInitializeCorrectly() { UUID caseUuid = UUID.randomUUID(); String envName = "test-env"; - ProcessExecutionContext processContext = new ProcessExecutionContext<>(executionId, caseUuid, config, envName); + ProcessExecutionContext processContext = new ProcessExecutionContext<>(executionId, caseUuid, config, envName, null); assertThat(processContext.getConfig()).isEqualTo(config); assertThat(processContext.getExecutionId()).isEqualTo(executionId); assertThat(processContext.getCaseUuid()).isEqualTo(caseUuid); assertThat(processContext.getExecutionEnvName()).isEqualTo(envName); assertThat(processContext.getNetwork()).isNull(); + assertThat(processContext.getDebugFileLocation()).isNull(); } @Test void shouldSetAndGetNetwork() { - ProcessExecutionContext processContext = new ProcessExecutionContext<>(UUID.randomUUID(), UUID.randomUUID(), config, "test-env"); + ProcessExecutionContext processContext = new ProcessExecutionContext<>(UUID.randomUUID(), UUID.randomUUID(), config, "test-env", null); processContext.setNetwork(network); @@ -57,7 +58,7 @@ void shouldSetAndGetNetwork() { @Test void shouldCreateStepContext() { - ProcessExecutionContext processContext = new ProcessExecutionContext<>(UUID.randomUUID(), UUID.randomUUID(), config, "test-env"); + ProcessExecutionContext processContext = new ProcessExecutionContext<>(UUID.randomUUID(), UUID.randomUUID(), config, "test-env", null); ProcessStep step = mock(ProcessStep.class); ProcessStepType stepType = mock(ProcessStepType.class); when(stepType.getName()).thenReturn("test-step"); diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContextTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContextTest.java index 7dd80efe..2dce111d 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContextTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/core/ProcessStepExecutionContextTest.java @@ -49,6 +49,7 @@ void shouldInitializeCorrectly() { when(processContext.getCaseUuid()).thenReturn(caseUuid); when(processContext.getNetwork()).thenReturn(network); when(processContext.getConfig()).thenReturn(config); + when(processContext.getDebugFileLocation()).thenReturn("debug/file/location"); ProcessStepExecutionContext stepContext = new ProcessStepExecutionContext<>(processContext, stepType, stepExecutionId, stepOrder); @@ -64,6 +65,7 @@ void shouldInitializeCorrectly() { assertThat(stepContext.getProcessExecutionId()).isEqualTo(executionId); assertThat(stepContext.getCaseUuid()).isEqualTo(caseUuid); assertThat(stepContext.getNetwork()).isEqualTo(network); + assertThat(stepContext.getDebugFileLocation()).isEqualTo(processContext.getDebugFileLocation()); } @Test diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index 388b4e16..bc583f9a 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,25 +14,26 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.services.FilterService; -import org.gridsuite.monitor.worker.server.services.NetworkModificationRestService; -import org.gridsuite.monitor.worker.server.services.NetworkModificationService; +import org.gridsuite.monitor.worker.server.dto.ReportInfos; +import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.util.function.ThrowingConsumer; +import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; /** * @author Antoine Bouhours @@ -49,6 +50,9 @@ class ApplyModificationsStepTest { @Mock private FilterService filterService; + @Mock + private S3Service s3Service; + @Mock private ProcessConfig config; @@ -62,7 +66,7 @@ class ApplyModificationsStepTest { @BeforeEach void setUp() { - applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, filterService); + applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, s3Service, filterService); when(config.modificationUuids()).thenReturn(List.of(MODIFICATION_UUID)); when(stepContext.getConfig()).thenReturn(config); ReportInfos reportInfos = new ReportInfos(REPORT_UUID, ReportNode.newRootReportNode() @@ -88,4 +92,47 @@ void executeApplyModifications() { verify(networkModificationRestService).getModifications(any(List.class)); verify(networkModificationService).applyModifications(any(Network.class), any(List.class), any(ReportNode.class), any(FilterService.class)); } + + @Test + void executeApplyModificationsDebugOn() throws IOException { + String stepType = applyModificationsStep.getType().getName(); + assertEquals("APPLY_MODIFICATIONS", stepType); + + List modificationInfos = List.of(LoadModificationInfos.builder().equipmentId("load1").q0(new AttributeModification<>(300., OperationType.SET)).build()); + + Network network = mock(Network.class); + when(stepContext.getNetwork()).thenReturn(network); + when(networkModificationRestService.getModifications(any(List.class))).thenReturn(modificationInfos); + doNothing().when(networkModificationService).applyModifications(any(Network.class), any(List.class), any(ReportNode.class), any(FilterService.class)); + + // --- mock data specific to debug behaviour --- + String debugFileLocation = "debug/file/location"; + when(stepContext.getDebugFileLocation()).thenReturn(debugFileLocation); + when(stepContext.getProcessStepType()).thenReturn(CommonStepType.APPLY_MODIFICATIONS); + when(stepContext.getStepOrder()).thenReturn(7); + + // -- execute method + applyModificationsStep.execute(stepContext); + + verify(networkModificationRestService).getModifications(any(List.class)); + verify(networkModificationService).applyModifications(any(Network.class), any(List.class), any(ReportNode.class), any(FilterService.class)); + + // --- verify debug behaviour --- + ArgumentCaptor> networkWriterCapture = ArgumentCaptor.forClass(ThrowingConsumer.class); + + verify(s3Service).exportCompressedToS3( + eq(debugFileLocation + "/APPLY_MODIFICATIONS_7/debug.xiidm.gz"), + eq("debug"), + eq(".xiidm"), // very important - file suffix is very important when using network.write(...) + networkWriterCapture.capture() + ); + + // --- assert networkWriterCapture.get() is actually calling network.write() --- + Path mockedPath = mock(Path.class); + ThrowingConsumer networkWriter = networkWriterCapture.getValue(); + + networkWriter.accept(mockedPath); + + verify(network).write("XIIDM", null, mockedPath); + } } diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ConsumerServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ConsumerServiceTest.java index c03685a3..4c5b44ce 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ConsumerServiceTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ConsumerServiceTest.java @@ -41,7 +41,7 @@ void setUp() { @Test void consumeRun() { - ProcessRunMessage runMessage = new ProcessRunMessage<>(UUID.randomUUID(), UUID.randomUUID(), processConfig); + ProcessRunMessage runMessage = new ProcessRunMessage<>(UUID.randomUUID(), UUID.randomUUID(), processConfig, null); Message> message = MessageBuilder.withPayload(runMessage).build(); var consumer = consumerService.consumeRun(); diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java index c4cc51ae..15ea89fc 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java @@ -59,6 +59,9 @@ class ProcessExecutionServiceTest { @Mock private FilterService filterService; + @Mock + private S3Service s3Service; + @Mock private SecurityAnalysisService securityAnalysisService; @@ -80,7 +83,7 @@ void setUp() { processExecutionService = new ProcessExecutionService(processList, notificationService, EXECUTION_ENV_NAME); loadNetworkStep = new LoadNetworkStep<>(networkConversionService); - applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, filterService); + applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, s3Service, filterService); runComputationStep = new SecurityAnalysisRunComputationStep(securityAnalysisService); } @@ -90,7 +93,7 @@ void executeProcessShouldCompleteSuccessfullyWhenProcessExecutesWithoutError() { UUID caseUuid = UUID.randomUUID(); when(processConfig.processType()).thenReturn(ProcessType.SECURITY_ANALYSIS); doNothing().when(process).execute(any(ProcessExecutionContext.class)); - ProcessRunMessage runMessage = new ProcessRunMessage<>(executionId, caseUuid, processConfig); + ProcessRunMessage runMessage = new ProcessRunMessage<>(executionId, caseUuid, processConfig, null); when(process.defineSteps()).thenReturn((List) List.of(loadNetworkStep, applyModificationsStep, runComputationStep)); processExecutionService.executeProcess(runMessage); @@ -139,7 +142,7 @@ void executeProcessShouldSendFailedStatusWhenProcessThrowsException() { when(processConfig.processType()).thenReturn(ProcessType.SECURITY_ANALYSIS); RuntimeException processException = new RuntimeException("Process execution failed"); doThrow(processException).when(process).execute(any(ProcessExecutionContext.class)); - ProcessRunMessage runMessage = new ProcessRunMessage<>(executionId, caseUuid, processConfig); + ProcessRunMessage runMessage = new ProcessRunMessage<>(executionId, caseUuid, processConfig, null); assertThrows(RuntimeException.class, () -> processExecutionService.executeProcess(runMessage)); @@ -158,7 +161,7 @@ void executeProcessShouldSendFailedStatusWhenProcessThrowsException() { @Test void executeProcessShouldThrowIllegalArgumentExceptionWhenProcessTypeNotFound() { when(processConfig.processType()).thenReturn(null); - ProcessRunMessage runMessage = new ProcessRunMessage<>(UUID.randomUUID(), UUID.randomUUID(), processConfig); + ProcessRunMessage runMessage = new ProcessRunMessage<>(UUID.randomUUID(), UUID.randomUUID(), processConfig, null); assertThatThrownBy(() -> processExecutionService.executeProcess(runMessage)) .isInstanceOf(IllegalArgumentException.class) diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3RestServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3RestServiceTest.java new file mode 100644 index 00000000..867108ea --- /dev/null +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3RestServiceTest.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.services; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; + +/** + * @author Kevin Le Saulnier + */ +@ExtendWith(MockitoExtension.class) +class S3RestServiceTest { + @Mock + S3Client s3Client; + + S3RestService s3RestService; + + @TempDir + Path testDir; + + @BeforeEach + void setup() { + s3RestService = new S3RestService(s3Client, "my-bucket"); + } + + @Test + void testUploadFileToS3() throws Exception { + Path tempFile = Files.createTempFile(testDir, "test", ".txt"); + Files.writeString(tempFile, "dataToUpload"); + + s3RestService.uploadFile(tempFile, "testS3Key", "fileToUploadName"); + + ArgumentCaptor requestCaptor = + ArgumentCaptor.forClass(PutObjectRequest.class); + + verify(s3Client).putObject( + requestCaptor.capture(), + any(RequestBody.class) + ); + + PutObjectRequest request = requestCaptor.getValue(); + + assertThat(request.bucket()).isEqualTo("my-bucket"); + assertThat(request.key()).isEqualTo("testS3Key"); + assertThat(request.metadata()).containsEntry("file-name", "fileToUploadName"); + } + + @Test + void testUpdateFileToS3Error() throws Exception { + + Path fileToUpload = Files.createTempFile(testDir, "test", ".txt"); + Files.writeString(fileToUpload, "dataToUpload"); + + doThrow(SdkException.builder().message("sdkError").build()) + .when(s3Client) + .putObject(any(PutObjectRequest.class), any(RequestBody.class)); + + assertThatThrownBy(() -> + s3RestService.uploadFile(fileToUpload, "key", "file.txt") + ) + .isInstanceOf(IOException.class) + .hasMessageContaining("Error occurred while uploading file to S3") + .hasMessageContaining("sdkError"); + + verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); + } +} diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3ServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3ServiceTest.java new file mode 100644 index 00000000..f0f2e7d2 --- /dev/null +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/S3ServiceTest.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.worker.server.services; + +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.util.function.ThrowingConsumer; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.atomic.AtomicReference; +import java.util.zip.GZIPInputStream; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; + +/** + * @author Kevin Le Saulnier + */ +@ExtendWith(MockitoExtension.class) +class S3ServiceTest { + @Mock + S3RestService s3RestService; + + @InjectMocks + S3Service s3Service; + + @TempDir + Path testDir; + + @Test + void testPathIsCompressedBeforeUploading() throws Exception { + // --- Inputs --- + Network network = EurostagTutorialExample1Factory.create(); + String s3Key = "s3Key"; + String fileNamePrefix = "fileName"; + String fileNameSuffix = ".xiidm"; + ThrowingConsumer writer = + path -> network.write("XIIDM", null, path); + + // write network directly in expectedFile.xiidm for later assertions + Path expectedFilePath = testDir.resolve("expectedFile.xiidm"); + network.write("XIIDM", null, expectedFilePath); + + // Since temp files are deleted after "uploadFile", make a copy of the compressed file to assert its content + Path capturedCopy = Files.createTempFile(testDir, "test-copy", ".gz"); + doAnswer(invocation -> { + Path uploaded = invocation.getArgument(0); + Files.copy(uploaded, capturedCopy, StandardCopyOption.REPLACE_EXISTING); + return null; + }).when(s3RestService) + .uploadFile(any(Path.class), anyString(), anyString()); + + // --- Method invocation --- + s3Service.exportCompressedToS3(s3Key, fileNamePrefix, fileNameSuffix, writer); + + // --- Assertions --- + verify(s3RestService).uploadFile(any(), eq(s3Key), eq(String.join("", fileNamePrefix, fileNameSuffix, ".gz"))); + try (InputStream in = new GZIPInputStream(Files.newInputStream(capturedCopy))) { + String uncompressedContent = new String(in.readAllBytes(), UTF_8); + assertThat(uncompressedContent).isNotNull(); + assertThat(uncompressedContent).isEqualTo(Files.readString(expectedFilePath)); + } + } + + @Test + void testTempFilesAreDeletedAfterExecution() throws Exception { + // --- Inputs --- + AtomicReference debugFileToUplad = new AtomicReference<>(); + AtomicReference debugTempDir = new AtomicReference<>(); + String s3Key = "s3Key"; + String fileNamePrefix = "fileName"; + String fileNameSuffix = ".test"; + + ThrowingConsumer writer = path -> { + debugFileToUplad.set(path); + debugTempDir.set(path.getParent()); + Files.writeString(path, "hello"); + }; + + // --- Method invocation --- + s3Service.exportCompressedToS3(s3Key, fileNamePrefix, fileNameSuffix, writer); + + // --- Assertions --- + ArgumentCaptor compressedDebugFileToUploadCaptor = ArgumentCaptor.forClass(Path.class); + verify(s3RestService).uploadFile( + compressedDebugFileToUploadCaptor.capture(), + eq(s3Key), + eq(String.join("", fileNamePrefix, fileNameSuffix, ".gz")) + ); + Path compressedDebugFileToUpload = compressedDebugFileToUploadCaptor.getValue(); + + assertThat(Files.exists(compressedDebugFileToUpload)).isFalse(); + assertThat(Files.exists(debugFileToUplad.get())).isFalse(); + assertThat(Files.exists(debugTempDir.get())).isFalse(); + } +} From 9c292e28c3c1cdb4a6da203b0f00b91de7e0615b Mon Sep 17 00:00:00 2001 From: Achour berrahma Date: Wed, 25 Feb 2026 14:31:16 +0100 Subject: [PATCH 18/34] Implement state estimation result provider and service (#44) Signed-off-by: achour94 --- .../gridsuite/monitor/commons/ResultType.java | 1 + .../StateEstimationResultProvider.java | 39 ++++++++ .../services/StateEstimationService.java | 67 +++++++++++++ .../src/main/resources/application-local.yaml | 4 +- .../StateEstimationResultProviderTest.java | 59 +++++++++++ .../services/StateEstimationServiceTest.java | 98 +++++++++++++++++++ 6 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationResultProvider.java create mode 100644 monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationService.java create mode 100644 monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationResultProviderTest.java create mode 100644 monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationServiceTest.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ResultType.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ResultType.java index 19864a79..78be8dda 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ResultType.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ResultType.java @@ -11,4 +11,5 @@ */ public enum ResultType { SECURITY_ANALYSIS, + STATE_ESTIMATION } diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationResultProvider.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationResultProvider.java new file mode 100644 index 00000000..cb1faa9c --- /dev/null +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationResultProvider.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import org.gridsuite.monitor.commons.ResultType; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +/** + * @author Achour BERRAHMA + */ +@Service +public class StateEstimationResultProvider implements ResultProvider { + private final StateEstimationService stateEstimationService; + + public StateEstimationResultProvider(StateEstimationService stateEstimationService) { + this.stateEstimationService = stateEstimationService; + } + + @Override + public ResultType getType() { + return ResultType.STATE_ESTIMATION; + } + + @Override + public String getResult(UUID resultId) { + return stateEstimationService.getResult(resultId); + } + + @Override + public void deleteResult(UUID resultId) { + stateEstimationService.deleteResult(resultId); + } +} diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationService.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationService.java new file mode 100644 index 00000000..052b17c6 --- /dev/null +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/StateEstimationService.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import lombok.Setter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.List; +import java.util.UUID; + +/** + * @author Achour BERRAHMA + */ +@Service +public class StateEstimationService { + private static final Logger LOGGER = LoggerFactory.getLogger(StateEstimationService.class); + static final String SE_API_VERSION = "v1"; + private static final String DELIMITER = "/"; + + private final RestTemplate restTemplate; + + @Setter + private String stateEstimationServerBaseUri; + + private String getStateEstimationServerBaseUri() { + return this.stateEstimationServerBaseUri + DELIMITER + SE_API_VERSION + DELIMITER; + } + + public StateEstimationService( + RestTemplateBuilder restTemplateBuilder, + @Value("${gridsuite.services.state-estimation-server.base-uri:http://state-estimation-server/}") String stateEstimationServerBaseUri) { + this.stateEstimationServerBaseUri = stateEstimationServerBaseUri; + this.restTemplate = restTemplateBuilder.build(); + } + + public String getResult(UUID resultUuid) { + LOGGER.info("Fetching state estimation result {}", resultUuid); + + var path = UriComponentsBuilder.fromPath("/results/{resultUuid}") + .buildAndExpand(resultUuid) + .toUriString(); + + return restTemplate.exchange(getStateEstimationServerBaseUri() + path, HttpMethod.GET, null, String.class).getBody(); + } + + public void deleteResult(UUID resultUuid) { + LOGGER.info("Deleting state estimation result {}", resultUuid); + + var path = UriComponentsBuilder.fromPath("/results") + .queryParam("resultsUuids", List.of(resultUuid)) + .build() + .toUriString(); + + restTemplate.delete(getStateEstimationServerBaseUri() + path); + } +} diff --git a/monitor-server/src/main/resources/application-local.yaml b/monitor-server/src/main/resources/application-local.yaml index 248fecfc..69d2f51e 100644 --- a/monitor-server/src/main/resources/application-local.yaml +++ b/monitor-server/src/main/resources/application-local.yaml @@ -18,4 +18,6 @@ gridsuite: report-server: base-uri: http://localhost:5028 security-analysis-server: - base-uri: http://localhost:5023 \ No newline at end of file + base-uri: http://localhost:5023 + state-estimation-server: + base-uri: http://localhost:6040 \ No newline at end of file diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationResultProviderTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationResultProviderTest.java new file mode 100644 index 00000000..81589f1d --- /dev/null +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationResultProviderTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import org.gridsuite.monitor.commons.ResultType; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +/** + * @author Achour BERRAHMA + */ +class StateEstimationResultProviderTest { + private final StateEstimationService stateEstimationService = + Mockito.mock(StateEstimationService.class); + + private final StateEstimationResultProvider provider = + new StateEstimationResultProvider(stateEstimationService); + + @Test + void getTypeShouldReturnStateEstimation() { + assertThat(provider.getType()) + .isEqualTo(ResultType.STATE_ESTIMATION); + } + + @Test + void getResultShouldDelegateToStateEstimationService() { + UUID id = UUID.randomUUID(); + String expected = "result"; + + when(stateEstimationService.getResult(id)).thenReturn(expected); + + String result = provider.getResult(id); + + assertThat(result).isEqualTo(expected); + verify(stateEstimationService).getResult(id); + verifyNoMoreInteractions(stateEstimationService); + } + + @Test + void deleteResultShouldDelegateToStateEstimationService() { + UUID id = UUID.randomUUID(); + + doNothing().when(stateEstimationService).deleteResult(id); + + provider.deleteResult(id); + + verify(stateEstimationService).deleteResult(id); + verifyNoMoreInteractions(stateEstimationService); + } +} diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationServiceTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationServiceTest.java new file mode 100644 index 00000000..c7ff33a5 --- /dev/null +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/StateEstimationServiceTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.server.services; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.match.MockRestRequestMatchers; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.RestClientException; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +/** + * @author Achour BERRAHMA + */ +@RestClientTest(StateEstimationService.class) +@ContextConfiguration(classes = { StateEstimationService.class }) +class StateEstimationServiceTest { + + private static final UUID RESULT_UUID = UUID.randomUUID(); + private static final String RESULT_BODY = "{\"status\":\"OK\"}"; + + @Autowired + private StateEstimationService stateEstimationService; + + @Autowired + private MockRestServiceServer server; + + @AfterEach + void tearDown() { + server.verify(); + } + + @Test + void getResult() { + server.expect(MockRestRequestMatchers.method(HttpMethod.GET)) + .andExpect(MockRestRequestMatchers.requestTo( + "http://state-estimation-server/v1/results/" + RESULT_UUID + )) + .andRespond(MockRestResponseCreators.withSuccess( + RESULT_BODY, + MediaType.APPLICATION_JSON + )); + + String result = stateEstimationService.getResult(RESULT_UUID); + + assertThat(result).isEqualTo(RESULT_BODY); + } + + @Test + void getResultFailed() { + server.expect(MockRestRequestMatchers.method(HttpMethod.GET)) + .andExpect(MockRestRequestMatchers.requestTo( + "http://state-estimation-server/v1/results/" + RESULT_UUID + )) + .andRespond(MockRestResponseCreators.withServerError()); + + assertThatThrownBy(() -> stateEstimationService.getResult(RESULT_UUID)) + .isInstanceOf(RestClientException.class); + } + + @Test + void deleteResult() { + server.expect(MockRestRequestMatchers.method(HttpMethod.DELETE)) + .andExpect(MockRestRequestMatchers.requestTo( + "http://state-estimation-server/v1/results?resultsUuids=" + RESULT_UUID + )) + .andRespond(MockRestResponseCreators.withSuccess()); + + assertThatNoException().isThrownBy(() -> stateEstimationService.deleteResult(RESULT_UUID)); + } + + @Test + void deleteResultFailed() { + server.expect(MockRestRequestMatchers.method(HttpMethod.DELETE)) + .andExpect(MockRestRequestMatchers.requestTo( + "http://state-estimation-server/v1/results?resultsUuids=" + RESULT_UUID + )) + .andRespond(MockRestResponseCreators.withServerError()); + + assertThatThrownBy(() -> stateEstimationService.deleteResult(RESULT_UUID)) + .isInstanceOf(RestClientException.class); + } +} From ecc04fdb64e5c2bc633dc2af3aa327e0eab5cb5a Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 10:43:08 +0100 Subject: [PATCH 19/34] merge with main Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../processes/commons/steps/ApplyModificationsStepTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index bc583f9a..4aa694c6 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,8 +14,8 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 09a3315339bcff3b26804b82b72fd65f6f6d8be9 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Mon, 2 Mar 2026 17:12:10 +0100 Subject: [PATCH 20/34] resolve change request Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 16 ++++++++++------ .../server/services/StepExecutionService.java | 15 --------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 4f836c21..57eaef8c 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,9 +19,13 @@ */ public abstract class AbstractStepExecutor { - protected abstract StepStatusPublisher getStepStatusPublisher(); + public AbstractStepExecutor() { + StepStatusPublisher stepStatusPublisher; + ReportPublisher reportPublisher; + } - protected abstract ReportPublisher getReportPublisher(); + protected StepStatusPublisher stepStatusPublisher; + protected ReportPublisher reportPublisher; public void skipStep( UUID processExecutionId, @@ -40,7 +44,7 @@ public void skipStep( null, startedAt, Instant.now()); - getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); } public void executeStep( @@ -64,11 +68,11 @@ public void executeStep( reportUuid, startedAt, null); - getStepStatusPublisher().updateStepStatus(processExecutionId, executionStep); + stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); try { stepExecution.run(); - getReportPublisher().sendReport(reportInfos); + reportPublisher.sendReport(reportInfos); updateStepStatus( processExecutionId, stepExecutionId, @@ -112,6 +116,6 @@ private void updateStepStatus( reportUuid, startedAt, Instant.now()); - getStepStatusPublisher().updateStepStatus(processExecutionId, updated); + stepStatusPublisher.updateStepStatus(processExecutionId, updated); } } diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 50256341..0a1e46de 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -7,9 +7,7 @@ package org.gridsuite.monitor.worker.server.services; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.steps.ReportPublisher; import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; -import org.gridsuite.monitor.commons.steps.StepStatusPublisher; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.springframework.stereotype.Service; @@ -22,24 +20,11 @@ @Service public class StepExecutionService extends AbstractStepExecutor { - private final StepStatusPublisher stepStatusPublisher; - private final ReportPublisher reportPublisher; - public StepExecutionService(NotificationService notificationService, ReportService reportService) { this.stepStatusPublisher = notificationService::updateStepStatus; this.reportPublisher = reportService::sendReport; } - @Override - public StepStatusPublisher getStepStatusPublisher() { - return stepStatusPublisher; - } - - @Override - public ReportPublisher getReportPublisher() { - return reportPublisher; - } - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { skipStep(context.getProcessExecutionId(), context.getStepExecutionId(), From d03aaf9864921d40a7c7a3a917f66ee2f8e5b083 Mon Sep 17 00:00:00 2001 From: FranckLecuyer <47824306+FranckLecuyer@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:25:05 +0100 Subject: [PATCH 21/34] Add endpoint to get all process configs of a given type (#42) Signed-off-by: Franck LECUYER --- .../commons/PersistedProcessConfig.java | 18 +++++ .../commons/SecurityAnalysisConfig.java | 1 - .../controllers/ProcessConfigController.java | 18 ++++- ...igEntity.java => ProcessConfigEntity.java} | 8 ++- .../SecurityAnalysisConfigEntity.java | 12 +--- .../mapper/SecurityAnalysisConfigMapper.java | 9 ++- .../repositories/ProcessConfigRepository.java | 7 +- .../server/services/ProcessConfigService.java | 34 ++++++--- .../server/MonitorIntegrationTest.java | 69 +++++++++++++++++-- .../ProcessConfigControllerTest.java | 50 +++++++++++++- .../services/ProcessConfigServiceTest.java | 53 +++++++++++--- 11 files changed, 232 insertions(+), 47 deletions(-) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/PersistedProcessConfig.java rename monitor-server/src/main/java/org/gridsuite/monitor/server/entities/{AbstractProcessConfigEntity.java => ProcessConfigEntity.java} (88%) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/PersistedProcessConfig.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/PersistedProcessConfig.java new file mode 100644 index 00000000..fe78f7da --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/PersistedProcessConfig.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons; + +import java.util.UUID; + +/** + * @author Franck Lecuyer + */ +public record PersistedProcessConfig( + UUID id, + ProcessConfig processConfig +) { +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java index 6e405e8c..639ee4c6 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java @@ -17,7 +17,6 @@ public record SecurityAnalysisConfig( List contingencies, List modificationUuids ) implements ProcessConfig { - @Override public ProcessType processType() { return ProcessType.SECURITY_ANALYSIS; diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/ProcessConfigController.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/ProcessConfigController.java index 2b4e9c3e..b9b383f3 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/ProcessConfigController.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/ProcessConfigController.java @@ -11,7 +11,9 @@ 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 org.gridsuite.monitor.commons.PersistedProcessConfig; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.server.services.ProcessConfigService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -22,8 +24,10 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -55,9 +59,9 @@ public ResponseEntity createProcessConfig(@RequestBody ProcessConfig proce @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "process config was returned"), @ApiResponse(responseCode = "404", description = "process config was not found")}) - public ResponseEntity getProcessConfig( + public ResponseEntity getProcessConfig( @Parameter(description = "process config UUID") @PathVariable("uuid") UUID processConfigUuid) { - Optional processConfig = processConfigService.getProcessConfig(processConfigUuid); + Optional processConfig = processConfigService.getProcessConfig(processConfigUuid); return processConfig.map(config -> ResponseEntity.ok().body(config)).orElseGet(() -> ResponseEntity.notFound().build()); } @@ -85,4 +89,14 @@ public ResponseEntity deleteProcessConfig( ResponseEntity.ok().build() : ResponseEntity.notFound().build(); } + + @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get all process configs of a given type") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "The process configs of the given type were returned")}) + public ResponseEntity> getProcessConfigs( + @Parameter(description = "Process type") @RequestParam(name = "processType") ProcessType processType) { + List processConfigs = processConfigService.getProcessConfigs(processType); + return ResponseEntity.ok().body(processConfigs); + } } diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/AbstractProcessConfigEntity.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessConfigEntity.java similarity index 88% rename from monitor-server/src/main/java/org/gridsuite/monitor/server/entities/AbstractProcessConfigEntity.java rename to monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessConfigEntity.java index 0a4e6f3c..6cbd950f 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/AbstractProcessConfigEntity.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessConfigEntity.java @@ -11,6 +11,8 @@ import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -43,7 +45,7 @@ @AllArgsConstructor @Getter @Setter -public abstract class AbstractProcessConfigEntity { +public class ProcessConfigEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") @@ -57,7 +59,9 @@ public abstract class AbstractProcessConfigEntity { @OrderColumn(name = "pos_modifications") private List modificationUuids; - public abstract ProcessType getType(); + @Enumerated(EnumType.STRING) + @Column(name = "process_type", insertable = false, updatable = false) + private ProcessType processType; } diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/SecurityAnalysisConfigEntity.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/SecurityAnalysisConfigEntity.java index 59b924d8..155fe353 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/SecurityAnalysisConfigEntity.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/entities/SecurityAnalysisConfigEntity.java @@ -21,7 +21,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.gridsuite.monitor.commons.ProcessType; import java.util.List; import java.util.UUID; @@ -37,7 +36,7 @@ @AllArgsConstructor @Getter @Setter -public class SecurityAnalysisConfigEntity extends AbstractProcessConfigEntity { +public class SecurityAnalysisConfigEntity extends ProcessConfigEntity { @Column(name = "parameters_uuid") private UUID parametersUuid; @@ -48,13 +47,4 @@ public class SecurityAnalysisConfigEntity extends AbstractProcessConfigEntity { @Column(name = "contingency") @OrderColumn(name = "pos_contingencies") private List contingencies; - - @Override - public ProcessType getType() { - return ProcessType.SECURITY_ANALYSIS; - } } - - - - diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/SecurityAnalysisConfigMapper.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/SecurityAnalysisConfigMapper.java index 0330f74d..b40afb8a 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/SecurityAnalysisConfigMapper.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/SecurityAnalysisConfigMapper.java @@ -6,6 +6,8 @@ */ package org.gridsuite.monitor.server.mapper; +import org.gridsuite.monitor.commons.PersistedProcessConfig; +import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.commons.SecurityAnalysisConfig; import org.gridsuite.monitor.server.entities.SecurityAnalysisConfigEntity; @@ -16,16 +18,17 @@ public class SecurityAnalysisConfigMapper { public static SecurityAnalysisConfigEntity toEntity(SecurityAnalysisConfig dto) { SecurityAnalysisConfigEntity entity = new SecurityAnalysisConfigEntity(); + entity.setProcessType(ProcessType.SECURITY_ANALYSIS); update(entity, dto); return entity; } - public static SecurityAnalysisConfig toDto(SecurityAnalysisConfigEntity entity) { - return new SecurityAnalysisConfig( + public static PersistedProcessConfig toDto(SecurityAnalysisConfigEntity entity) { + return new PersistedProcessConfig(entity.getId(), new SecurityAnalysisConfig( entity.getParametersUuid(), entity.getContingencies(), entity.getModificationUuids() - ); + )); } public static void update(SecurityAnalysisConfigEntity entity, SecurityAnalysisConfig dto) { diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/repositories/ProcessConfigRepository.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/repositories/ProcessConfigRepository.java index 47fda5f7..3a8f37ad 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/repositories/ProcessConfigRepository.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/repositories/ProcessConfigRepository.java @@ -6,15 +6,18 @@ */ package org.gridsuite.monitor.server.repositories; -import org.gridsuite.monitor.server.entities.AbstractProcessConfigEntity; +import org.gridsuite.monitor.commons.ProcessType; +import org.gridsuite.monitor.server.entities.ProcessConfigEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.UUID; /** * @author Franck Lecuyer */ @Repository -public interface ProcessConfigRepository extends JpaRepository { +public interface ProcessConfigRepository extends JpaRepository { + List findAllByProcessType(ProcessType processType); } diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/ProcessConfigService.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/ProcessConfigService.java index 22ea3a7e..2ca28cd0 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/services/ProcessConfigService.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/services/ProcessConfigService.java @@ -6,15 +6,18 @@ */ package org.gridsuite.monitor.server.services; +import org.gridsuite.monitor.commons.PersistedProcessConfig; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.commons.SecurityAnalysisConfig; -import org.gridsuite.monitor.server.entities.AbstractProcessConfigEntity; +import org.gridsuite.monitor.server.entities.ProcessConfigEntity; import org.gridsuite.monitor.server.entities.SecurityAnalysisConfigEntity; import org.gridsuite.monitor.server.mapper.SecurityAnalysisConfigMapper; import org.gridsuite.monitor.server.repositories.ProcessConfigRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -31,19 +34,19 @@ public ProcessConfigService(ProcessConfigRepository processConfigRepository) { @Transactional public UUID createProcessConfig(ProcessConfig processConfig) { - AbstractProcessConfigEntity entity = switch (processConfig) { - case SecurityAnalysisConfig sac -> SecurityAnalysisConfigMapper.toEntity(sac); + switch (processConfig) { + case SecurityAnalysisConfig sac -> { + return processConfigRepository.save(SecurityAnalysisConfigMapper.toEntity(sac)).getId(); + } default -> throw new IllegalArgumentException("Unsupported process config type: " + processConfig.processType()); - }; - return processConfigRepository.save(entity).getId(); + } } @Transactional(readOnly = true) - public Optional getProcessConfig(UUID processConfigUuid) { + public Optional getProcessConfig(UUID processConfigUuid) { return processConfigRepository.findById(processConfigUuid).flatMap(entity -> switch (entity) { - case SecurityAnalysisConfigEntity sae -> - Optional.of((ProcessConfig) SecurityAnalysisConfigMapper.toDto(sae)); - default -> throw new IllegalArgumentException("Unsupported entity type: " + entity.getType()); + case SecurityAnalysisConfigEntity sae -> Optional.of(SecurityAnalysisConfigMapper.toDto(sae)); + default -> throw new IllegalArgumentException("Unsupported entity type: " + entity.getProcessType()); }); } @@ -51,8 +54,8 @@ public Optional getProcessConfig(UUID processConfigUuid) { public boolean updateProcessConfig(UUID processConfigUuid, ProcessConfig processConfig) { return processConfigRepository.findById(processConfigUuid) .map(entity -> { - if (entity.getType() != processConfig.processType()) { - throw new IllegalArgumentException("Process config type mismatch : " + entity.getType()); + if (entity.getProcessType() != processConfig.processType()) { + throw new IllegalArgumentException("Process config type mismatch : " + entity.getProcessType()); } switch (processConfig) { case SecurityAnalysisConfig sac -> @@ -72,4 +75,13 @@ public boolean deleteProcessConfig(UUID processConfigUuid) { } return false; } + + @Transactional(readOnly = true) + public List getProcessConfigs(ProcessType processType) { + List processConfigs = processConfigRepository.findAllByProcessType(processType); + return processConfigs.stream().map(entity -> switch (entity) { + case SecurityAnalysisConfigEntity sae -> SecurityAnalysisConfigMapper.toDto(sae); + default -> throw new IllegalArgumentException("Unsupported entity type: " + entity.getProcessType()); + }).toList(); + } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java index 8f051fa9..aa7fad2a 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/MonitorIntegrationTest.java @@ -244,9 +244,9 @@ void processConfigIT() { UUID configId = configService.createProcessConfig(securityAnalysisConfig); assertThat(processConfigRepository.findById(configId)).isNotEmpty(); - Optional config = configService.getProcessConfig(configId); + Optional config = configService.getProcessConfig(configId); assertThat(config).isNotEmpty(); - assertThat(config.get()).usingRecursiveComparison().isEqualTo(securityAnalysisConfig); + assertThat(config.get().processConfig()).usingRecursiveComparison().isEqualTo(securityAnalysisConfig); UUID updatedParametersUuid = UUID.randomUUID(); UUID updatedModificationUuid = UUID.randomUUID(); @@ -257,13 +257,72 @@ void processConfigIT() { ); boolean updated = configService.updateProcessConfig(configId, updatedSecurityAnalysisConfig); assertThat(updated).isTrue(); - Optional updatedConfig = configService.getProcessConfig(configId); + Optional updatedConfig = configService.getProcessConfig(configId); assertThat(updatedConfig).isNotEmpty(); - assertThat(updatedConfig.get()).usingRecursiveComparison().isEqualTo(updatedSecurityAnalysisConfig); + assertThat(updatedConfig.get().processConfig()).usingRecursiveComparison().isEqualTo(updatedSecurityAnalysisConfig); boolean deleted = configService.deleteProcessConfig(configId); assertThat(deleted).isTrue(); - Optional deletedConfig = configService.getProcessConfig(configId); + Optional deletedConfig = configService.getProcessConfig(configId); assertThat(deletedConfig).isEmpty(); } + + @Test + void processConfigsIT() { + UUID parametersUuid1 = UUID.randomUUID(); + UUID modificationUuid1 = UUID.randomUUID(); + SecurityAnalysisConfig securityAnalysisConfig1 = new SecurityAnalysisConfig( + parametersUuid1, + List.of("contingency1", "contingency2"), + List.of(modificationUuid1) + ); + UUID configId1 = configService.createProcessConfig(securityAnalysisConfig1); + assertThat(processConfigRepository.findById(configId1)).isNotEmpty(); + + UUID parametersUuid2 = UUID.randomUUID(); + UUID modificationUuid2 = UUID.randomUUID(); + SecurityAnalysisConfig securityAnalysisConfig2 = new SecurityAnalysisConfig( + parametersUuid2, + List.of("contingency3", "contingency4"), + List.of(modificationUuid2) + ); + UUID configId2 = configService.createProcessConfig(securityAnalysisConfig2); + assertThat(processConfigRepository.findById(configId2)).isNotEmpty(); + + List processConfigs = configService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + assertThat(processConfigs).hasSize(2); + + PersistedProcessConfig retrievedConfig1 = processConfigs.stream() + .filter(c -> c.id().equals(configId1)) + .findFirst() + .orElseThrow(); + SecurityAnalysisConfig retrievedSecurityAnalysisConfig1 = (SecurityAnalysisConfig) retrievedConfig1.processConfig(); + + PersistedProcessConfig retrievedConfig2 = processConfigs.stream() + .filter(c -> c.id().equals(configId2)) + .findFirst() + .orElseThrow(); + SecurityAnalysisConfig retrievedSecurityAnalysisConfig2 = (SecurityAnalysisConfig) retrievedConfig2.processConfig(); + + assertThat(retrievedSecurityAnalysisConfig1.parametersUuid()).isEqualTo(parametersUuid1); + assertThat(retrievedSecurityAnalysisConfig1.contingencies()).isEqualTo(List.of("contingency1", "contingency2")); + assertThat(retrievedSecurityAnalysisConfig1.modificationUuids()).isEqualTo(List.of(modificationUuid1)); + + assertThat(retrievedSecurityAnalysisConfig2.parametersUuid()).isEqualTo(parametersUuid2); + assertThat(retrievedSecurityAnalysisConfig2.contingencies()).isEqualTo(List.of("contingency3", "contingency4")); + assertThat(retrievedSecurityAnalysisConfig2.modificationUuids()).isEqualTo(List.of(modificationUuid2)); + + boolean deleted = configService.deleteProcessConfig(configId1); + assertThat(deleted).isTrue(); + + List remainingConfigs = configService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + assertThat(remainingConfigs).hasSize(1); + assertThat(remainingConfigs.get(0).processConfig().processType()).isEqualTo(ProcessType.SECURITY_ANALYSIS); + + boolean deletedSecond = configService.deleteProcessConfig(configId2); + assertThat(deletedSecond).isTrue(); + + List noConfigs = configService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + assertThat(noConfigs).isEmpty(); + } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/ProcessConfigControllerTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/ProcessConfigControllerTest.java index a7a80bfd..a0c46d47 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/ProcessConfigControllerTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/ProcessConfigControllerTest.java @@ -7,7 +7,9 @@ package org.gridsuite.monitor.server.controllers; import com.fasterxml.jackson.databind.ObjectMapper; +import org.gridsuite.monitor.commons.PersistedProcessConfig; import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.commons.SecurityAnalysisConfig; import org.gridsuite.monitor.server.services.ProcessConfigService; import org.junit.jupiter.api.Test; @@ -72,11 +74,11 @@ void createSecurityAnalysisConfig() throws Exception { @Test void getSecurityAnalysisConfig() throws Exception { UUID processConfigId = UUID.randomUUID(); - SecurityAnalysisConfig securityAnalysisConfig = new SecurityAnalysisConfig( + PersistedProcessConfig securityAnalysisConfig = new PersistedProcessConfig(UUID.randomUUID(), new SecurityAnalysisConfig( UUID.randomUUID(), List.of("contingency1", "contingency2"), List.of(UUID.randomUUID(), UUID.randomUUID()) - ); + )); String expectedJson = objectMapper.writeValueAsString(securityAnalysisConfig); when(processConfigService.getProcessConfig(any(UUID.class))) @@ -170,4 +172,48 @@ void deleteSecurityAnalysisConfigNotFound() throws Exception { verify(processConfigService).deleteProcessConfig(any(UUID.class)); } + + @Test + void getAllSecurityAnalysisConfigs() throws Exception { + List securityAnalysisConfigs = List.of( + new PersistedProcessConfig(UUID.randomUUID(), new SecurityAnalysisConfig( + UUID.randomUUID(), + List.of("contingency1", "contingency2"), + List.of(UUID.randomUUID(), UUID.randomUUID()) + )), + new PersistedProcessConfig(UUID.randomUUID(), new SecurityAnalysisConfig( + UUID.randomUUID(), + List.of("contingency3", "contingency4"), + List.of(UUID.randomUUID()) + )) + ); + String expectedJson = objectMapper.writeValueAsString(securityAnalysisConfigs); + + when(processConfigService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS)) + .thenReturn(securityAnalysisConfigs); + + mockMvc.perform(get("/v1/process-configs") + .param("processType", ProcessType.SECURITY_ANALYSIS.name()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(expectedJson)); + + verify(processConfigService).getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + } + + @Test + void getAllSecurityAnalysisConfigsNotFound() throws Exception { + when(processConfigService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS)) + .thenReturn(List.of()); + + mockMvc.perform(get("/v1/process-configs") + .param("processType", ProcessType.SECURITY_ANALYSIS.name()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json("[]")); + + verify(processConfigService).getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + } } diff --git a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/ProcessConfigServiceTest.java b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/ProcessConfigServiceTest.java index 284fd0f8..130f5d07 100644 --- a/monitor-server/src/test/java/org/gridsuite/monitor/server/services/ProcessConfigServiceTest.java +++ b/monitor-server/src/test/java/org/gridsuite/monitor/server/services/ProcessConfigServiceTest.java @@ -6,7 +6,7 @@ */ package org.gridsuite.monitor.server.services; -import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.PersistedProcessConfig; import org.gridsuite.monitor.commons.ProcessType; import org.gridsuite.monitor.commons.SecurityAnalysisConfig; import org.gridsuite.monitor.server.entities.SecurityAnalysisConfigEntity; @@ -36,7 +36,6 @@ */ @ExtendWith(MockitoExtension.class) class ProcessConfigServiceTest { - @Mock private ProcessConfigRepository processConfigRepository; @@ -72,7 +71,7 @@ void createSecurityAnalysisConfig() { SecurityAnalysisConfigEntity savedEntity = captor.getValue(); assertThat(savedEntity.getId()).isEqualTo(expectedProcessConfigId); - assertThat(savedEntity.getType()).isEqualTo(ProcessType.SECURITY_ANALYSIS); + assertThat(savedEntity.getProcessType()).isEqualTo(ProcessType.SECURITY_ANALYSIS); assertThat(savedEntity.getParametersUuid()).isEqualTo(securityAnalysisConfig.parametersUuid()); assertThat(savedEntity.getContingencies()).isEqualTo(securityAnalysisConfig.contingencies()); assertThat(savedEntity.getModificationUuids()).isEqualTo(securityAnalysisConfig.modificationUuids()); @@ -85,10 +84,10 @@ void getSecurityAnalysisConfig() { when(processConfigRepository.findById(processConfigId)).thenReturn(Optional.of(securityAnalysisConfigEntity)); - Optional processConfig = processConfigService.getProcessConfig(processConfigId); + Optional processConfig = processConfigService.getProcessConfig(processConfigId); verify(processConfigRepository).findById(processConfigId); assertThat(processConfig).isPresent(); - assertThat(processConfig.get()).usingRecursiveComparison().isEqualTo(securityAnalysisConfig); + assertThat(processConfig.get().processConfig()).usingRecursiveComparison().isEqualTo(securityAnalysisConfig); } @Test @@ -97,7 +96,7 @@ void getSecurityAnalysisConfigNotFound() { when(processConfigRepository.findById(processConfigId)).thenReturn(Optional.empty()); - Optional processConfig = processConfigService.getProcessConfig(processConfigId); + Optional processConfig = processConfigService.getProcessConfig(processConfigId); verify(processConfigRepository).findById(processConfigId); assertThat(processConfig).isEmpty(); } @@ -120,9 +119,9 @@ void updateSecurityAnalysisConfig() { verify(processConfigRepository).findById(processConfigId); - Optional processConfigUpdated = processConfigService.getProcessConfig(processConfigId); + Optional processConfigUpdated = processConfigService.getProcessConfig(processConfigId); assertThat(processConfigUpdated).isPresent(); - assertThat(processConfigUpdated.get()).usingRecursiveComparison().isEqualTo(newSecurityAnalysisConfig); + assertThat(processConfigUpdated.get().processConfig()).usingRecursiveComparison().isEqualTo(newSecurityAnalysisConfig); } @Test @@ -168,4 +167,42 @@ void deleteSecurityAnalysisConfigNotFound() { verify(processConfigRepository).existsById(processConfigId); verify(processConfigRepository, never()).deleteById(processConfigId); } + + @Test + void getSecurityAnalysisConfigs() { + SecurityAnalysisConfig securityAnalysisConfig1 = new SecurityAnalysisConfig(UUID.randomUUID(), List.of("contingency1", "contingency2"), List.of(UUID.randomUUID())); + SecurityAnalysisConfigEntity securityAnalysisConfigEntity1 = SecurityAnalysisConfigMapper.toEntity(securityAnalysisConfig1); + SecurityAnalysisConfig securityAnalysisConfig2 = new SecurityAnalysisConfig(UUID.randomUUID(), List.of("contingency3", "contingency4"), List.of(UUID.randomUUID())); + SecurityAnalysisConfigEntity securityAnalysisConfigEntity2 = SecurityAnalysisConfigMapper.toEntity(securityAnalysisConfig2); + + when(processConfigRepository.findAllByProcessType(ProcessType.SECURITY_ANALYSIS)) + .thenReturn(List.of(securityAnalysisConfigEntity1, securityAnalysisConfigEntity2)); + + List processConfigs = processConfigService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + + verify(processConfigRepository).findAllByProcessType(ProcessType.SECURITY_ANALYSIS); + assertThat(processConfigs).hasSize(2); + assertThat(processConfigs.get(0).processConfig().processType()).isEqualTo(ProcessType.SECURITY_ANALYSIS); + assertThat(processConfigs.get(1).processConfig().processType()).isEqualTo(ProcessType.SECURITY_ANALYSIS); + + SecurityAnalysisConfig resSecurityAnalysisConfig1 = (SecurityAnalysisConfig) processConfigs.get(0).processConfig(); + assertThat(resSecurityAnalysisConfig1.parametersUuid()).isEqualTo(securityAnalysisConfig1.parametersUuid()); + assertThat(resSecurityAnalysisConfig1.contingencies()).isEqualTo(securityAnalysisConfig1.contingencies()); + assertThat(resSecurityAnalysisConfig1.modificationUuids()).isEqualTo(securityAnalysisConfig1.modificationUuids()); + + SecurityAnalysisConfig resSecurityAnalysisConfig2 = (SecurityAnalysisConfig) processConfigs.get(1).processConfig(); + assertThat(resSecurityAnalysisConfig2.parametersUuid()).isEqualTo(securityAnalysisConfig2.parametersUuid()); + assertThat(resSecurityAnalysisConfig2.contingencies()).isEqualTo(securityAnalysisConfig2.contingencies()); + assertThat(resSecurityAnalysisConfig2.modificationUuids()).isEqualTo(securityAnalysisConfig2.modificationUuids()); + } + + @Test + void getSecurityAnalysisConfigsNotFound() { + when(processConfigRepository.findAllByProcessType(ProcessType.SECURITY_ANALYSIS)).thenReturn(List.of()); + + List processConfigs = processConfigService.getProcessConfigs(ProcessType.SECURITY_ANALYSIS); + + verify(processConfigRepository).findAllByProcessType(ProcessType.SECURITY_ANALYSIS); + assertThat(processConfigs).isEmpty(); + } } From 51bcf5457d6d06bf3f53bdac0f8e4f5f7fbd7f83 Mon Sep 17 00:00:00 2001 From: Kamil MARUT <38701488+KoloMenek@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:23:51 +0100 Subject: [PATCH 22/34] refactor: move modificationUuids from ProcessConfig to ModifyingProcessConfig interface (#43) Moved modificationUuids from ProcessConfig to ModifyingProcessConfig interface in order to make the ProcessConfig interface more generic. It is required for further developments related to SnapshotRefinerServer's connection to monitor-server Signed-off-by: Kamil MARUT --- .../commons/ModifyingProcessConfig.java | 19 ++++++++ .../monitor/commons/ProcessConfig.java | 4 -- .../commons/SecurityAnalysisConfig.java | 3 +- .../commons/steps/ApplyModificationsStep.java | 4 +- .../steps/ApplyModificationsStepTest.java | 46 +++++++++++++------ 5 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/ModifyingProcessConfig.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ModifyingProcessConfig.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ModifyingProcessConfig.java new file mode 100644 index 00000000..80ff6542 --- /dev/null +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ModifyingProcessConfig.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons; + +import java.util.List; +import java.util.UUID; + +/** + * @author Kamil MARUT {@literal } + */ +public interface ModifyingProcessConfig extends ProcessConfig { + + List modificationUuids(); + +} diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessConfig.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessConfig.java index 419e426f..b415e73e 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessConfig.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessConfig.java @@ -9,9 +9,6 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import java.util.List; -import java.util.UUID; - /** * @author Antoine Bouhours */ @@ -25,5 +22,4 @@ public interface ProcessConfig { ProcessType processType(); - List modificationUuids(); } diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java index 639ee4c6..3e98f6a7 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java @@ -16,7 +16,8 @@ public record SecurityAnalysisConfig( UUID parametersUuid, List contingencies, List modificationUuids -) implements ProcessConfig { +) implements ModifyingProcessConfig { + @Override public ProcessType processType() { return ProcessType.SECURITY_ANALYSIS; diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java index 45be4f5b..d37d6d45 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStep.java @@ -11,7 +11,7 @@ import com.powsybl.iidm.network.Network; import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.modification.dto.ModificationInfos; -import org.gridsuite.monitor.commons.ProcessConfig; +import org.gridsuite.monitor.commons.ModifyingProcessConfig; import org.gridsuite.monitor.worker.server.core.AbstractProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.gridsuite.monitor.worker.server.services.*; @@ -29,7 +29,7 @@ * @author Antoine Bouhours */ @Component -public class ApplyModificationsStep extends AbstractProcessStep { +public class ApplyModificationsStep extends AbstractProcessStep { private final NetworkModificationService networkModificationService; private final NetworkModificationRestService networkModificationRestService; diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index 4aa694c6..0e5be278 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -13,9 +13,9 @@ import org.gridsuite.modification.dto.LoadModificationInfos; import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; -import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.ReportInfos; +import org.gridsuite.monitor.commons.ModifyingProcessConfig; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; +import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -54,30 +55,27 @@ class ApplyModificationsStepTest { private S3Service s3Service; @Mock - private ProcessConfig config; + private ModifyingProcessConfig config; - private ApplyModificationsStep applyModificationsStep; + private ApplyModificationsStep applyModificationsStep; @Mock - private ProcessStepExecutionContext stepContext; + private ProcessStepExecutionContext stepContext; private static final UUID MODIFICATION_UUID = UUID.randomUUID(); private static final UUID REPORT_UUID = UUID.randomUUID(); @BeforeEach void setUp() { - applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, s3Service, filterService); - when(config.modificationUuids()).thenReturn(List.of(MODIFICATION_UUID)); when(stepContext.getConfig()).thenReturn(config); - ReportInfos reportInfos = new ReportInfos(REPORT_UUID, ReportNode.newRootReportNode() - .withResourceBundles("i18n.reports") - .withMessageTemplate("test") - .build()); - when(stepContext.getReportInfos()).thenReturn(reportInfos); + + applyModificationsStep = new ApplyModificationsStep<>(networkModificationService, networkModificationRestService, s3Service, filterService); } @Test - void executeApplyModifications() { + void executeApplyModificationsWhenModificationUuidsNotEmpty() { + setUpReportInfos(); + when(config.modificationUuids()).thenReturn(List.of(MODIFICATION_UUID)); String stepType = applyModificationsStep.getType().getName(); assertEquals("APPLY_MODIFICATIONS", stepType); @@ -93,8 +91,22 @@ void executeApplyModifications() { verify(networkModificationService).applyModifications(any(Network.class), any(List.class), any(ReportNode.class), any(FilterService.class)); } + @Test + void executeDoesNothingWhenModificationUuidsEmpty() { + when(config.modificationUuids()).thenReturn(Collections.emptyList()); + + applyModificationsStep.execute(stepContext); + + verifyNoInteractions(networkModificationService); + verifyNoInteractions(networkModificationRestService); + verifyNoInteractions(filterService); + verifyNoInteractions(s3Service); + } + @Test void executeApplyModificationsDebugOn() throws IOException { + setUpReportInfos(); + when(config.modificationUuids()).thenReturn(List.of(MODIFICATION_UUID)); String stepType = applyModificationsStep.getType().getName(); assertEquals("APPLY_MODIFICATIONS", stepType); @@ -135,4 +147,12 @@ void executeApplyModificationsDebugOn() throws IOException { verify(network).write("XIIDM", null, mockedPath); } + + private void setUpReportInfos() { + ReportInfos reportInfos = new ReportInfos(REPORT_UUID, ReportNode.newRootReportNode() + .withResourceBundles("i18n.reports") + .withMessageTemplate("test") + .build()); + when(stepContext.getReportInfos()).thenReturn(reportInfos); + } } From eb9b4a0dcdb88d409596d21ada6efb3ee2d27881 Mon Sep 17 00:00:00 2001 From: Antoine Bouhours Date: Fri, 27 Feb 2026 10:24:52 +0100 Subject: [PATCH 23/34] Code improvements (#52) --- .../mapper/ProcessExecutionStepMapper.java | 21 +++--- .../worker/server/core/AbstractProcess.java | 10 ++- .../monitor/worker/server/core/Process.java | 2 +- .../SecurityAnalysisProcess.java | 2 +- .../services/ProcessExecutionService.java | 33 +++++---- .../server/services/StepExecutionService.java | 74 +++++++++++++------ .../services/ProcessExecutionServiceTest.java | 4 +- 7 files changed, 94 insertions(+), 52 deletions(-) diff --git a/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java b/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java index b58db313..f923aa76 100644 --- a/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java +++ b/monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java @@ -15,15 +15,16 @@ @SuppressWarnings("checkstyle:HideUtilityClassConstructor") public class ProcessExecutionStepMapper { public static ProcessExecutionStep toDto(ProcessExecutionStepEntity entity) { - return new ProcessExecutionStep( - entity.getId(), - entity.getStepType(), - entity.getStepOrder(), - entity.getStatus(), - entity.getResultId(), - entity.getResultType(), - entity.getReportId(), - entity.getStartedAt(), - entity.getCompletedAt()); + return ProcessExecutionStep.builder() + .id(entity.getId()) + .stepType(entity.getStepType()) + .stepOrder(entity.getStepOrder()) + .status(entity.getStatus()) + .resultId(entity.getResultId()) + .resultType(entity.getResultType()) + .reportId(entity.getReportId()) + .startedAt(entity.getStartedAt()) + .completedAt(entity.getCompletedAt()) + .build(); } } diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/AbstractProcess.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/AbstractProcess.java index 9b1a7198..b87e532e 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/AbstractProcess.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/AbstractProcess.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; import java.util.List; /** @@ -32,9 +33,16 @@ protected AbstractProcess( this.stepExecutionService = stepExecutionService; } + protected abstract List> defineSteps(); + + @Override + public List> getSteps() { + return Collections.unmodifiableList(defineSteps()); + } + @Override public void execute(ProcessExecutionContext context) { - List> steps = defineSteps(); + List> steps = getSteps(); boolean skipRemaining = false; for (int i = 0; i < steps.size(); i++) { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/Process.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/Process.java index 0959f0b8..1dabfcca 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/Process.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/core/Process.java @@ -20,5 +20,5 @@ public interface Process { void execute(ProcessExecutionContext context); - List> defineSteps(); + List> getSteps(); } diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/SecurityAnalysisProcess.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/SecurityAnalysisProcess.java index 0e80ed74..844af215 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/SecurityAnalysisProcess.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/processes/securityanalysis/SecurityAnalysisProcess.java @@ -40,7 +40,7 @@ public SecurityAnalysisProcess( } @Override - public List> defineSteps() { + protected List> defineSteps() { return List.of( loadNetworkStep, applyModificationsStep, diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java index 470ec754..fa9f5c9b 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java @@ -61,24 +61,31 @@ public void executeProcess(ProcessRunMessage runMes ); try { - List> steps = process.defineSteps(); - notificationService.updateStepsStatuses(context.getExecutionId(), - IntStream.range(0, steps.size()) - .mapToObj(i -> new ProcessExecutionStep(steps.get(i).getId(), steps.get(i).getType().getName(), i, StepStatus.SCHEDULED, null, null, null, null, null)) - .toList()); + initializeSteps(process, context); + executeSteps(process, context); } catch (Exception e) { updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.FAILED); throw e; } + } - try { - updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.RUNNING); - process.execute(context); - updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.COMPLETED); - } catch (Exception e) { - updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.FAILED); - throw e; - } + private void initializeSteps(Process process, ProcessExecutionContext context) { + List> steps = process.getSteps(); + notificationService.updateStepsStatuses(context.getExecutionId(), + IntStream.range(0, steps.size()) + .mapToObj(i -> ProcessExecutionStep.builder() + .id(steps.get(i).getId()) + .stepType(steps.get(i).getType().getName()) + .stepOrder(i) + .status(StepStatus.SCHEDULED) + .build()) + .toList()); + } + + private void executeSteps(Process process, ProcessExecutionContext context) { + updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.RUNNING); + process.execute(context); + updateExecutionStatus(context.getExecutionId(), context.getExecutionEnvName(), ProcessStatus.COMPLETED); } private void updateExecutionStatus(UUID executionId, String envName, ProcessStatus status) { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index 0a1e46de..bac5754b 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,45 +6,71 @@ */ package org.gridsuite.monitor.worker.server.services; +import lombok.RequiredArgsConstructor; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.StepStatus; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.springframework.stereotype.Service; -import java.util.Objects; +import java.time.Instant; /** * @author Antoine Bouhours */ @Service -public class StepExecutionService extends AbstractStepExecutor { +@RequiredArgsConstructor +public class StepExecutionService { - public StepExecutionService(NotificationService notificationService, ReportService reportService) { - this.stepStatusPublisher = notificationService::updateStepStatus; - this.reportPublisher = reportService::sendReport; - } + private final NotificationService notificationService; + private final ReportService reportService; - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - skipStep(context.getProcessExecutionId(), - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - context.getStartedAt() - ); + public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(context.getStepExecutionId()) + .stepType(step.getType().getName()) + .stepOrder(context.getStepOrder()) + .status(StepStatus.SKIPPED) + .startedAt(context.getStartedAt()) + .completedAt(Instant.now()) + .build(); + notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - executeStep(context.getProcessExecutionId(), - context.getStepExecutionId(), - step.getType().getName(), - context.getStepOrder(), - context.getStartedAt(), - Objects.requireNonNull(context.getReportInfos()).reportUuid(), - context.getReportInfos(), - context.getResultInfos(), - () -> step.execute(context) - ); + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(context.getStepExecutionId()) + .stepType(step.getType().getName()) + .stepOrder(context.getStepOrder()) + .status(StepStatus.RUNNING) + .reportId(context.getReportInfos().reportUuid()) + .startedAt(context.getStartedAt()) + .build(); + notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); + + try { + step.execute(context); + reportService.sendReport(context.getReportInfos()); + updateStepStatus(context, StepStatus.COMPLETED, step); + } catch (Exception e) { + updateStepStatus(context, StepStatus.FAILED, step); + throw e; + } } + private void updateStepStatus(ProcessStepExecutionContext context, StepStatus status, ProcessStep step) { + ProcessExecutionStep updated = ProcessExecutionStep.builder() + .id(context.getStepExecutionId()) + .stepType(step.getType().getName()) + .stepOrder(context.getStepOrder()) + .status(status) + .resultId(context.getResultInfos() != null ? context.getResultInfos().resultUUID() : null) + .resultType(context.getResultInfos() != null ? context.getResultInfos().resultType() : null) + .reportId(context.getReportInfos().reportUuid()) + .startedAt(context.getStartedAt()) + .completedAt(Instant.now()) + .build(); + notificationService.updateStepStatus(context.getProcessExecutionId(), updated); + } } diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java index 15ea89fc..8bc8c811 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionServiceTest.java @@ -94,11 +94,11 @@ void executeProcessShouldCompleteSuccessfullyWhenProcessExecutesWithoutError() { when(processConfig.processType()).thenReturn(ProcessType.SECURITY_ANALYSIS); doNothing().when(process).execute(any(ProcessExecutionContext.class)); ProcessRunMessage runMessage = new ProcessRunMessage<>(executionId, caseUuid, processConfig, null); - when(process.defineSteps()).thenReturn((List) List.of(loadNetworkStep, applyModificationsStep, runComputationStep)); + when(process.getSteps()).thenReturn((List) List.of(loadNetworkStep, applyModificationsStep, runComputationStep)); processExecutionService.executeProcess(runMessage); - verify(process, times(1)).defineSteps(); + verify(process, times(1)).getSteps(); verify(notificationService, times(1)).updateStepsStatuses(eq(executionId), argThat(steps -> steps.size() == 3 && steps.get(0).getStatus() == StepStatus.SCHEDULED && From 81d722908614601843184428eff238692dccbd2a Mon Sep 17 00:00:00 2001 From: klesaulnier <42617371+klesaulnier@users.noreply.github.com> Date: Fri, 27 Feb 2026 11:25:25 +0100 Subject: [PATCH 24/34] update to gridsuite-dependencies 49 (#53) Signed-off-by: LE SAULNIER Kevin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 09f11506..3ba5b7b5 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ - 46.0.0 + 49.0.0 gridsuite org.gridsuite:monitor-core From 9dbd43e54f2caa7ffb33875bb8a44d74f450e852 Mon Sep 17 00:00:00 2001 From: Antoine Bouhours Date: Fri, 27 Feb 2026 15:53:08 +0100 Subject: [PATCH 25/34] Add copyright and author comments to SecurityAnalysisService (#54) --- .../worker/server/services/SecurityAnalysisService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/SecurityAnalysisService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/SecurityAnalysisService.java index c4344fbf..e729c393 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/SecurityAnalysisService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/SecurityAnalysisService.java @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ package org.gridsuite.monitor.worker.server.services; import com.powsybl.security.SecurityAnalysisResult; @@ -17,6 +23,9 @@ import java.util.Objects; import java.util.UUID; +/** + * @author Kevin Le Saulnier + */ @Service public class SecurityAnalysisService { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityAnalysisService.class); From daed7f4e7c93d1b8acb5ad831c6d8c6702b05603 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 09:36:45 +0100 Subject: [PATCH 26/34] merge with main Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 57 +++++++++---------- .../steps/ApplyModificationsStepTest.java | 2 +- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 57eaef8c..0aa7cd5c 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -34,16 +34,14 @@ public void skipStep( int stepOrder, Instant startedAt ) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.SKIPPED, - null, - null, - null, - startedAt, - Instant.now()); + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(StepStatus.SKIPPED) + .startedAt(startedAt) + .completedAt(Instant.now()) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); } @@ -58,16 +56,14 @@ public void executeStep( ResultInfos resultInfos, Runnable stepExecution ) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.RUNNING, - null, - null, - reportUuid, - startedAt, - null); + ProcessExecutionStep executionStep = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(StepStatus.RUNNING) + .reportId(reportUuid) + .startedAt(startedAt) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, executionStep); try { @@ -106,16 +102,17 @@ private void updateStepStatus( ResultInfos resultInfos, StepStatus status ) { - ProcessExecutionStep updated = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - status, - resultInfos != null ? resultInfos.resultUUID() : null, - resultInfos != null ? resultInfos.resultType() : null, - reportUuid, - startedAt, - Instant.now()); + ProcessExecutionStep updated = ProcessExecutionStep.builder() + .id(stepExecutionId) + .stepType(stepTypeName) + .stepOrder(stepOrder) + .status(status) + .resultId(resultInfos != null ? resultInfos.resultUUID() : null) + .resultType(resultInfos != null ? resultInfos.resultType() : null) + .reportId(reportUuid) + .startedAt(startedAt) + .completedAt(Instant.now()) + .build(); stepStatusPublisher.updateStepStatus(processExecutionId, updated); } } diff --git a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java index 0e5be278..5c4f3bd5 100644 --- a/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java +++ b/monitor-worker-server/src/test/java/org/gridsuite/monitor/worker/server/processes/commons/steps/ApplyModificationsStepTest.java @@ -14,8 +14,8 @@ import org.gridsuite.modification.dto.ModificationInfos; import org.gridsuite.modification.dto.OperationType; import org.gridsuite.monitor.commons.ModifyingProcessConfig; +import org.gridsuite.monitor.commons.ReportInfos; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; -import org.gridsuite.monitor.worker.server.dto.ReportInfos; import org.gridsuite.monitor.worker.server.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 4c2fc29aed91b1473ca5c9bf21ac16742be789bf Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 15:14:56 +0100 Subject: [PATCH 27/34] add unit tests Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- monitor-commons/pom.xml | 7 + .../commons/steps/AbstractStepExecutor.java | 2 - .../commons/steps/ReportPublisherTest.java | 33 ++++ .../commons/steps/StepExecutorTest.java | 156 ++++++++++++++++++ .../steps/StepStatusPublisherTest.java | 40 +++++ 5 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java create mode 100644 monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java diff --git a/monitor-commons/pom.xml b/monitor-commons/pom.xml index cb20fb46..5d8bb2cf 100644 --- a/monitor-commons/pom.xml +++ b/monitor-commons/pom.xml @@ -27,5 +27,12 @@ com.powsybl powsybl-commons
+ + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 0aa7cd5c..1d52e894 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -20,8 +20,6 @@ public abstract class AbstractStepExecutor { public AbstractStepExecutor() { - StepStatusPublisher stepStatusPublisher; - ReportPublisher reportPublisher; } protected StepStatusPublisher stepStatusPublisher; diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java new file mode 100644 index 00000000..526cbf20 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/ReportPublisherTest.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import com.powsybl.commons.report.ReportNode; +import org.gridsuite.monitor.commons.ReportInfos; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @author Mohamed Ben-rejeb + */ +class ReportPublisherTest { + + @Test + void sendReportShouldForwardReportInfosToImplementation() { + AtomicReference published = new AtomicReference<>(); + ReportPublisher reportPublisher = published::set; + ReportInfos reportInfos = new ReportInfos(UUID.randomUUID(), ReportNode.NO_OP); + + reportPublisher.sendReport(reportInfos); + + assertSame(reportInfos, published.get()); + } +} diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java new file mode 100644 index 00000000..b181af18 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.gridsuite.monitor.commons.ReportInfos; +import org.gridsuite.monitor.commons.ResultInfos; +import org.gridsuite.monitor.commons.ResultType; +import org.gridsuite.monitor.commons.StepStatus; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Mohamed Ben-rejeb + */ +class StepExecutorTest { + + @Test + void skipStepShouldPublishSkippedStatus() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + executor.skipStep(processExecutionId, stepExecutionId, "LOAD_NETWORK", 1, startedAt); + + assertEquals(1, executor.publishedSteps().size()); + PublishedStep published = executor.publishedSteps().get(0); + assertEquals(processExecutionId, published.processExecutionId()); + assertEquals(stepExecutionId, published.step().getId()); + assertEquals("LOAD_NETWORK", published.step().getStepType()); + assertEquals(1, published.step().getStepOrder()); + assertEquals(StepStatus.SKIPPED, published.step().getStatus()); + assertNull(published.step().getResultId()); + assertNull(published.step().getResultType()); + assertNull(published.step().getReportId()); + assertEquals(startedAt, published.step().getStartedAt()); + assertNotNull(published.step().getCompletedAt()); + assertTrue(executor.publishedReports().isEmpty()); + } + + @Test + void executeStepShouldPublishRunningThenCompletedAndSendReport() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + UUID reportId = UUID.randomUUID(); + UUID resultId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + ResultInfos resultInfos = new ResultInfos(resultId, ResultType.SECURITY_ANALYSIS); + ReportInfos reportInfos = new ReportInfos(reportId, null); + AtomicInteger executionCount = new AtomicInteger(0); + + executor.executeStep( + processExecutionId, + stepExecutionId, + "RUN_SECURITY_ANALYSIS", + 2, + startedAt, + reportId, + reportInfos, + resultInfos, + executionCount::incrementAndGet + ); + + assertEquals(1, executionCount.get()); + assertEquals(List.of(reportInfos), executor.publishedReports()); + assertEquals(2, executor.publishedSteps().size()); + + PublishedStep running = executor.publishedSteps().get(0); + assertEquals(processExecutionId, running.processExecutionId()); + assertEquals(StepStatus.RUNNING, running.step().getStatus()); + assertEquals(reportId, running.step().getReportId()); + assertNull(running.step().getCompletedAt()); + + PublishedStep completed = executor.publishedSteps().get(1); + assertEquals(processExecutionId, completed.processExecutionId()); + assertEquals(StepStatus.COMPLETED, completed.step().getStatus()); + assertEquals(resultId, completed.step().getResultId()); + assertEquals(ResultType.SECURITY_ANALYSIS, completed.step().getResultType()); + assertEquals(reportId, completed.step().getReportId()); + assertNotNull(completed.step().getCompletedAt()); + } + + @Test + void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { + TestStepExecutor executor = new TestStepExecutor(); + UUID processExecutionId = UUID.randomUUID(); + UUID stepExecutionId = UUID.randomUUID(); + UUID reportId = UUID.randomUUID(); + Instant startedAt = Instant.now(); + + RuntimeException failure = new RuntimeException("boom"); + + RuntimeException thrown = assertThrows(RuntimeException.class, () -> executor.executeStep( + processExecutionId, + stepExecutionId, + "FAILING_STEP", + 3, + startedAt, + reportId, + new ReportInfos(reportId, null), + null, + () -> { + throw failure; + } + )); + + assertSame(failure, thrown); + assertTrue(executor.publishedReports().isEmpty()); + assertEquals(2, executor.publishedSteps().size()); + + PublishedStep running = executor.publishedSteps().get(0); + assertEquals(StepStatus.RUNNING, running.step().getStatus()); + + PublishedStep failed = executor.publishedSteps().get(1); + assertEquals(StepStatus.FAILED, failed.step().getStatus()); + assertNull(failed.step().getResultId()); + assertNull(failed.step().getResultType()); + assertNotNull(failed.step().getCompletedAt()); + } + + private static final class TestStepExecutor extends AbstractStepExecutor { + private final List publishedSteps = new ArrayList<>(); + private final List publishedReports = new ArrayList<>(); + + private TestStepExecutor() { + this.stepStatusPublisher = (executionId, processExecutionStep) -> + publishedSteps.add(new PublishedStep(executionId, processExecutionStep)); + this.reportPublisher = publishedReports::add; + } + + List publishedSteps() { + return publishedSteps; + } + + List publishedReports() { + return publishedReports; + } + } + + private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { + } +} diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java new file mode 100644 index 00000000..a2f3a857 --- /dev/null +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepStatusPublisherTest.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.monitor.commons.steps; + +import org.gridsuite.monitor.commons.ProcessExecutionStep; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @author Mohamed Ben-rejeb + */ +class StepStatusPublisherTest { + + @Test + void updateStepStatusShouldForwardExecutionIdAndStepToImplementation() { + AtomicReference publishedExecutionId = new AtomicReference<>(); + AtomicReference publishedStep = new AtomicReference<>(); + StepStatusPublisher stepStatusPublisher = (executionId, processExecutionStep) -> { + publishedExecutionId.set(executionId); + publishedStep.set(processExecutionStep); + }; + + UUID executionId = UUID.randomUUID(); + ProcessExecutionStep step = ProcessExecutionStep.builder().stepType("step").build(); + + stepStatusPublisher.updateStepStatus(executionId, step); + + assertEquals(executionId, publishedExecutionId.get()); + assertSame(step, publishedStep.get()); + } +} From cc9e831af14a82686567e3b659a55dbac9c63d20 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Mar 2026 15:28:46 +0100 Subject: [PATCH 28/34] add unit tests and fix sonar issues Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 2 +- .../commons/steps/StepExecutorTest.java | 195 +++++++++++++----- 2 files changed, 142 insertions(+), 55 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 1d52e894..2e4d6506 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,7 +19,7 @@ */ public abstract class AbstractStepExecutor { - public AbstractStepExecutor() { + protected AbstractStepExecutor() { } protected StepStatusPublisher stepStatusPublisher; diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java index b181af18..8bf6e9a7 100644 --- a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -35,19 +35,43 @@ void skipStepShouldPublishSkippedStatus() { executor.skipStep(processExecutionId, stepExecutionId, "LOAD_NETWORK", 1, startedAt); - assertEquals(1, executor.publishedSteps().size()); - PublishedStep published = executor.publishedSteps().get(0); + assertEquals( + 1, + executor.publishedSteps() + .size()); + PublishedStep published = executor.publishedSteps() + .get(0); assertEquals(processExecutionId, published.processExecutionId()); - assertEquals(stepExecutionId, published.step().getId()); - assertEquals("LOAD_NETWORK", published.step().getStepType()); - assertEquals(1, published.step().getStepOrder()); - assertEquals(StepStatus.SKIPPED, published.step().getStatus()); - assertNull(published.step().getResultId()); - assertNull(published.step().getResultType()); - assertNull(published.step().getReportId()); - assertEquals(startedAt, published.step().getStartedAt()); - assertNotNull(published.step().getCompletedAt()); - assertTrue(executor.publishedReports().isEmpty()); + assertEquals( + stepExecutionId, + published.step() + .getId()); + assertEquals( + "LOAD_NETWORK", + published.step() + .getStepType()); + assertEquals( + 1, + published.step() + .getStepOrder()); + assertEquals( + StepStatus.SKIPPED, + published.step() + .getStatus()); + assertNull(published.step() + .getResultId()); + assertNull(published.step() + .getResultType()); + assertNull(published.step() + .getReportId()); + assertEquals( + startedAt, + published.step() + .getStartedAt()); + assertNotNull(published.step() + .getCompletedAt()); + assertTrue(executor.publishedReports() + .isEmpty()); } @Test @@ -72,26 +96,50 @@ void executeStepShouldPublishRunningThenCompletedAndSendReport() { reportId, reportInfos, resultInfos, - executionCount::incrementAndGet - ); + executionCount::incrementAndGet); assertEquals(1, executionCount.get()); assertEquals(List.of(reportInfos), executor.publishedReports()); - assertEquals(2, executor.publishedSteps().size()); + assertEquals( + 2, + executor.publishedSteps() + .size()); - PublishedStep running = executor.publishedSteps().get(0); + PublishedStep running = executor.publishedSteps() + .get(0); assertEquals(processExecutionId, running.processExecutionId()); - assertEquals(StepStatus.RUNNING, running.step().getStatus()); - assertEquals(reportId, running.step().getReportId()); - assertNull(running.step().getCompletedAt()); + assertEquals( + StepStatus.RUNNING, + running.step() + .getStatus()); + assertEquals( + reportId, + running.step() + .getReportId()); + assertNull(running.step() + .getCompletedAt()); - PublishedStep completed = executor.publishedSteps().get(1); + PublishedStep completed = executor.publishedSteps() + .get(1); assertEquals(processExecutionId, completed.processExecutionId()); - assertEquals(StepStatus.COMPLETED, completed.step().getStatus()); - assertEquals(resultId, completed.step().getResultId()); - assertEquals(ResultType.SECURITY_ANALYSIS, completed.step().getResultType()); - assertEquals(reportId, completed.step().getReportId()); - assertNotNull(completed.step().getCompletedAt()); + assertEquals( + StepStatus.COMPLETED, + completed.step() + .getStatus()); + assertEquals( + resultId, + completed.step() + .getResultId()); + assertEquals( + ResultType.SECURITY_ANALYSIS, + completed.step() + .getResultType()); + assertEquals( + reportId, + completed.step() + .getReportId()); + assertNotNull(completed.step() + .getCompletedAt()); } @Test @@ -102,43 +150,62 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { UUID reportId = UUID.randomUUID(); Instant startedAt = Instant.now(); - RuntimeException failure = new RuntimeException("boom"); - - RuntimeException thrown = assertThrows(RuntimeException.class, () -> executor.executeStep( - processExecutionId, - stepExecutionId, - "FAILING_STEP", - 3, - startedAt, - reportId, - new ReportInfos(reportId, null), - null, - () -> { - throw failure; - } - )); + ReportInfos reportInfos = new ReportInfos(reportId, null); + RuntimeException failure = new RuntimeException("exp"); + + Runnable failingExecution = () -> { + throw failure; + }; + + RuntimeException thrown = assertThrows( + RuntimeException.class, + () -> executeFailingStep( + executor, + processExecutionId, + stepExecutionId, + startedAt, + reportId, + reportInfos, + failingExecution)); assertSame(failure, thrown); - assertTrue(executor.publishedReports().isEmpty()); - assertEquals(2, executor.publishedSteps().size()); - - PublishedStep running = executor.publishedSteps().get(0); - assertEquals(StepStatus.RUNNING, running.step().getStatus()); - - PublishedStep failed = executor.publishedSteps().get(1); - assertEquals(StepStatus.FAILED, failed.step().getStatus()); - assertNull(failed.step().getResultId()); - assertNull(failed.step().getResultType()); - assertNotNull(failed.step().getCompletedAt()); + assertTrue(executor.publishedReports() + .isEmpty()); + assertEquals( + 2, + executor.publishedSteps() + .size()); + + PublishedStep running = executor.publishedSteps() + .get(0); + assertEquals( + StepStatus.RUNNING, + running.step() + .getStatus()); + + PublishedStep failed = executor.publishedSteps() + .get(1); + assertEquals( + StepStatus.FAILED, + failed.step() + .getStatus()); + assertNull(failed.step() + .getResultId()); + assertNull(failed.step() + .getResultType()); + assertNotNull(failed.step() + .getCompletedAt()); } private static final class TestStepExecutor extends AbstractStepExecutor { + private final List publishedSteps = new ArrayList<>(); private final List publishedReports = new ArrayList<>(); private TestStepExecutor() { - this.stepStatusPublisher = (executionId, processExecutionStep) -> - publishedSteps.add(new PublishedStep(executionId, processExecutionStep)); + this.stepStatusPublisher = (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( + executionId, + processExecutionStep)); this.reportPublisher = publishedReports::add; } @@ -151,6 +218,26 @@ List publishedReports() { } } - private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { + private static void executeFailingStep( + TestStepExecutor executor, + UUID processExecutionId, + UUID stepExecutionId, + Instant startedAt, + UUID reportId, + ReportInfos reportInfos, + Runnable failingExecution + ) { + executor.executeStep( + processExecutionId, + stepExecutionId, + "FAILING_STEP", + 3, + startedAt, + reportId, + reportInfos, + null, + failingExecution); } + + private record PublishedStep(UUID processExecutionId, ProcessExecutionStep step) { } } From 5c0fb8e31ae3b98e613e8ded0c130c0bbb06bc55 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 10:28:17 +0100 Subject: [PATCH 29/34] set Step and report publishers in the constructor of the parent class Signed-off-by: benrejebmoh --- .../commons/steps/AbstractStepExecutor.java | 10 ++- .../commons/steps/StepExecutorTest.java | 21 ++++-- .../server/services/StepExecutionService.java | 73 ++++++------------- 3 files changed, 42 insertions(+), 62 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java index 2e4d6506..4ea0db2a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/AbstractStepExecutor.java @@ -19,11 +19,13 @@ */ public abstract class AbstractStepExecutor { - protected AbstractStepExecutor() { - } + protected final StepStatusPublisher stepStatusPublisher; + protected final ReportPublisher reportPublisher; - protected StepStatusPublisher stepStatusPublisher; - protected ReportPublisher reportPublisher; + protected AbstractStepExecutor(StepStatusPublisher stepStatusPublisher, ReportPublisher reportPublisher) { + this.stepStatusPublisher = stepStatusPublisher; + this.reportPublisher = reportPublisher; + } public void skipStep( UUID processExecutionId, diff --git a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java index 8bf6e9a7..fbfd4ada 100644 --- a/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java +++ b/monitor-commons/src/test/java/org/gridsuite/monitor/commons/steps/StepExecutorTest.java @@ -158,8 +158,7 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { }; RuntimeException thrown = assertThrows( - RuntimeException.class, - () -> executeFailingStep( + RuntimeException.class, () -> executeFailingStep( executor, processExecutionId, stepExecutionId, @@ -199,14 +198,20 @@ void executeStepShouldPublishFailedStatusAndRethrowWhenStepThrows() { private static final class TestStepExecutor extends AbstractStepExecutor { - private final List publishedSteps = new ArrayList<>(); - private final List publishedReports = new ArrayList<>(); + private final List publishedSteps; + private final List publishedReports; private TestStepExecutor() { - this.stepStatusPublisher = (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( - executionId, - processExecutionStep)); - this.reportPublisher = publishedReports::add; + this(new ArrayList<>(), new ArrayList<>()); + } + + private TestStepExecutor(List publishedSteps, List publishedReports) { + super( + (executionId, processExecutionStep) -> publishedSteps.add(new PublishedStep( + executionId, + processExecutionStep)), publishedReports::add); + this.publishedSteps = publishedSteps; + this.publishedReports = publishedReports; } List publishedSteps() { diff --git a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java index bac5754b..e9baac9e 100644 --- a/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java +++ b/monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java @@ -6,71 +6,44 @@ */ package org.gridsuite.monitor.worker.server.services; -import lombok.RequiredArgsConstructor; import org.gridsuite.monitor.commons.ProcessConfig; -import org.gridsuite.monitor.commons.ProcessExecutionStep; -import org.gridsuite.monitor.commons.StepStatus; +import org.gridsuite.monitor.commons.steps.AbstractStepExecutor; import org.gridsuite.monitor.worker.server.core.ProcessStep; import org.gridsuite.monitor.worker.server.core.ProcessStepExecutionContext; import org.springframework.stereotype.Service; -import java.time.Instant; +import java.util.Objects; /** * @author Antoine Bouhours */ @Service -@RequiredArgsConstructor -public class StepExecutionService { +public class StepExecutionService extends AbstractStepExecutor { - private final NotificationService notificationService; - private final ReportService reportService; + public StepExecutionService(NotificationService notificationService, ReportService reportService) { + super(notificationService::updateStepStatus, reportService::sendReport); + } - public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = ProcessExecutionStep.builder() - .id(context.getStepExecutionId()) - .stepType(step.getType().getName()) - .stepOrder(context.getStepOrder()) - .status(StepStatus.SKIPPED) - .startedAt(context.getStartedAt()) - .completedAt(Instant.now()) - .build(); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); + public void skipStep(ProcessStepExecutionContext context, ProcessStep step) { + skipStep(context.getProcessExecutionId(), + context.getStepExecutionId(), + step.getType().getName(), + context.getStepOrder(), + context.getStartedAt() + ); } public void executeStep(ProcessStepExecutionContext context, ProcessStep step) { - ProcessExecutionStep executionStep = ProcessExecutionStep.builder() - .id(context.getStepExecutionId()) - .stepType(step.getType().getName()) - .stepOrder(context.getStepOrder()) - .status(StepStatus.RUNNING) - .reportId(context.getReportInfos().reportUuid()) - .startedAt(context.getStartedAt()) - .build(); - notificationService.updateStepStatus(context.getProcessExecutionId(), executionStep); - - try { - step.execute(context); - reportService.sendReport(context.getReportInfos()); - updateStepStatus(context, StepStatus.COMPLETED, step); - } catch (Exception e) { - updateStepStatus(context, StepStatus.FAILED, step); - throw e; - } + executeStep(context.getProcessExecutionId(), + context.getStepExecutionId(), + step.getType().getName(), + context.getStepOrder(), + context.getStartedAt(), + Objects.requireNonNull(context.getReportInfos()).reportUuid(), + context.getReportInfos(), + context.getResultInfos(), + () -> step.execute(context) + ); } - private void updateStepStatus(ProcessStepExecutionContext context, StepStatus status, ProcessStep step) { - ProcessExecutionStep updated = ProcessExecutionStep.builder() - .id(context.getStepExecutionId()) - .stepType(step.getType().getName()) - .stepOrder(context.getStepOrder()) - .status(status) - .resultId(context.getResultInfos() != null ? context.getResultInfos().resultUUID() : null) - .resultType(context.getResultInfos() != null ? context.getResultInfos().resultType() : null) - .reportId(context.getReportInfos().reportUuid()) - .startedAt(context.getStartedAt()) - .completedAt(Instant.now()) - .build(); - notificationService.updateStepStatus(context.getProcessExecutionId(), updated); - } } From 262dfc4b4ae596b66250f8f65407e45f5cb52d78 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:21:15 +0100 Subject: [PATCH 30/34] activate gpg Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../gridsuite/monitor/commons/steps/ReportPublisher.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index bb119d8a..8d6e250a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,11 +6,14 @@ */ package org.gridsuite.monitor.commons.steps; +import org.gridsuite.monitor.commons.ReportInfos; + /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { + + void sendReport(ReportInfos reportInfos); - void sendReport(R reportInfos); } From 6a89f1da1157470ba6fe3d5c9594863463efe24a Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:35:24 +0100 Subject: [PATCH 31/34] retry gpg Signed-off-by: benrejebmoh --- .../org/gridsuite/monitor/commons/steps/ReportPublisher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index 8d6e250a..c1600c80 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -15,5 +15,4 @@ public interface ReportPublisher { void sendReport(ReportInfos reportInfos); - } From 36fc0b6d029617a1d78e43ba83b01681b11a8a2c Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 24 Feb 2026 16:45:24 +0100 Subject: [PATCH 32/34] extract step execution to commons Signed-off-by: benrejebmoh --- .../gridsuite/monitor/commons/steps/ReportPublisher.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index c1600c80..bb119d8a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,13 +6,11 @@ */ package org.gridsuite.monitor.commons.steps; -import org.gridsuite.monitor.commons.ReportInfos; - /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { - void sendReport(ReportInfos reportInfos); + void sendReport(R reportInfos); } From 6ae08fbac2fc6fa784fe3bbdfd817cd3389fa6b4 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 12:21:15 +0100 Subject: [PATCH 33/34] activate gpg Signed-off-by: benrejebmoh Signed-off-by: benrejebmoh --- .../gridsuite/monitor/commons/steps/ReportPublisher.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java index bb119d8a..8d6e250a 100644 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java +++ b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/ReportPublisher.java @@ -6,11 +6,14 @@ */ package org.gridsuite.monitor.commons.steps; +import org.gridsuite.monitor.commons.ReportInfos; + /** * @author Mohamed Ben-rejeb */ @FunctionalInterface -public interface ReportPublisher { +public interface ReportPublisher { + + void sendReport(ReportInfos reportInfos); - void sendReport(R reportInfos); } From d658a75e457415a9965e0ef2446e2863805761cf Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 4 Mar 2026 14:26:51 +0100 Subject: [PATCH 34/34] finishing rebase properly Signed-off-by: benrejebmoh --- .../commons/steps/StepExecutionInterface.java | 95 ------------------- 1 file changed, 95 deletions(-) delete mode 100644 monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java diff --git a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java b/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java deleted file mode 100644 index d232b114..00000000 --- a/monitor-commons/src/main/java/org/gridsuite/monitor/commons/steps/StepExecutionInterface.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2026, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.gridsuite.monitor.commons.steps; - -import org.gridsuite.monitor.commons.ProcessExecutionStep; -import org.gridsuite.monitor.commons.ResultInfos; -import org.gridsuite.monitor.commons.StepStatus; - -import java.time.Instant; -import java.util.UUID; - -/** - * @author Mohamed Ben-rejeb - */ -public interface StepExecutionInterface { - - UUID getExecutionId(); - - void setExecutionId(UUID executionId); - - StepStatusPublisher getStepStatusPublisher(); - - ReportPublisher getReportPublisher(); - - default void skipStep(UUID stepExecutionId, String stepTypeName, int stepOrder, Instant startedAt) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.SKIPPED, - null, - null, - null, - startedAt, - Instant.now() - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); - } - - default void executeStep(UUID stepExecutionId, - String stepTypeName, - int stepOrder, - Instant startedAt, - UUID reportUuid, - R reportInfos, - ResultInfos resultInfos, - Runnable stepExecution) { - ProcessExecutionStep executionStep = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - StepStatus.RUNNING, - null, - null, - reportUuid, - startedAt, - null - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), executionStep); - - try { - stepExecution.run(); - getReportPublisher().sendReport(reportInfos); - updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.COMPLETED); - } catch (Exception e) { - updateStepStatus(stepExecutionId, stepTypeName, stepOrder, startedAt, reportUuid, resultInfos, StepStatus.FAILED); - throw e; - } - } - - default void updateStepStatus(UUID stepExecutionId, - String stepTypeName, - int stepOrder, - Instant startedAt, - UUID reportUuid, - ResultInfos resultInfos, - StepStatus status) { - ProcessExecutionStep updated = new ProcessExecutionStep( - stepExecutionId, - stepTypeName, - stepOrder, - status, - resultInfos != null ? resultInfos.resultUUID() : null, - resultInfos != null ? resultInfos.resultType() : null, - reportUuid, - startedAt, - Instant.now() - ); - getStepStatusPublisher().updateStepStatus(getExecutionId(), updated); - } -}