Skip to content

POC : use existing servers to run cyclic chain#47

Open
FranckLecuyer wants to merge 4 commits intomainfrom
poc_use_exiting_servers_to_run_cyclic_chain
Open

POC : use existing servers to run cyclic chain#47
FranckLecuyer wants to merge 4 commits intomainfrom
poc_use_exiting_servers_to_run_cyclic_chain

Conversation

@FranckLecuyer
Copy link
Contributor

PR Summary

Signed-off-by: Franck LECUYER <franck.lecuyer@rte-france.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd069d and f52b58e.

📒 Files selected for processing (1)
  • monitor-server/src/main/resources/application-local.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • monitor-server/src/main/resources/application-local.yaml

📝 Walkthrough

Walkthrough

Added end-to-end "security analysis using servers" flow: new DTOs, REST services, messaging consumers, controller endpoint, service orchestration, DB schema and entity changes, and stream binding configuration to run a two-phase process (network modifications → security analysis) and propagate results.

Changes

Cohort / File(s) Summary
New DTO
monitor-commons/src/main/java/org/gridsuite/monitor/commons/CaseResultInfos.java
New immutable DTO with UUIDs, stepType and status; lombok getters and full-args constructor.
Controller API
monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java
Added POST /execute/security-analysis-using-servers endpoint delegating to monitorService.executeProcessUsingServers.
Orchestration consumers
monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java
New Spring config exposing 3 Consumer<Message<...>> beans to orchestrate run, network-modifications result handling, and security-analysis result handling; manages execution context map.
Service orchestration / Exec API
monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java, monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java
Added executeProcessUsingServers method; extended NotificationService with overloaded sendProcessRunMessage(signature with bindingName/debugFileLocation).
External REST clients
monitor-server/src/main/java/org/gridsuite/monitor/server/services/NetworkModificationRestService.java, monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java
New services that call external network-modification and security-analysis servers (v1 APIs) to apply modifications and run/save analyses.
Cloud Stream config
monitor-server/src/main/resources/config/application.yaml
Added new function definitions and bindings for run/consume flows and new topics for using-servers flow.
Local config
monitor-server/src/main/resources/application-local.yaml
Added network-modification-server base-uri for local environment.
DB / Entity / DTO expansion
monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionStepEntity.java, monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java, monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessExecutionStep.java
Added resultCaseId column/field, propagated through entity→dto mapping and DTO definition (constructor arity increased).
Liquibase
monitor-server/src/main/resources/db/changelog/changesets/changelog_20260225T073450Z.xml, monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml
New changeset adding result_case_id UUID column and included in master changelog.
Worker & tests updates
monitor-worker-server/.../ProcessExecutionService.java, monitor-worker-server/.../StepExecutionService.java, monitor-server/src/test/.../MonitorControllerTest.java, monitor-server/src/test/.../MonitorServiceTest.java
Updated call sites and tests to pass the new trailing constructor argument for ProcessExecutionStep (constructor arity increased to 10).

Sequence Diagram(s)

sequenceDiagram
    participant Client as "Client\n(API caller)"
    participant Controller as "MonitorController\n(/execute/security-analysis-using-servers)"
    participant MonitorSvc as "MonitorService"
    participant Notification as "NotificationService\n(publishes to broker)"
    participant Broker as "Message Broker\n(cloud-stream)"
    rect rgba(135,206,235,0.5)
    participant Consumer as "ConsumerServiceUsingServers\nconsumeRunUsingServers"
    end
    participant NetRest as "NetworkModificationRestService"
    participant NetServer as "Network-Modification-Server"
    participant SAstore as "SecurityAnalysisRestService"
    participant SAServer as "Security-Analysis-Server"
    participant ConsumerNet as "ConsumerServiceUsingServers\nconsumeNetworkModifications"
    participant ConsumerSA as "ConsumerServiceUsingServers\nconsumeSecurityAnalysis"
    participant MonitorUpd as "Monitor (DB updates)"
    
    Client->>Controller: POST /execute/security-analysis-using-servers(caseUuid, config)
    Controller->>MonitorSvc: executeProcessUsingServers(caseUuid,userId,config)
    MonitorSvc->>MonitorSvc: create ProcessExecutionEntity (SCHEDULED)
    MonitorSvc->>Notification: sendProcessRunMessage(..., bindingName)
    Notification->>Broker: publish run message (monitor.process.securityanalysis.using.servers.run)
    Broker->>Consumer: deliver ProcessRunMessage
    Consumer->>MonitorUpd: set execution status RUNNING, create steps
    Consumer->>NetRest: applyModifications(caseUuid, executionId, modificationUuids)
    NetRest->>NetServer: POST /v1/cases/{caseUuid}/network-composite-modifications?executionUuid=...
    NetServer-->>ConsumerNet: emit CaseResultInfos (network phase result)
    ConsumerNet->>MonitorUpd: update network step status, attach resultCaseId
    alt network succeeded
      ConsumerNet->>SAstore: runSecurityAnalysis(caseUuid, executionId, contingencies, parametersUuid)
      SAstore->>SAServer: POST /v1/cases/{caseUuid}/run-and-save?executionUuid=...
      SAServer-->>ConsumerSA: emit CaseResultInfos (security analysis result)
      ConsumerSA->>MonitorUpd: update security step + overall execution status, persist report/result UUIDs
    else network failed
      ConsumerNet->>MonitorUpd: mark execution failed and cleanup
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • thangqp
  • antoinebhs
  • klesaulnier

Poem

"A rabbit in a labcoat hops with glee, 🐇
Pipes of messages hum, a process set free.
Steps march in order, UUIDs in a row,
Servers whisper back results in their flow.
I nibble a carrot and watch the logs grow."

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is empty except for a placeholder template, providing no meaningful information about the changeset, making it impossible to assess whether it relates to the actual changes. Provide a detailed description of the changes, including what was modified and why, to help reviewers understand the purpose of the pull request.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'POC : use existing servers to run cyclic chain' is partially related to the changeset, referring to a real aspect of the changes (using existing servers), but does not clearly summarize the main point of the entire changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch poc_use_exiting_servers_to_run_cyclic_chain

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java (1)

34-37: Add connect/read timeouts on the external call.

Prevents a stuck call from blocking the caller thread indefinitely.

⏱️ Suggested change
+import java.time.Duration;
@@
-        this.securityAnalysisServerRest = restTemplateBuilder.build();
+        this.securityAnalysisServerRest = restTemplateBuilder
+            .setConnectTimeout(Duration.ofSeconds(5))
+            .setReadTimeout(Duration.ofSeconds(60))
+            .build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java`
around lines 34 - 37, The RestTemplate built in the SecurityAnalysisRestService
constructor currently has no connect/read timeouts; update the constructor to
configure timeouts on the RestTemplateBuilder before building (e.g. use
setConnectTimeout and setReadTimeout with appropriate Durations) so
securityAnalysisServerRest has both connect and read timeouts to avoid
indefinite blocking. Locate the SecurityAnalysisRestService constructor and
modify the restTemplateBuilder chain to apply the timeouts prior to calling
build().
monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java (1)

222-240: Consider extracting the execution environment name to a constant.

Avoids a magic string and makes future reuse/changes safer.

♻️ Suggested refactor
 public class MonitorService {
+    private static final String EXECUTION_ENV_USING_SERVERS = "using-servers-env";
@@
-            .executionEnvName("using-servers-env")
+            .executionEnvName(EXECUTION_ENV_USING_SERVERS)
             .build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java`
around lines 222 - 240, The method executeProcessUsingServers in MonitorService
uses a hard-coded executionEnvName "using-servers-env"; extract this magic
string to a private static final constant (e.g., EXEC_ENV_USING_SERVERS) and
replace the literal in ProcessExecutionEntity.builder().executionEnvName(...)
with that constant so the environment name is centralized for reuse and safer
refactoring across MonitorService and any other callers.
monitor-server/src/main/java/org/gridsuite/monitor/server/services/NetworkModificationRestService.java (1)

34-37: Add connect/read timeouts for this external call.

Keeps message consumer threads from hanging if the server is unresponsive.

⏱️ Suggested change
+import java.time.Duration;
@@
-        this.networkModificationServerRest = restTemplateBuilder.build();
+        this.networkModificationServerRest = restTemplateBuilder
+            .setConnectTimeout(Duration.ofSeconds(5))
+            .setReadTimeout(Duration.ofSeconds(60))
+            .build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NetworkModificationRestService.java`
around lines 34 - 37, The RestTemplate created in the
NetworkModificationRestService constructor is missing connect/read timeouts;
update the constructor to configure timeouts on the RestTemplateBuilder before
building (use RestTemplateBuilder.setConnectTimeout(Duration) and
setReadTimeout(Duration)) so networkModificationServerRest is created with
sensible timeouts (e.g., configurable via properties or reasonable defaults)
while leaving networkModificationServerBaseUri unchanged.
monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java (1)

50-50: Add an eviction/timeout strategy for execution contexts.

Entries are only removed on explicit completion/failure paths, so a lost message or hung external server can leave the map growing without bound. Consider expiring contexts after a max duration or on execution timeout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
at line 50, The processExecutionContexts map in ConsumerServiceUsingServers can
grow unbounded because entries are only removed on completion/failure; add an
eviction/timeout strategy by associating an expiration time with each
ProcessExecutionContext (or storing a small wrapper with expiry) and scheduling
periodic cleanup or per-entry scheduled removal using a
ScheduledExecutorService. On context creation (where you currently put into
processExecutionContexts) schedule a removal task that removes the UUID key
after the max allowed duration (or use a timed cache like Caffeine/Guava with
expireAfterWrite), and ensure you cancel that scheduled removal and remove the
entry immediately in the existing completion/failure paths to avoid races.
Include configuration for the timeout value and unit so it can be tuned.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`:
- Around line 76-83: The switch over processConfig.processType() only handles
SECURITY_ANALYSIS and leaves contingencies and parametersUuid null for other
types; add an explicit default branch in that switch (inside
ConsumerServiceUsingServers -> the block where contingencies and parametersUuid
are set) that marks the execution as failed and returns early instead of
continuing to schedule a security analysis, e.g. call the failure-path used
elsewhere in this class (or set the appropriate execution status/emit error and
return) so unsupported process types cannot proceed with null
contingencies/parametersUuid.
- Around line 129-134: In ConsumerServiceUsingServers, when handling
non-COMPLETED stepStatus (the else branch that currently only does
processExecutionContexts.remove(executionId)), update the corresponding
execution record to a terminal state (FAILED or CANCELLED) and set its end time
before removing the context; locate the execution by executionId (or via any
Execution/ExecutionRepository/ExecutionService used elsewhere in this class) and
call its update/persist method so the execution status and endTimestamp are
saved, then remove the processExecutionContexts entry and skip calling
securityAnalysisRestService.runSecurityAnalysis.
- Around line 85-101: The code currently creates new ProcessExecutionContext
entries and always calls networkModificationRestService.applyModifications for
every incoming message, which can cause duplicate modifications; add a
deduplication guard that checks whether this executionId already exists or is
past the start state before creating a new ProcessExecutionContext and updating
step statuses (e.g., check processExecutionContexts.containsKey(executionId) or
consult monitorService for an existing ProcessStatus for executionId and return
early if already RUNNING/COMPLETED), and ensure the external call
networkModificationRestService.applyModifications is invoked only when the guard
allows (also verify applyModifications treats executionId idempotently on the
network-modification-server side).
- Around line 148-149: The call StepStatus.valueOf(caseResultInfos.getStatus())
can throw for unexpected server values; in both consumeNetworkModifications()
and consumeSecurityAnalysis() replace direct valueOf usage with safe parsing:
catch IllegalArgumentException/NullPointerException around the valueOf call (or
use StepStatus.fromString helper) and fall back to a default safe status (e.g.,
StepStatus.UNKNOWN or StepStatus.FAILED) while logging the raw status and
context; ensure the surrounding logic uses the fallback status so consumers
don't crash or loop indefinitely and add a unit-friendly message to logs
identifying the method (consumeNetworkModifications / consumeSecurityAnalysis)
and the offending status string.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java`:
- Around line 40-42: In runSecurityAnalysis, the UriComponentsBuilder.fromPath
call builds a path with a redundant slash causing "/v1//cases/…"; fix by
removing the extra "/" before "cases" so the path concatenation uses DELIMITER +
SECURITY_ANALYSIS_SERVER_API_VERSION + DELIMITER +
"cases/{caseUuid}/run-and-save" (or otherwise ensure only one DELIMITER is
inserted), updating the UriComponentsBuilder.fromPath invocation accordingly.

In `@monitor-server/src/main/resources/config/application.yaml`:
- Around line 14-21: The three consumer bindings consumeRunUsingServers-in-0,
consumeNetworkModifications-in-0, and consumeSecurityAnalysis-in-0 should be
given explicit consumer group names to enable competing-consumer behavior;
update each binding in application.yaml to add a unique group property (for
example runUsingServersGroup, networkModificationsGroup, securityAnalysisGroup)
so only one monitor-server instance in the group processes each message and
prevents duplicate APPLY_MODIFICATIONS/SECURITY_ANALYSIS executions.

---

Nitpick comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`:
- Line 50: The processExecutionContexts map in ConsumerServiceUsingServers can
grow unbounded because entries are only removed on completion/failure; add an
eviction/timeout strategy by associating an expiration time with each
ProcessExecutionContext (or storing a small wrapper with expiry) and scheduling
periodic cleanup or per-entry scheduled removal using a
ScheduledExecutorService. On context creation (where you currently put into
processExecutionContexts) schedule a removal task that removes the UUID key
after the max allowed duration (or use a timed cache like Caffeine/Guava with
expireAfterWrite), and ensure you cancel that scheduled removal and remove the
entry immediately in the existing completion/failure paths to avoid races.
Include configuration for the timeout value and unit so it can be tuned.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java`:
- Around line 222-240: The method executeProcessUsingServers in MonitorService
uses a hard-coded executionEnvName "using-servers-env"; extract this magic
string to a private static final constant (e.g., EXEC_ENV_USING_SERVERS) and
replace the literal in ProcessExecutionEntity.builder().executionEnvName(...)
with that constant so the environment name is centralized for reuse and safer
refactoring across MonitorService and any other callers.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NetworkModificationRestService.java`:
- Around line 34-37: The RestTemplate created in the
NetworkModificationRestService constructor is missing connect/read timeouts;
update the constructor to configure timeouts on the RestTemplateBuilder before
building (use RestTemplateBuilder.setConnectTimeout(Duration) and
setReadTimeout(Duration)) so networkModificationServerRest is created with
sensible timeouts (e.g., configurable via properties or reasonable defaults)
while leaving networkModificationServerBaseUri unchanged.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java`:
- Around line 34-37: The RestTemplate built in the SecurityAnalysisRestService
constructor currently has no connect/read timeouts; update the constructor to
configure timeouts on the RestTemplateBuilder before building (e.g. use
setConnectTimeout and setReadTimeout with appropriate Durations) so
securityAnalysisServerRest has both connect and read timeouts to avoid
indefinite blocking. Locate the SecurityAnalysisRestService constructor and
modify the restTemplateBuilder chain to apply the timeouts prior to calling
build().

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e3181e and b0866ac.

📒 Files selected for processing (9)
  • monitor-commons/src/main/java/org/gridsuite/monitor/commons/CaseResultInfos.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/NetworkModificationRestService.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java
  • monitor-server/src/main/resources/application-local.yaml
  • monitor-server/src/main/resources/config/application.yaml

Comment on lines +76 to +83
List<String> contingencies = null;
UUID parametersUuid = null;
switch (processConfig.processType()) {
case SECURITY_ANALYSIS -> {
contingencies = ((SecurityAnalysisConfig) processConfig).contingencies();
parametersUuid = ((SecurityAnalysisConfig) processConfig).parametersUuid();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail fast for unsupported process types.

Only SECURITY_ANALYSIS is handled; other types leave contingencies/parametersUuid null but still schedule security analysis and call the SA server. Add a default branch that marks the execution failed and returns.

✅ Add an explicit default branch
             List<String> contingencies = null;
             UUID parametersUuid = null;
             switch (processConfig.processType()) {
                 case SECURITY_ANALYSIS -> {
-                    contingencies = ((SecurityAnalysisConfig) processConfig).contingencies();
-                    parametersUuid = ((SecurityAnalysisConfig) processConfig).parametersUuid();
+                    SecurityAnalysisConfig saConfig = (SecurityAnalysisConfig) processConfig;
+                    contingencies = saConfig.contingencies();
+                    parametersUuid = saConfig.parametersUuid();
                 }
+                default -> {
+                    LOGGER.error("Unsupported process type {} for executionId: {}", processConfig.processType(), executionId);
+                    monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                    return;
+                }
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
List<String> contingencies = null;
UUID parametersUuid = null;
switch (processConfig.processType()) {
case SECURITY_ANALYSIS -> {
contingencies = ((SecurityAnalysisConfig) processConfig).contingencies();
parametersUuid = ((SecurityAnalysisConfig) processConfig).parametersUuid();
}
}
List<String> contingencies = null;
UUID parametersUuid = null;
switch (processConfig.processType()) {
case SECURITY_ANALYSIS -> {
SecurityAnalysisConfig saConfig = (SecurityAnalysisConfig) processConfig;
contingencies = saConfig.contingencies();
parametersUuid = saConfig.parametersUuid();
}
default -> {
LOGGER.error("Unsupported process type {} for executionId: {}", processConfig.processType(), executionId);
monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
return;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 76 - 83, The switch over processConfig.processType() only handles
SECURITY_ANALYSIS and leaves contingencies and parametersUuid null for other
types; add an explicit default branch in that switch (inside
ConsumerServiceUsingServers -> the block where contingencies and parametersUuid
are set) that marks the execution as failed and returns early instead of
continuing to schedule a security analysis, e.g. call the failure-path used
elsewhere in this class (or set the appropriate execution status/emit error and
return) so unsupported process types cannot proceed with null
contingencies/parametersUuid.

Comment on lines 85 to 101
processExecutionContexts.put(executionId, new ProcessExecutionContext(
applyModificationsStepId,
securityAnalysisStepId,
caseUuid,
contingencies,
parametersUuid
));

monitorService.updateExecutionStatus(executionId, ProcessStatus.RUNNING, null, Instant.now(), null);
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(applyModificationsStepId, "APPLY_MODIFICATIONS", 0, StepStatus.RUNNING, null, null, null, Instant.now(), null));
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(securityAnalysisStepId, "SECURITY_ANALYSIS", 1, StepStatus.SCHEDULED, null, null, null, null, null));

// call network-modification-server to apply modifications
networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard against duplicate run messages to avoid double modifications.

Message delivery is typically at-least-once; this code generates new step IDs and calls applyModifications on every delivery, which can duplicate steps and apply changes twice. Add a deduplication guard (and keep the external call idempotent by executionId).

🛡️ Proposed deduplication guard
-            processExecutionContexts.put(executionId, new ProcessExecutionContext(
-                applyModificationsStepId,
-                securityAnalysisStepId,
-                caseUuid,
-                contingencies,
-                parametersUuid
-            ));
+            ProcessExecutionContext newContext = new ProcessExecutionContext(
+                applyModificationsStepId,
+                securityAnalysisStepId,
+                caseUuid,
+                contingencies,
+                parametersUuid
+            );
+            ProcessExecutionContext existing = processExecutionContexts.putIfAbsent(executionId, newContext);
+            if (existing != null) {
+                LOGGER.warn("Duplicate run message ignored for executionId: {}", executionId);
+                return;
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
processExecutionContexts.put(executionId, new ProcessExecutionContext(
applyModificationsStepId,
securityAnalysisStepId,
caseUuid,
contingencies,
parametersUuid
));
monitorService.updateExecutionStatus(executionId, ProcessStatus.RUNNING, null, Instant.now(), null);
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(applyModificationsStepId, "APPLY_MODIFICATIONS", 0, StepStatus.RUNNING, null, null, null, Instant.now(), null));
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(securityAnalysisStepId, "SECURITY_ANALYSIS", 1, StepStatus.SCHEDULED, null, null, null, null, null));
// call network-modification-server to apply modifications
networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
};
ProcessExecutionContext newContext = new ProcessExecutionContext(
applyModificationsStepId,
securityAnalysisStepId,
caseUuid,
contingencies,
parametersUuid
);
ProcessExecutionContext existing = processExecutionContexts.putIfAbsent(executionId, newContext);
if (existing != null) {
LOGGER.warn("Duplicate run message ignored for executionId: {}", executionId);
return;
}
monitorService.updateExecutionStatus(executionId, ProcessStatus.RUNNING, null, Instant.now(), null);
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(applyModificationsStepId, "APPLY_MODIFICATIONS", 0, StepStatus.RUNNING, null, null, null, Instant.now(), null));
monitorService.updateStepStatus(executionId,
new ProcessExecutionStep(securityAnalysisStepId, "SECURITY_ANALYSIS", 1, StepStatus.SCHEDULED, null, null, null, null, null));
// call network-modification-server to apply modifications
networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 85 - 101, The code currently creates new ProcessExecutionContext
entries and always calls networkModificationRestService.applyModifications for
every incoming message, which can cause duplicate modifications; add a
deduplication guard that checks whether this executionId already exists or is
past the start state before creating a new ProcessExecutionContext and updating
step statuses (e.g., check processExecutionContexts.containsKey(executionId) or
consult monitorService for an existing ProcessStatus for executionId and return
early if already RUNNING/COMPLETED), and ensure the external call
networkModificationRestService.applyModifications is invoked only when the guard
allows (also verify applyModifications treats executionId idempotently on the
network-modification-server side).

Comment on lines +129 to +134
if (stepStatus == StepStatus.COMPLETED) {
// call security-analysis-server to run security analysis
securityAnalysisRestService.runSecurityAnalysis(caseResultUuid, executionId, processExecutionContext.contingencies(), processExecutionContext.parametersUuid());
} else {
processExecutionContexts.remove(executionId);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Update execution status when network modifications fail.

On non-COMPLETED statuses you skip security analysis and remove the context, but the execution remains RUNNING. Mark the execution as FAILED (or CANCELLED) and set the end time.

🧭 Propagate failure to the execution status
             if (stepStatus == StepStatus.COMPLETED) {
                 // call security-analysis-server to run security analysis
                 securityAnalysisRestService.runSecurityAnalysis(caseResultUuid, executionId, processExecutionContext.contingencies(), processExecutionContext.parametersUuid());
             } else {
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
                 processExecutionContexts.remove(executionId);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 129 - 134, In ConsumerServiceUsingServers, when handling
non-COMPLETED stepStatus (the else branch that currently only does
processExecutionContexts.remove(executionId)), update the corresponding
execution record to a terminal state (FAILED or CANCELLED) and set its end time
before removing the context; locate the execution by executionId (or via any
Execution/ExecutionRepository/ExecutionService used elsewhere in this class) and
call its update/persist method so the execution status and endTimestamp are
saved, then remove the processExecutionContexts entry and skip calling
securityAnalysisRestService.runSecurityAnalysis.

Comment on lines 148 to 149
StepStatus stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate the file and examine the current state around lines 148-149
find . -type f -name "ConsumerServiceUsingServers.java" -exec wc -l {} \;

Repository: gridsuite/monitor-core

Length of output: 171


🏁 Script executed:

# Check the file structure and content around the specified lines
find . -type f -name "ConsumerServiceUsingServers.java" -exec sed -n '140,160p' {} +

Repository: gridsuite/monitor-core

Length of output: 1332


🏁 Script executed:

# Find CaseResultInfos class definition
fd 'CaseResultInfos.java' -x cat -n {}

Repository: gridsuite/monitor-core

Length of output: 1386


🏁 Script executed:

# Search for all StepStatus.valueOf usages in the codebase
rg -n "StepStatus\.valueOf" -g '*.java' -B2 -A2

Repository: gridsuite/monitor-core

Length of output: 1740


🏁 Script executed:

# Find consumeNetworkModifications method
rg -n "consumeNetworkModifications" -g '*.java' -B2 -A10

Repository: gridsuite/monitor-core

Length of output: 2030


🏁 Script executed:

# Check StepStatus enum definition to see valid values
fd 'StepStatus.java' -x cat -n {}

Repository: gridsuite/monitor-core

Length of output: 663


🏁 Script executed:

# Check the method name for line 148
sed -n '130,150p' ./monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java

Repository: gridsuite/monitor-core

Length of output: 1133


🏁 Script executed:

# Search for any other StepStatus usages or related parsing
rg -n "StepStatus|getStatus" ./monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java -B1 -A1

Repository: gridsuite/monitor-core

Length of output: 2294


🏁 Script executed:

# Check if there's any validation or documentation about status values from external servers
rg -n "status|Status" ./monitor-server/src/main/java/org/gridsuite/monitor/commons/CaseResultInfos.java -B2 -A2

Repository: gridsuite/monitor-core

Length of output: 187


Harden status parsing from external servers in two locations.

StepStatus.valueOf will throw if the server sends an unexpected or case-mismatched status, causing the consumer to crash and potentially retry indefinitely. Unsafe parsing exists at:

  • Line 114 in consumeNetworkModifications()
  • Line 148 in consumeSecurityAnalysis()

Use safe parsing with error handling and fallback in both methods.

🛡️ Safer status parsing pattern
-            StepStatus stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
+            StepStatus stepStatus;
+            try {
+                stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
+            } catch (IllegalArgumentException ex) {
+                LOGGER.error("Unsupported step status '{}' for executionId: {}", caseResultInfos.getStatus(), executionId, ex);
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                processExecutionContexts.remove(executionId);
+                return;
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
StepStatus stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
StepStatus stepStatus;
try {
stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
} catch (IllegalArgumentException ex) {
LOGGER.error("Unsupported step status '{}' for executionId: {}", caseResultInfos.getStatus(), executionId, ex);
monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
processExecutionContexts.remove(executionId);
return;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 148 - 149, The call StepStatus.valueOf(caseResultInfos.getStatus())
can throw for unexpected server values; in both consumeNetworkModifications()
and consumeSecurityAnalysis() replace direct valueOf usage with safe parsing:
catch IllegalArgumentException/NullPointerException around the valueOf call (or
use StepStatus.fromString helper) and fall back to a default safe status (e.g.,
StepStatus.UNKNOWN or StepStatus.FAILED) while logging the raw status and
context; ensure the surrounding logic uses the fallback status so consumers
don't crash or loop indefinitely and add a unit-friendly message to logs
identifying the method (consumeNetworkModifications / consumeSecurityAnalysis)
and the offending status string.

Comment on lines +40 to +42
public void runSecurityAnalysis(UUID caseUuid, UUID executionId, List<String> contingencies, UUID parametersUuid) {
var uriComponentsBuilder = UriComponentsBuilder.fromPath(DELIMITER + SECURITY_ANALYSIS_SERVER_API_VERSION + DELIMITER + "/cases/{caseUuid}/run-and-save");
var path = uriComponentsBuilder
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "SecurityAnalysisRestService.java" -type f

Repository: gridsuite/monitor-core

Length of output: 167


🏁 Script executed:

cat -n ./monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java | head -100

Repository: gridsuite/monitor-core

Length of output: 2915


Remove the redundant leading slash in the path construction.

The current path concatenation produces /v1//cases/... due to the extra / before "cases". This double-slash can cause issues with some proxies or stricter routing rules.

Suggested fix
-        var uriComponentsBuilder = UriComponentsBuilder.fromPath(DELIMITER + SECURITY_ANALYSIS_SERVER_API_VERSION + DELIMITER + "/cases/{caseUuid}/run-and-save");
+        var uriComponentsBuilder = UriComponentsBuilder.fromPath(DELIMITER + SECURITY_ANALYSIS_SERVER_API_VERSION + DELIMITER + "cases/{caseUuid}/run-and-save");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/SecurityAnalysisRestService.java`
around lines 40 - 42, In runSecurityAnalysis, the UriComponentsBuilder.fromPath
call builds a path with a redundant slash causing "/v1//cases/…"; fix by
removing the extra "/" before "cases" so the path concatenation uses DELIMITER +
SECURITY_ANALYSIS_SERVER_API_VERSION + DELIMITER +
"cases/{caseUuid}/run-and-save" (or otherwise ensure only one DELIMITER is
inserted), updating the UriComponentsBuilder.fromPath invocation accordingly.

Comment on lines +14 to +21
publishRunSecurityAnalysisUsingServers-out-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}monitor.process.securityanalysis.using.servers.run
consumeRunUsingServers-in-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}monitor.process.securityanalysis.using.servers.run
consumeNetworkModifications-in-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}modifications.case.result
consumeSecurityAnalysis-in-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}sa.case.result
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "application\.(yaml|yml|properties)" | head -20

Repository: gridsuite/monitor-core

Length of output: 297


🏁 Script executed:

find . -path "*/monitor-server/src/main/resources/config/application.yaml" -o -path "*/application.yaml" | head -20

Repository: gridsuite/monitor-core

Length of output: 305


🏁 Script executed:

fd "application\.(yaml|yml)" monitor-server/

Repository: gridsuite/monitor-core

Length of output: 174


🏁 Script executed:

cat -n monitor-server/src/main/resources/config/application.yaml | head -50

Repository: gridsuite/monitor-core

Length of output: 1591


🏁 Script executed:

rg "consumer.*group|duplicate.*processing" monitor-server/ -i -A 2 -B 2

Repository: gridsuite/monitor-core

Length of output: 48


🏁 Script executed:

rg "spring.*cloud.*stream" . --type yaml --type yml -l

Repository: gridsuite/monitor-core

Length of output: 93


🏁 Script executed:

fd "pom.xml|build.gradle" monitor-server/ | xargs cat | grep -A 5 -B 5 "spring-cloud-stream"

Repository: gridsuite/monitor-core

Length of output: 1369


🏁 Script executed:

find . -name "docker-compose*.yml" -o -name "docker-compose*.yaml" | xargs cat 2>/dev/null | grep -A 5 "monitor-server"

Repository: gridsuite/monitor-core

Length of output: 48


🏁 Script executed:

rg "replicas|instances|scale" --type yaml -A 2 -B 2 | head -30

Repository: gridsuite/monitor-core

Length of output: 48


🏁 Script executed:

find . -path "*/src/main/java/*" -name "*Consume*" -o -name "*Consumer*" | head -10

Repository: gridsuite/monitor-core

Length of output: 563


🏁 Script executed:

find . -path "*/src/main/java/org/gridsuite/monitor/server/services/ConsumerService.java" -exec cat -n {} \; | head -100

Repository: gridsuite/monitor-core

Length of output: 4912


🏁 Script executed:

rg "consumeNetworkModifications|consumeSecurityAnalysis|consumeRunUsingServers" monitor-server/src/main/java -A 15 -B 2

Repository: gridsuite/monitor-core

Length of output: 8032


🏁 Script executed:

rg "idempotent|deduplicat|duplicate|lock|mutex" monitor-server/src/main/java -i -A 2

Repository: gridsuite/monitor-core

Length of output: 48


🏁 Script executed:

rg "processExecutionContexts" monitor-server/src/main/java -B 5 -A 5 | head -80

Repository: gridsuite/monitor-core

Length of output: 9479


Add consumer groups to prevent duplicate process execution across multiple instances.

Spring Cloud Stream creates a broadcast queue (anonymous group) when no consumer group is configured. With multiple monitor-server instances, all instances will independently receive and process consumeRunUsingServers, consumeNetworkModifications, and consumeSecurityAnalysis messages, causing duplicate APPLY_MODIFICATIONS and SECURITY_ANALYSIS operations. The in-memory processExecutionContexts map cannot synchronize state across instances. Configure explicit consumer groups to enable competing consumer pattern, so only one instance in the group processes each message.

Note: consumeMonitorUpdate-in-0 already has group: monitorUpdateGroup, confirming this pattern is established in your configuration.

💡 Suggested configuration
        consumeRunUsingServers-in-0:
          destination: ${powsybl-ws.rabbitmq.destination.prefix:}monitor.process.securityanalysis.using.servers.run
+         group: monitorRunUsingServersGroup
        consumeNetworkModifications-in-0:
          destination: ${powsybl-ws.rabbitmq.destination.prefix:}modifications.case.result
+         group: monitorNetworkModificationsGroup
        consumeSecurityAnalysis-in-0:
          destination: ${powsybl-ws.rabbitmq.destination.prefix:}sa.case.result
+         group: monitorSecurityAnalysisGroup
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monitor-server/src/main/resources/config/application.yaml` around lines 14 -
21, The three consumer bindings consumeRunUsingServers-in-0,
consumeNetworkModifications-in-0, and consumeSecurityAnalysis-in-0 should be
given explicit consumer group names to enable competing-consumer behavior;
update each binding in application.yaml to add a unique group property (for
example runUsingServersGroup, networkModificationsGroup, securityAnalysisGroup)
so only one monitor-server instance in the group processes each message and
prevents duplicate APPLY_MODIFICATIONS/SECURITY_ANALYSIS executions.

Signed-off-by: Franck LECUYER <franck.lecuyer@rte-france.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java (1)

159-196: ⚠️ Potential issue | 🟡 Minor

Add resultCaseId assertions to all three step-status update test methods.

The three test methods—updateStepStatusShouldAddNewStep, updateStepStatusShouldUpdateExistingStep, and updateStepsStatusesShouldUpdateExistingSteps—do not set or assert resultCaseId on the DTO or resulting entity. Although MonitorService.updateStepStatus correctly copies resultCaseId from DTO to entity, this persistence path is not verified by tests.

Add a resultCaseId UUID to each test's ProcessExecutionStep builder and assert addedStep.getResultCaseId() / updatedStep.getResultCaseId() respectively.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java`
around lines 159 - 196, Add a UUID for resultCaseId to each ProcessExecutionStep
built in the three tests (updateStepStatusShouldAddNewStep,
updateStepStatusShouldUpdateExistingStep,
updateStepsStatusesShouldUpdateExistingSteps) and include assertions that the
resulting entity's getResultCaseId() equals that UUID after
MonitorService.updateStepStatus/updateStepsStatuses runs; locate the
ProcessExecutionStep builder usages in each test, set
.resultCaseId(resultCaseId) on the DTO, and add
assertThat(addedStep.getResultCaseId()).isEqualTo(resultCaseId) or
assertThat(updatedStep.getResultCaseId()).isEqualTo(resultCaseId) as
appropriate, keeping the existing verify(executionRepository).save(execution)
checks.
♻️ Duplicate comments (4)
monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java (4)

115-115: ⚠️ Potential issue | 🟠 Major

Harden enum parsing for external status payloads.

Lines 115 and 149 use StepStatus.valueOf(...) directly. Unexpected/null/case-mismatched status from external servers will throw and can cause repeated consumer failure loops.

🛡️ Proposed fix
-            StepStatus stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
+            StepStatus stepStatus = parseStepStatus(caseResultInfos.getStatus(), executionId, "consumeNetworkModifications");
+            if (stepStatus == null) {
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                processExecutionContexts.remove(executionId);
+                return;
+            }
...
-            StepStatus stepStatus = StepStatus.valueOf(caseResultInfos.getStatus());
+            StepStatus stepStatus = parseStepStatus(caseResultInfos.getStatus(), executionId, "consumeSecurityAnalysis");
+            if (stepStatus == null) {
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                processExecutionContexts.remove(executionId);
+                return;
+            }
// Add helper in the same class (outside bean methods)
private StepStatus parseStepStatus(String rawStatus, UUID executionId, String consumerName) {
    if (rawStatus == null) {
        LOGGER.error("{} received null status for executionId: {}", consumerName, executionId);
        return null;
    }
    try {
        return StepStatus.valueOf(rawStatus);
    } catch (IllegalArgumentException ex) {
        LOGGER.error("{} received unsupported status '{}' for executionId: {}", consumerName, rawStatus, executionId, ex);
        return null;
    }
}
#!/bin/bash
# Verify enum parsing risk and call sites.
# Expected: StepStatus enum constants are strict names; direct valueOf calls exist on raw external status strings.

fd 'StepStatus.java' -x sed -n '1,220p' {}
fd 'CaseResultInfos.java' -x sed -n '1,220p' {}
rg -n -C2 'StepStatus\.valueOf\(caseResultInfos\.getStatus\(\)\)' --type=java

Also applies to: 149-149

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
at line 115, The code calls StepStatus.valueOf(...) directly (e.g.,
StepStatus.valueOf(caseResultInfos.getStatus())) which will throw on
null/unknown/case-mismatched external values; add a private helper method
parseStepStatus(String rawStatus, UUID executionId, String consumerName) in
ConsumerServiceUsingServers that returns null on null/unknown values and logs a
descriptive error (including rawStatus, executionId and consumerName) instead of
throwing, then replace direct valueOf(...) calls with
parseStepStatus(caseResultInfos.getStatus(), executionId, consumerName) at the
existing call sites (the occurrences of
StepStatus.valueOf(caseResultInfos.getStatus()) around the consumer processing
logic).

78-83: ⚠️ Potential issue | 🟠 Major

Fail fast for unsupported process types and unsafe config cast.

Line 78 only handles SECURITY_ANALYSIS; other types continue with null contingencies/parametersUuid. Also, Lines 80-81 perform an unchecked cast. Add a guarded branch and return early on unsupported or mismatched config types.

✅ Proposed fix
             List<String> contingencies = null;
             UUID parametersUuid = null;
             switch (processConfig.processType()) {
                 case SECURITY_ANALYSIS -> {
-                    contingencies = ((SecurityAnalysisConfig) processConfig).contingencies();
-                    parametersUuid = ((SecurityAnalysisConfig) processConfig).parametersUuid();
+                    if (!(processConfig instanceof SecurityAnalysisConfig saConfig)) {
+                        LOGGER.error("Invalid config type {} for process type {} and executionId: {}",
+                            processConfig.getClass().getName(), processConfig.processType(), executionId);
+                        monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                        return;
+                    }
+                    contingencies = saConfig.contingencies();
+                    parametersUuid = saConfig.parametersUuid();
                 }
+                default -> {
+                    LOGGER.error("Unsupported process type {} for executionId: {}", processConfig.processType(), executionId);
+                    monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                    return;
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 78 - 83, The switch in ConsumerServiceUsingServers.java that sets
contingencies and parametersUuid only handles SECURITY_ANALYSIS and performs an
unchecked cast; update the switch in the method containing
processConfig.processType() so that the SECURITY_ANALYSIS branch first checks
that processConfig is an instance of SecurityAnalysisConfig before casting
(e.g., if (!(processConfig instanceof SecurityAnalysisConfig)) throw new
IllegalArgumentException(...)); assign contingencies and parametersUuid from the
safely-cast SecurityAnalysisConfig, and add a default branch that fails fast
(throw IllegalArgumentException or a DomainSpecificException) for unsupported
process types to avoid null values and unsafe casts.

85-101: ⚠️ Potential issue | 🔴 Critical

Add deduplication guard before scheduling external work.

Lines 85-101 accept duplicate run messages for the same executionId, create new step IDs, and call applyModifications again. This can trigger duplicate side effects.

🛡️ Proposed fix
-            processExecutionContexts.put(executionId, new ProcessExecutionContext(
+            ProcessExecutionContext newContext = new ProcessExecutionContext(
                 applyModificationsStepId,
                 securityAnalysisStepId,
                 caseUuid,
                 contingencies,
                 parametersUuid
-            ));
+            );
+            ProcessExecutionContext existing = processExecutionContexts.putIfAbsent(executionId, newContext);
+            if (existing != null) {
+                LOGGER.warn("Duplicate run message ignored for executionId: {}", executionId);
+                return;
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 85 - 101, The code currently accepts duplicate run messages and may
schedule external work twice; before creating a new ProcessExecutionContext and
calling networkModificationRestService.applyModifications, add a deduplication
guard that checks the existing processExecutionContexts for executionId (e.g.,
use processExecutionContexts.putIfAbsent(executionId, new
ProcessExecutionContext(...)) or check containsKey and return early), and only
proceed with monitorService.updateExecutionStatus, updateStepStatus calls, and
networkModificationRestService.applyModifications(caseUuid, executionId,
modificationUuids) when the context was actually inserted (i.e., this is the
first run for that executionId) to prevent duplicate side effects.

133-135: ⚠️ Potential issue | 🟠 Major

Propagate network-modification failure to execution status.

Line 134 removes context for non-COMPLETED network-modification outcomes, but execution status is not transitioned to a terminal failed state.

🧭 Proposed fix
             if (stepStatus == StepStatus.COMPLETED) {
                 // call security-analysis-server to run security analysis
                 securityAnalysisRestService.runSecurityAnalysis(caseResultUuid, executionId, processExecutionContext.contingencies(), processExecutionContext.parametersUuid());
             } else {
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
                 processExecutionContexts.remove(executionId);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 133 - 135, The current branch in ConsumerServiceUsingServers that
calls processExecutionContexts.remove(executionId) for non-COMPLETED
network-modification outcomes discards the context without marking the execution
as failed; update this branch so that before removing the context you transition
the execution's status to a terminal failed state (e.g., set the
Execution/ExecutionContext status to FAILED or invoke the existing
failure-handling API), include the network-modification error/reason when
setting the status, and then remove the context; reference the
network-modification outcome handling block and replace the bare
processExecutionContexts.remove(executionId) with a call that updates the
execution status (or calls markExecutionFailed/handleExecutionFailure if such
helper exists) then removes the context.
🧹 Nitpick comments (4)
monitor-server/src/main/resources/db/changelog/changesets/changelog_20260225T073450Z.xml (1)

3-7: Consider adding an explicit <rollback> block.

Without a <rollback> tag, Liquibase's auto-rollback for addColumn is database-dependent and not always reliable. An explicit rollback makes the migration fully reversible:

♻️ Proposed rollback addition
     <changeSet author="lecuyerfra (generated)" id="1772004899864-1">
         <addColumn tableName="process_execution_step">
             <column name="result_case_id" type="uuid"/>
         </addColumn>
+        <rollback>
+            <dropColumn tableName="process_execution_step" columnName="result_case_id"/>
+        </rollback>
     </changeSet>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/resources/db/changelog/changesets/changelog_20260225T073450Z.xml`
around lines 3 - 7, Add an explicit <rollback> block to the changeSet id
"1772004899864-1" that drops the added column to ensure deterministic
reversibility: in the changeSet that adds column "result_case_id" to table
"process_execution_step", include a rollback that executes a dropColumn (or
equivalent SQL) for "result_case_id" so Liquibase will reliably revert the
migration across databases.
monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java (1)

66-66: Consider using the builder instead of the 10-arg positional constructor.

Six trailing null arguments is error-prone; adding any new field to ProcessExecutionStep shifts the argument positions silently. The @Builder is already available.

♻️ Proposed refactor
-    .mapToObj(i -> new ProcessExecutionStep(steps.get(i).getId(), steps.get(i).getType().getName(), i, StepStatus.SCHEDULED, null, null, null, null, null, null))
+    .mapToObj(i -> ProcessExecutionStep.builder()
+        .id(steps.get(i).getId())
+        .stepType(steps.get(i).getType().getName())
+        .stepOrder(i)
+        .status(StepStatus.SCHEDULED)
+        .build())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java`
at line 66, Replace the positional 10-arg constructor call in the mapping lambda
with the ProcessExecutionStep.builder() fluent API: set id, typeName, index and
status via .id(...).typeName(...).index(...).status(StepStatus.SCHEDULED) and
omit or explicitly set the remaining optional fields via their builder methods
as needed, then call .build(); update the lambda that currently creates new
ProcessExecutionStep(steps.get(i).getId(), steps.get(i).getType().getName(), i,
StepStatus.SCHEDULED, null, null, null, null, null, null) to use the builder to
avoid the fragile multi-null argument list.
monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java (1)

161-163: Consider using the builder instead of the 10-arg positional constructor.

Same concern as in ProcessExecutionService and MonitorServiceTest: six positional null arguments create brittle call sites.

♻️ Proposed refactor
-        ProcessExecutionStep processExecutionStep1 = new ProcessExecutionStep(UUID.randomUUID(), "loadNetwork", 0, StepStatus.RUNNING, null, null, UUID.randomUUID(), Instant.now(), null, null);
-        ProcessExecutionStep processExecutionStep2 = new ProcessExecutionStep(UUID.randomUUID(), "applyModifs", 1, StepStatus.SCHEDULED, null, null, UUID.randomUUID(), null, null, null);
-        ProcessExecutionStep processExecutionStep3 = new ProcessExecutionStep(UUID.randomUUID(), "runSA", 2, StepStatus.SCHEDULED, null, null, UUID.randomUUID(), null, null, null);
+        ProcessExecutionStep processExecutionStep1 = ProcessExecutionStep.builder()
+            .id(UUID.randomUUID()).stepType("loadNetwork").stepOrder(0).status(StepStatus.RUNNING)
+            .reportId(UUID.randomUUID()).startedAt(Instant.now()).build();
+        ProcessExecutionStep processExecutionStep2 = ProcessExecutionStep.builder()
+            .id(UUID.randomUUID()).stepType("applyModifs").stepOrder(1).status(StepStatus.SCHEDULED)
+            .reportId(UUID.randomUUID()).build();
+        ProcessExecutionStep processExecutionStep3 = ProcessExecutionStep.builder()
+            .id(UUID.randomUUID()).stepType("runSA").stepOrder(2).status(StepStatus.SCHEDULED)
+            .reportId(UUID.randomUUID()).build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java`
around lines 161 - 163, Replace the brittle 10-arg positional constructor calls
for ProcessExecutionStep with the builder API: use
ProcessExecutionStep.builder() and set named properties (id, name, order,
status, parentExecutionId, startedAt, etc.) instead of passing multiple nulls;
update the three test instances (processExecutionStep1, processExecutionStep2,
processExecutionStep3) to call
builder().id(...).name("...").order(...).status(StepStatus....).parentExecutionId(...).startedAt(...)/scheduledAt(...)
as appropriate and then build() to make the test resilient and readable.
monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java (1)

466-467: Prefer builder over the 10-arg positional constructor in tests.

Consistent with the builder usage elsewhere in this same test file, replacing positional constructors reduces the risk of silent argument misalignment on future field additions.

♻️ Proposed refactor
-        ProcessExecutionStep processExecutionStep1 = new ProcessExecutionStep(stepId1, "loadNetwork", 0, StepStatus.RUNNING, null, null, null, startedAt1, null, null);
-        ProcessExecutionStep processExecutionStep2 = new ProcessExecutionStep(stepId2, "applyModifs", 1, StepStatus.SCHEDULED, null, null, null, null, null, null);
+        ProcessExecutionStep processExecutionStep1 = ProcessExecutionStep.builder()
+            .id(stepId1).stepType("loadNetwork").stepOrder(0).status(StepStatus.RUNNING).startedAt(startedAt1).build();
+        ProcessExecutionStep processExecutionStep2 = ProcessExecutionStep.builder()
+            .id(stepId2).stepType("applyModifs").stepOrder(1).status(StepStatus.SCHEDULED).build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java`
around lines 466 - 467, Replace the two 10-argument positional constructors for
ProcessExecutionStep (processExecutionStep1 and processExecutionStep2) with the
class's builder to avoid positional-arg mistakes; locate the instantiations of
ProcessExecutionStep and use ProcessExecutionStep.builder() to set id, name,
order/index, status (e.g., StepStatus.RUNNING / StepStatus.SCHEDULED), startedAt
and any other nullable fields explicitly via builder methods, then call
build()—mirror the builder style used elsewhere in this test file to keep
construction consistent and explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`:
- Around line 99-100: Wrap external REST calls (notably
networkModificationRestService.applyModifications(caseUuid, executionId,
modificationUuids) and the other REST call in the same method) in try/catch to
prevent uncaught exceptions from leaving executions stuck in RUNNING; on failure
catch Exception, log the error with context
(caseUuid/executionId/modificationUuids), transition the execution to a terminal
failure state via the existing execution state updater (e.g.,
executionRepository or executionService method used elsewhere in this class),
persist that state change, and ensure any execution context cleanup/rollback
runs in a finally block so no context leaks remain.

---

Outside diff comments:
In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java`:
- Around line 159-196: Add a UUID for resultCaseId to each ProcessExecutionStep
built in the three tests (updateStepStatusShouldAddNewStep,
updateStepStatusShouldUpdateExistingStep,
updateStepsStatusesShouldUpdateExistingSteps) and include assertions that the
resulting entity's getResultCaseId() equals that UUID after
MonitorService.updateStepStatus/updateStepsStatuses runs; locate the
ProcessExecutionStep builder usages in each test, set
.resultCaseId(resultCaseId) on the DTO, and add
assertThat(addedStep.getResultCaseId()).isEqualTo(resultCaseId) or
assertThat(updatedStep.getResultCaseId()).isEqualTo(resultCaseId) as
appropriate, keeping the existing verify(executionRepository).save(execution)
checks.

---

Duplicate comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`:
- Line 115: The code calls StepStatus.valueOf(...) directly (e.g.,
StepStatus.valueOf(caseResultInfos.getStatus())) which will throw on
null/unknown/case-mismatched external values; add a private helper method
parseStepStatus(String rawStatus, UUID executionId, String consumerName) in
ConsumerServiceUsingServers that returns null on null/unknown values and logs a
descriptive error (including rawStatus, executionId and consumerName) instead of
throwing, then replace direct valueOf(...) calls with
parseStepStatus(caseResultInfos.getStatus(), executionId, consumerName) at the
existing call sites (the occurrences of
StepStatus.valueOf(caseResultInfos.getStatus()) around the consumer processing
logic).
- Around line 78-83: The switch in ConsumerServiceUsingServers.java that sets
contingencies and parametersUuid only handles SECURITY_ANALYSIS and performs an
unchecked cast; update the switch in the method containing
processConfig.processType() so that the SECURITY_ANALYSIS branch first checks
that processConfig is an instance of SecurityAnalysisConfig before casting
(e.g., if (!(processConfig instanceof SecurityAnalysisConfig)) throw new
IllegalArgumentException(...)); assign contingencies and parametersUuid from the
safely-cast SecurityAnalysisConfig, and add a default branch that fails fast
(throw IllegalArgumentException or a DomainSpecificException) for unsupported
process types to avoid null values and unsafe casts.
- Around line 85-101: The code currently accepts duplicate run messages and may
schedule external work twice; before creating a new ProcessExecutionContext and
calling networkModificationRestService.applyModifications, add a deduplication
guard that checks the existing processExecutionContexts for executionId (e.g.,
use processExecutionContexts.putIfAbsent(executionId, new
ProcessExecutionContext(...)) or check containsKey and return early), and only
proceed with monitorService.updateExecutionStatus, updateStepStatus calls, and
networkModificationRestService.applyModifications(caseUuid, executionId,
modificationUuids) when the context was actually inserted (i.e., this is the
first run for that executionId) to prevent duplicate side effects.
- Around line 133-135: The current branch in ConsumerServiceUsingServers that
calls processExecutionContexts.remove(executionId) for non-COMPLETED
network-modification outcomes discards the context without marking the execution
as failed; update this branch so that before removing the context you transition
the execution's status to a terminal failed state (e.g., set the
Execution/ExecutionContext status to FAILED or invoke the existing
failure-handling API), include the network-modification error/reason when
setting the status, and then remove the context; reference the
network-modification outcome handling block and replace the bare
processExecutionContexts.remove(executionId) with a call that updates the
execution status (or calls markExecutionFailed/handleExecutionFailure if such
helper exists) then removes the context.

---

Nitpick comments:
In
`@monitor-server/src/main/resources/db/changelog/changesets/changelog_20260225T073450Z.xml`:
- Around line 3-7: Add an explicit <rollback> block to the changeSet id
"1772004899864-1" that drops the added column to ensure deterministic
reversibility: in the changeSet that adds column "result_case_id" to table
"process_execution_step", include a rollback that executes a dropColumn (or
equivalent SQL) for "result_case_id" so Liquibase will reliably revert the
migration across databases.

In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java`:
- Around line 161-163: Replace the brittle 10-arg positional constructor calls
for ProcessExecutionStep with the builder API: use
ProcessExecutionStep.builder() and set named properties (id, name, order,
status, parentExecutionId, startedAt, etc.) instead of passing multiple nulls;
update the three test instances (processExecutionStep1, processExecutionStep2,
processExecutionStep3) to call
builder().id(...).name("...").order(...).status(StepStatus....).parentExecutionId(...).startedAt(...)/scheduledAt(...)
as appropriate and then build() to make the test resilient and readable.

In
`@monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java`:
- Around line 466-467: Replace the two 10-argument positional constructors for
ProcessExecutionStep (processExecutionStep1 and processExecutionStep2) with the
class's builder to avoid positional-arg mistakes; locate the instantiations of
ProcessExecutionStep and use ProcessExecutionStep.builder() to set id, name,
order/index, status (e.g., StepStatus.RUNNING / StepStatus.SCHEDULED), startedAt
and any other nullable fields explicitly via builder methods, then call
build()—mirror the builder style used elsewhere in this test file to keep
construction consistent and explicit.

In
`@monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java`:
- Line 66: Replace the positional 10-arg constructor call in the mapping lambda
with the ProcessExecutionStep.builder() fluent API: set id, typeName, index and
status via .id(...).typeName(...).index(...).status(StepStatus.SCHEDULED) and
omit or explicitly set the remaining optional fields via their builder methods
as needed, then call .build(); update the lambda that currently creates new
ProcessExecutionStep(steps.get(i).getId(), steps.get(i).getType().getName(), i,
StepStatus.SCHEDULED, null, null, null, null, null, null) to use the builder to
avoid the fragile multi-null argument list.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0866ac and 24999ae.

📒 Files selected for processing (11)
  • monitor-commons/src/main/java/org/gridsuite/monitor/commons/ProcessExecutionStep.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/entities/ProcessExecutionStepEntity.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/mapper/ProcessExecutionStepMapper.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java
  • monitor-server/src/main/resources/db/changelog/changesets/changelog_20260225T073450Z.xml
  • monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml
  • monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java
  • monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java
  • monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java
  • monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/StepExecutionService.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java

Comment on lines +99 to +100
// call network-modification-server to apply modifications
networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle REST client failures to avoid stuck RUNNING executions.

Lines 99-100 and 132 call external services without exception handling. If either call throws, execution state can remain inconsistent and context may leak.

🧯 Proposed fix
-            networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
+            try {
+                networkModificationRestService.applyModifications(caseUuid, executionId, modificationUuids);
+            } catch (RuntimeException ex) {
+                LOGGER.error("applyModifications failed for executionId: {}", executionId, ex);
+                monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                processExecutionContexts.remove(executionId);
+            }
...
             if (stepStatus == StepStatus.COMPLETED) {
                 // call security-analysis-server to run security analysis
-                securityAnalysisRestService.runSecurityAnalysis(caseResultUuid, executionId, processExecutionContext.contingencies(), processExecutionContext.parametersUuid());
+                try {
+                    securityAnalysisRestService.runSecurityAnalysis(caseResultUuid, executionId, processExecutionContext.contingencies(), processExecutionContext.parametersUuid());
+                } catch (RuntimeException ex) {
+                    LOGGER.error("runSecurityAnalysis failed for executionId: {}", executionId, ex);
+                    monitorService.updateExecutionStatus(executionId, ProcessStatus.FAILED, null, null, Instant.now());
+                    processExecutionContexts.remove(executionId);
+                }
             } else {
                 processExecutionContexts.remove(executionId);
             }

Also applies to: 130-133

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/ConsumerServiceUsingServers.java`
around lines 99 - 100, Wrap external REST calls (notably
networkModificationRestService.applyModifications(caseUuid, executionId,
modificationUuids) and the other REST call in the same method) in try/catch to
prevent uncaught exceptions from leaving executions stuck in RUNNING; on failure
catch Exception, log the error with context
(caseUuid/executionId/modificationUuids), transition the execution to a terminal
failure state via the existing execution state updater (e.g.,
executionRepository or executionService method used elsewhere in this class),
persist that state change, and ensure any execution context cleanup/rollback
runs in a finally block so no context leaks remain.

…rs_to_run_cyclic_chain

Signed-off-by: Franck LECUYER <franck.lecuyer@rte-france.com>

# Conflicts:
#	monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java
#	monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java (1)

67-67: Consider a builder or named-constructor to replace the 6 trailing null arguments.

With 10 positional parameters (6 of which are null), the call site is opaque — it's impossible to tell which null maps to which field without consulting the constructor signature. It also makes it easy to silently swap two UUID-typed nullable fields in the future.

If ProcessExecutionStep is a Java record (as is typical for a DTO), a small static factory or an overloaded scheduled(...) factory method on the record itself would make this readable:

♻️ Example: static factory on the record
-    .mapToObj(i -> new ProcessExecutionStep(steps.get(i).getId(), steps.get(i).getType().getName(), i, StepStatus.SCHEDULED, null, null, null, null, null, null))
+    .mapToObj(i -> ProcessExecutionStep.scheduled(steps.get(i).getId(), steps.get(i).getType().getName(), i))

In ProcessExecutionStep (commons module):

// static factory — documents intent and hides the null-filled fields
public static ProcessExecutionStep scheduled(UUID id, String typeName, int order) {
    return new ProcessExecutionStep(id, typeName, order, StepStatus.SCHEDULED,
        null, null, null, null, null, null);
}

Alternatively, if a builder is available or can be added (e.g., via Lombok @Builder), it would achieve the same goal.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java`
at line 67, Replace the opaque constructor call that passes 6 trailing nulls in
the mapToObj lambda with a clear factory or builder: add a static factory method
on ProcessExecutionStep (e.g., scheduled(UUID id, String typeName, int order) or
similar named-constructor) that returns the record/instance with
StepStatus.SCHEDULED and the nullable fields set to null, or alternatively add a
builder (Lombok `@Builder` or manual) and use it here; then update the lambda
(.mapToObj(...)) to call ProcessExecutionStep.scheduled(steps.get(i).getId(),
steps.get(i).getType().getName(), i) (or the builder variant) so the call site
no longer passes six positional nulls.
monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java (1)

33-33: Two adjacent String parameters (bindingName, debugFileLocation) are silently swappable.

Java provides no compile-time protection against swapping bindingName and debugFileLocation at call sites. A misorder would route messages to a wrong/nonexistent binding or send the binding name as a file location without any error.

Consider wrapping them in distinct types or a small value object, or at minimum ensure call sites in MonitorService have a test asserting the correct binding name is used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java`
at line 33, The method sendProcessRunMessage(UUID caseUuid, ProcessConfig
processConfig, UUID executionId, String bindingName, String debugFileLocation)
accepts two adjacent Strings that can be silently swapped; change the API to use
distinct types (e.g., introduce a BindingName value class and a
DebugFileLocation value class, or a small ProcessRunDestination/BindingInfo DTO
containing explicit fields) and update all call sites (notably in
MonitorService) to pass the new typed objects instead of raw Strings; also add a
unit test in MonitorService verifying the binding name is passed through
correctly to sendProcessRunMessage to prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java`:
- Around line 33-36: The MonitorService call to
NotificationService.sendProcessRunMessage passes the two String arguments in the
wrong order; update the call site in MonitorService where
notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), null, bindingName) is invoked to either swap the last two
args to notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), bindingName, null) so bindingName is used for publisher.send,
or replace the call with the 4-parameter overload
(notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), bindingName)) to centralize routing logic; ensure you modify
the call that references execution.getId() and bindingName accordingly.

---

Nitpick comments:
In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java`:
- Line 33: The method sendProcessRunMessage(UUID caseUuid, ProcessConfig
processConfig, UUID executionId, String bindingName, String debugFileLocation)
accepts two adjacent Strings that can be silently swapped; change the API to use
distinct types (e.g., introduce a BindingName value class and a
DebugFileLocation value class, or a small ProcessRunDestination/BindingInfo DTO
containing explicit fields) and update all call sites (notably in
MonitorService) to pass the new typed objects instead of raw Strings; also add a
unit test in MonitorService verifying the binding name is passed through
correctly to sendProcessRunMessage to prevent regressions.

In
`@monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java`:
- Line 67: Replace the opaque constructor call that passes 6 trailing nulls in
the mapToObj lambda with a clear factory or builder: add a static factory method
on ProcessExecutionStep (e.g., scheduled(UUID id, String typeName, int order) or
similar named-constructor) that returns the record/instance with
StepStatus.SCHEDULED and the nullable fields set to null, or alternatively add a
builder (Lombok `@Builder` or manual) and use it here; then update the lambda
(.mapToObj(...)) to call ProcessExecutionStep.scheduled(steps.get(i).getId(),
steps.get(i).getType().getName(), i) (or the builder variant) so the call site
no longer passes six positional nulls.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24999ae and 4bd069d.

📒 Files selected for processing (8)
  • monitor-server/src/main/java/org/gridsuite/monitor/server/controllers/MonitorController.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java
  • monitor-server/src/main/resources/application-local.yaml
  • monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml
  • monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java
  • monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java
  • monitor-worker-server/src/main/java/org/gridsuite/monitor/worker/server/services/ProcessExecutionService.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java
  • monitor-server/src/main/resources/db/changelog/db.changelog-master.yaml
  • monitor-server/src/test/java/org/gridsuite/monitor/server/controllers/MonitorControllerTest.java
  • monitor-server/src/main/resources/application-local.yaml
  • monitor-server/src/test/java/org/gridsuite/monitor/server/services/MonitorServiceTest.java

Comment on lines +33 to 36
public void sendProcessRunMessage(UUID caseUuid, ProcessConfig processConfig, UUID executionId, String bindingName, String debugFileLocation) {
ProcessRunMessage<?> message = new ProcessRunMessage<>(executionId, caseUuid, processConfig, debugFileLocation);
publisher.send(bindingName, message);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of the 5-arg sendProcessRunMessage overload
rg -n --type java 'sendProcessRunMessage\s*\(\s*[^,]+\s*,\s*[^,]+\s*,\s*[^,]+\s*,\s*[^,]+\s*,\s*[^,]+\s*\)' -A 2 -B 2

Repository: gridsuite/monitor-core

Length of output: 2037


🏁 Script executed:

#!/bin/bash
# Get directory structure to understand package layout
git ls-files "monitor-server/src/main/java/org/gridsuite/monitor/server/**" | grep -E "(NotificationService|MonitorService)" | head -20

Repository: gridsuite/monitor-core

Length of output: 244


🏁 Script executed:

#!/bin/bash
# Read the full NotificationService.java to understand the context
wc -l monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java

Repository: gridsuite/monitor-core

Length of output: 160


🏁 Script executed:

#!/bin/bash
# Read NotificationService.java around the method definition
cat -n monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java | head -37

Repository: gridsuite/monitor-core

Length of output: 1817


🏁 Script executed:

#!/bin/bash
# Read MonitorService.java around the call site to see full context
sed -n '255,270p' monitor-server/src/main/java/org/gridsuite/monitor/server/services/MonitorService.java | cat -n

Repository: gridsuite/monitor-core

Length of output: 680


🏁 Script executed:

#!/bin/bash
# Check if there are other call sites to this method
rg 'sendProcessRunMessage' monitor-server/src/main/java/org/gridsuite/monitor/server/services/ -B 1 -A 1

Repository: gridsuite/monitor-core

Length of output: 2266


Fix parameter order mismatch in MonitorService call to the 5-parameter overload.

The call at MonitorService.java:264 passes arguments in the wrong positions for the two adjacent String parameters:

notificationService.sendProcessRunMessage(caseUuid, processConfig, execution.getId(), null, bindingName);

But the method signature expects:

public void sendProcessRunMessage(..., String bindingName, String debugFileLocation)

This swaps the parameters: null is passed to bindingName (causing publisher.send(null, ...)) and bindingName is passed to debugFileLocation. Correct the argument order:

Fix parameter order
-notificationService.sendProcessRunMessage(caseUuid, processConfig, execution.getId(), null, bindingName);
+notificationService.sendProcessRunMessage(caseUuid, processConfig, execution.getId(), bindingName, null);

Alternatively, to avoid this fragility in the future, consider whether this call should use the 4-parameter overload instead and let the routing logic remain centralized in NotificationService.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@monitor-server/src/main/java/org/gridsuite/monitor/server/services/NotificationService.java`
around lines 33 - 36, The MonitorService call to
NotificationService.sendProcessRunMessage passes the two String arguments in the
wrong order; update the call site in MonitorService where
notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), null, bindingName) is invoked to either swap the last two
args to notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), bindingName, null) so bindingName is used for publisher.send,
or replace the call with the 4-parameter overload
(notificationService.sendProcessRunMessage(caseUuid, processConfig,
execution.getId(), bindingName)) to centralize routing logic; ensure you modify
the call that references execution.getId() and bindingName accordingly.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
20.8% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant