Skip to content

[WIP] Get computation statuses for branch#986

Open
sBouzols wants to merge 4 commits into
mainfrom
get_computation_statuses_for_branch
Open

[WIP] Get computation statuses for branch#986
sBouzols wants to merge 4 commits into
mainfrom
get_computation_statuses_for_branch

Conversation

@sBouzols
Copy link
Copy Markdown
Contributor

PR Summary

Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
@sBouzols sBouzols requested a review from SlimaneAmar April 30, 2026 14:55
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors computation services to use batch status retrieval APIs. Services now support fetching status for multiple result UUIDs in single calls, replacing per-UUID GET requests with bulk POST operations. Status return types change from String to typed enums. Controllers return enum names. Tests updated to mock batch assertion methods.

Changes

Batch Status Retrieval Pattern Refactoring

Layer / File(s) Summary
Status Type System & Repository Foundation
src/main/java/org/gridsuite/study/server/dto/ShortCircuitAnalysisStatus.java, src/main/java/org/gridsuite/study/server/repository/rootnetwork/RootNetworkNodeInfoRepository.java
ShortCircuitStatus enum renamed to ShortCircuitAnalysisStatus. RootNetworkNodeInfoRepository adds batch query method findAllByNodeInfoIdInAndRootNetworkId(List<UUID>, UUID).
Core Service Batch Status Retrieval
src/main/java/org/gridsuite/study/server/service/LoadFlowService.java, PccMinService.java, SecurityAnalysisService.java, SensitivityAnalysisService.java, StateEstimationService.java, VoltageInitService.java, ShortCircuitService.java
All services adopt consistent pattern: imports updated to support ParameterizedTypeReference, CollectionUtils, and collection types (HashSet, Set). New public getXxxStatuses(List<UUID>) method POSTs UUID list to /results/statuses endpoint returning Map<UUID, XxxStatus>. Single-result getXxxStatus(UUID) delegates to batch. New assertNoXxxRunning(List<UUID>) methods check for RUNNING status and throw StudyException(COMPUTATION_RUNNING). Status getters return typed DTOs instead of String.
Dynamic Service Clients & Services Batch Support
src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java, dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java, dynamicsimulation/DynamicSimulationClient.java, .../impl/DynamicSimulationClientImpl.java, src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java, dynamicsecurityanalysis/DynamicSecurityAnalysisService.java, dynamicsimulation/DynamicSimulationService.java, .../impl/DynamicSimulationServiceImpl.java
Clients add getStatuses(List<UUID>) methods POSTing to remote endpoints and deserializing typed status maps. Services import collection types, delegate single-result calls to batch methods, and add assertion methods validating non-running status across multiple results.
Root Network Node Info Batch Coordination
src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java
Adds explicit import for ComputationsInvalidationMode. Introduces getRootNetworkNodeInfos(List<UUID>, UUID) and getComputationResultUuids(List<UUID>, UUID, ComputationType) for batch retrieval. Replaces assertComputationNotRunning(UUID, UUID) with assertComputationsNotRunning(List<UUID>, UUID) checking multiple computation types across multiple nodes. Updates status getter return types from String to typed DTOs (SensitivityAnalysisStatus, ShortCircuitAnalysisStatus, VoltageInitStatus, StateEstimationStatus, PccMinStatus).
Controller Endpoints & Service Wiring
src/main/java/org/gridsuite/study/server/controller/StudyController.java, src/main/java/org/gridsuite/study/server/service/StudyService.java
StudyController status endpoints import CompositeModificationsActionType and ModificationsActionType. Status endpoints for shortcircuit, voltage-init, sensitivity-analysis, state-estimation, and pcc-min return typed enum values via result.name() or status.name(). StudyService delegates computation-not-running checks to rootNetworkNodeInfoService.assertComputationsNotRunning(...) with full node list.
Test Mock Updates
src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java, studycontroller/NodeControllerTest.java
Mockito stubs updated in three test scenarios (assertForbiddenNodeInsertions, testNodeStashAndRestore, testNodeAliases) and one helper (cutAndPasteNode) to call assertComputationsNotRunning(any(), any()) instead of assertComputationNotRunning(any(), any()).

Suggested reviewers

  • Meklo
  • khouadrired
  • SlimaneAmar
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is completely empty with only placeholder text ('## PR Summary' and comment markers), providing no meaningful information about the changes or rationale. Provide a detailed description explaining the batch status refactoring, why it was needed, and the impact on existing APIs and their callers.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '[WIP] Get computation statuses for branch' is vague and uses non-descriptive '[WIP]' prefix. It lacks specificity about the actual changes, making it unclear to reviewers what the main change entails. Remove '[WIP]' prefix and replace with a clear, specific title that describes the main change, such as 'Batch status retrieval for computation results' or 'Refactor status APIs to support batch operations'.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.2)
src/main/java/org/gridsuite/study/server/controller/StudyController.java
src/main/java/org/gridsuite/study/server/service/StudyService.java

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

Comment thread src/main/java/org/gridsuite/study/server/service/LoadFlowService.java Outdated
}
}

public void assertLoadFlowsNotRunning(List<UUID> resultUuids) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove assertLoadFlowNotRunning

}
}

public void assertLoadFlowsNotRunning(List<UUID> resultUuids) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rename by assertNoLoadFlowRunning ?

Comment on lines +222 to +224
if (loadFlowStatuses != null && loadFlowStatuses.containsValue(LoadFlowStatus.RUNNING)) {
throw new StudyException(COMPUTATION_RUNNING);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Set<LoadFlowStatus> values = new HashSet<>(loadFlowStatuses.values()); if (loadFlowStatuses != null && values.contains(LoadFlowStatus.RUNNING)) { throw new StudyException(COMPUTATION_RUNNING); }

sBouzols and others added 2 commits May 11, 2026 15:08
…computation

Co-authored-by: Copilot <copilot@github.com>
Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
Copy link
Copy Markdown

@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: 18

♻️ Duplicate comments (1)
src/main/java/org/gridsuite/study/server/service/LoadFlowService.java (1)

155-156: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden batch status response against null body

Line 155 returns getBody() directly; if the load-flow service responds with an empty body, downstream calls (e.g., assertNoLoadFlowRunning) can throw NPE. Return an empty map fallback here to keep callers safe.

Proposed fix
-        return restTemplate.exchange(loadFlowServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, LoadFlowStatus>>() {
-        }).getBody();
+        Map<UUID, LoadFlowStatus> statuses = restTemplate.exchange(
+            loadFlowServerBaseUri + path,
+            HttpMethod.POST,
+            httpEntity,
+            new ParameterizedTypeReference<Map<UUID, LoadFlowStatus>>() {
+            }
+        ).getBody();
+        return statuses != null ? statuses : Map.of();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/study/server/service/LoadFlowService.java` around
lines 155 - 156, The call in LoadFlowService that returns
restTemplate.exchange(...).getBody() can return null; update the code in the
method that calls restTemplate.exchange (the POST to loadFlowServerBaseUri +
path in LoadFlowService) to guard against a null body by returning an empty
Map<UUID, LoadFlowStatus> when getBody() is null (e.g., assign the result to a
local variable and return result != null ? result : Collections.emptyMap()) so
downstream callers like assertNoLoadFlowRunning never see a null.
🧹 Nitpick comments (5)
src/main/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisService.java (1)

137-143: 💤 Low value

Consider avoiding unnecessary HashSet allocation.

Creating a HashSet from the map values is unnecessary. You can check for the presence of RUNNING status directly on the Collection<DynamicSecurityAnalysisStatus> returned by .values().

♻️ Proposed refactor
 public void assertNoDynamicSecurityAnalysisRunning(List<UUID> resultUuids) {
     Map<UUID, DynamicSecurityAnalysisStatus> dynamicSecurityAnalysisStatuses = getDynamicSecurityAnalysisStatuses(resultUuids);
-    Set<DynamicSecurityAnalysisStatus> values = new HashSet<>(dynamicSecurityAnalysisStatuses.values());
-    if (values.contains(DynamicSecurityAnalysisStatus.RUNNING)) {
+    if (dynamicSecurityAnalysisStatuses.values().contains(DynamicSecurityAnalysisStatus.RUNNING)) {
         throw new StudyException(COMPUTATION_RUNNING);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisService.java`
around lines 137 - 143, The method assertNoDynamicSecurityAnalysisRunning
creates an unnecessary HashSet from dynamicSecurityAnalysisStatuses.values();
instead check for RUNNING directly on the Collection returned by
getDynamicSecurityAnalysisStatuses(resultUuids) to avoid allocation—e.g. replace
the new HashSet<>(dynamicSecurityAnalysisStatuses.values()) usage with a direct
contains/stream anyMatch on dynamicSecurityAnalysisStatuses.values() and keep
throwing StudyException(COMPUTATION_RUNNING) when
DynamicSecurityAnalysisStatus.RUNNING is found.
src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java (1)

202-215: 💤 Low value

Inconsistent null-safety in empty list check.

Line 203 uses resultUuids.isEmpty() which will throw NPE if resultUuids is null, while other similar services (PccMinService, StateEstimationService, VoltageInitService, SensitivityAnalysisService) use CollectionUtils.isEmpty(resultUuids) for null-safe checking. Consider using CollectionUtils.isEmpty() for consistency and defensive coding.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java`
around lines 202 - 215, In ShortCircuitService.getShortCircuitAnalysisStatuses,
replace the null-unsafe resultUuids.isEmpty() check with a null-safe
CollectionUtils.isEmpty(resultUuids) to match other services (PccMinService,
StateEstimationService, VoltageInitService, SensitivityAnalysisService); ensure
you import org.springframework.util.CollectionUtils if not already present and
keep the early return Map.of() behavior when the collection is empty or null.
src/main/java/org/gridsuite/study/server/service/VoltageInitService.java (1)

13-13: 💤 Low value

Inconsistent CollectionUtils import.

This file imports org.apache.commons.collections4.CollectionUtils while other services in this PR (PccMinService, StateEstimationService, SensitivityAnalysisService) use org.springframework.util.CollectionUtils. Consider using Spring's CollectionUtils for consistency across the codebase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/study/server/service/VoltageInitService.java` at
line 13, VoltageInitService imports
org.apache.commons.collections4.CollectionUtils which is inconsistent with other
services; change the import to org.springframework.util.CollectionUtils and
update any calls if needed to match Spring's API (e.g.,
CollectionUtils.isEmpty/hasLength semantics) so usages in the VoltageInitService
class (look for references inside the VoltageInitService methods/constructor)
compile and follow the same convention as PccMinService, StateEstimationService,
and SensitivityAnalysisService.
src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java (1)

208-208: ⚡ Quick win

Use .build() instead of .buildAndExpand().

Since no path variables are being expanded, .build() is more appropriate than .buildAndExpand().

♻️ Suggested simplification
-    var uriComponents = UriComponentsBuilder.fromUriString(endPointUrl + "/statuses").buildAndExpand();
+    var uriComponents = UriComponentsBuilder.fromUriString(endPointUrl + "/statuses").build();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
at line 208, Replace the unnecessary call to buildAndExpand() with build() when
constructing the URI for statuses: in DynamicMarginCalculationClient where
uriComponents is assigned (var uriComponents =
UriComponentsBuilder.fromUriString(endPointUrl + "/statuses")...), use .build()
instead of .buildAndExpand() because there are no path variables to expand.
src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java (1)

135-135: ⚡ Quick win

Unnecessary HashSet creation.

Creating a HashSet from values() adds overhead. The contains check can be performed directly on Collection<DynamicMarginCalculationStatus> returned by .values().

♻️ Simplified implementation
 public void assertNoDynamicMarginCalculationRunning(List<UUID> resultUuids) {
     Map<UUID, DynamicMarginCalculationStatus> dynamicMarginCalculationStatuses = getDynamicMarginCalculationStatuses(resultUuids);
-    Set<DynamicMarginCalculationStatus> values = new HashSet<>(dynamicMarginCalculationStatuses.values());
-    if (values.contains(DynamicMarginCalculationStatus.RUNNING)) {
+    if (dynamicMarginCalculationStatuses.values().contains(DynamicMarginCalculationStatus.RUNNING)) {
         throw new StudyException(COMPUTATION_RUNNING);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java`
at line 135, Replace the unnecessary HashSet allocation in
DynamicMarginCalculationService: instead of creating
Set<DynamicMarginCalculationStatus> values = new
HashSet<>(dynamicMarginCalculationStatuses.values()), use the collection
returned by dynamicMarginCalculationStatuses.values() directly (e.g. assign to
Collection<DynamicMarginCalculationStatus> or call .values().contains(...)
inline) to perform the contains check without the extra copy/overhead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`:
- Around line 197-199: getStatus currently may return null when
getStatuses(List.of(resultUuid)) doesn't contain resultUuid; change getStatus to
return Optional<DynamicMarginCalculationStatus> and wrap the map lookup with
Optional.ofNullable(getStatuses(List.of(resultUuid)).get(resultUuid)) so callers
must handle missing status explicitly; update any callers of
DynamicMarginCalculationClient.getStatus to handle Optional (or alternatively
throw a clear checked/unchecked exception with resultUuid in the message if you
prefer fail-fast semantics).
- Around line 215-216: The REST call currently returns
getRestTemplate().exchange(...).getBody() which can be null and cause NPEs; in
DynamicMarginCalculationClient replace the direct getBody() usage with a
defensive check: capture the response body into a local Map (from the exchange
call using the same ParameterizedTypeReference), then if the body is null return
Collections.emptyMap() (or throw a clear IllegalStateException if you prefer
failing fast) so callers always receive a non-null Map; ensure to import or
reference java.util.Collections for the empty map fallback.
- Around line 201-217: The remote /statuses POST may return a null body or omit
entries, so update DynamicMarginCalculationClient.getStatuses(List<UUID>) to
defensively handle null responses (e.g., if exchange().getBody() is null return
an empty Map or a map with requested UUIDs mapped to a default/UNKNOWN
DynamicMarginCalculationStatus) and ensure callers (notably getStatus(UUID))
check the returned map and handle missing keys instead of assuming non-null
values; adjust getStatus(UUID) to null-check the map and return an explicit
Optional/throw a clear exception or a default status when the requested UUID is
not present.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java`:
- Around line 184-186: The getStatus method delegates to
getStatuses(List.of(resultUuid)).get(resultUuid) which can return null; update
DynamicSecurityAnalysisClient.getStatus to handle a missing entry by checking
the map result from getStatuses(List.of(resultUuid)) and if the value for
resultUuid is null, throw a clear runtime exception (e.g., IllegalStateException
or NoSuchElementException) with a message containing the resultUuid, otherwise
return the non-null DynamicSecurityAnalysisStatus; reference the getStatus(...)
method and the getStatuses(...) call when making this change.
- Around line 188-205: Annotate the getStatuses(List<UUID> resultUuids)
parameter with the same `@NonNull` used elsewhere in
DynamicSecurityAnalysisClient, and add a null-safe handling of the REST
response: capture the ResponseEntity<Map<UUID, DynamicSecurityAnalysisStatus>>
from getRestTemplate().exchange(...) into a variable, check its getBody() for
null and return Map.of() (or Objects.requireNonNullElse(body, Map.of())) when
null instead of returning getBody() directly; keep existing empty-input check
using CollectionUtils.isEmpty and reuse getRestTemplate() and the existing
ParameterizedTypeReference to locate the exchange call.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`:
- Around line 208-210: The getStatus method delegates to
getStatuses(List.of(resultUuid)).get(resultUuid) which can NPE if getStatuses
returns null or the map lacks the key; update getStatus(UUID resultUuid) to call
getStatuses(List.of(resultUuid)) into a local Map variable, check for null and
for containsKey(resultUuid) and handle both cases safely (e.g., return null or
Optional<DynamicSimulationStatus> per project convention) instead of calling
.get() directly; reference methods getStatus, getStatuses and the type
DynamicSimulationStatus/resultUuid when making the checks and returning the safe
value.
- Around line 213-229: The getStatuses method in DynamicSimulationClientImpl may
return null because RestTemplate.exchange(...).getBody() can be null; update
getStatuses to defensively handle a null response by capturing the result of
getRestTemplate().exchange(...).getBody(), and if it is null return an empty map
(e.g. Map.of() or Collections.emptyMap()) so callers (like getStatus() which
calls .get() on the map) never get a null; apply the same null-safe pattern to
the corresponding methods in DynamicMarginCalculationClient and
DynamicSecurityAnalysisClient.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java`:
- Around line 88-90: getStatus(UUID resultUuid) calls
getDynamicMarginCalculationStatuses(List.of(resultUuid)).get(resultUuid) which
can return null if the map lacks the UUID; add a defensive null check in
getStatus to avoid returning null (e.g., check the map result for resultUuid and
either throw a clear exception such as
NotFoundException/IllegalArgumentException or change the method to return
Optional<DynamicMarginCalculationStatus>) so callers never receive a raw null;
update getStatus to locate the map entry from
getDynamicMarginCalculationStatuses and handle the null case accordingly.
- Around line 133-139: Add defensive checks in
assertNoDynamicMarginCalculationRunning: validate the input resultUuids (throw
IllegalArgumentException if null or empty) and null-safe the call to
getDynamicMarginCalculationStatuses (treat a null map as empty). When building
values from dynamicMarginCalculationStatuses.values(), guard against a null
collection to avoid NPEs; only check for DynamicMarginCalculationStatus.RUNNING
if the map/values are non-null. Use the existing
StudyException(COMPUTATION_RUNNING) only for the running-state detection and use
a clear IllegalArgumentException for bad input.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisService.java`:
- Around line 89-91: The call in getStatus delegates to
getDynamicSecurityAnalysisStatuses(List.of(resultUuid)) and then directly
.get(resultUuid), which can return null and cause NPEs; update getStatus to
first capture the Map returned by getDynamicSecurityAnalysisStatuses, check for
the presence of resultUuid (e.g. containsKey or get != null) and return a safe
value (null or an Optional) or throw a clear exception instead of dereferencing
a potentially missing entry; refer to the methods getStatus and
getDynamicSecurityAnalysisStatuses and the type DynamicSecurityAnalysisStatus to
locate where to add the null-safety check.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java`:
- Around line 245-251: The method assertNoDynamicSimulationRunning currently
assumes getDynamicSimulationStatuses(...) returns a non-null Map and will NPE
when it's null; update assertNoDynamicSimulationRunning to defensively handle a
null (or empty) result from getDynamicSimulationStatuses by treating a null map
as empty before creating the HashSet or checking values, then proceed to check
for DynamicSimulationStatus.RUNNING and throw
StudyException(COMPUTATION_RUNNING) if found; reference the
assertNoDynamicSimulationRunning method, getDynamicSimulationStatuses, and
DynamicSimulationStatus.RUNNING when locating and applying the fix.
- Around line 206-208: The getStatus method delegates to
getDynamicSimulationStatuses but can NPE if that method returns null or the map
lacks the key; update DynamicSimulationServiceImpl.getStatus to first call
Map<UUID, DynamicSimulationStatus> statuses =
getDynamicSimulationStatuses(List.of(resultUuid)), check if statuses is null and
return null (or an Optional if your API prefers), then return
statuses.get(resultUuid) only after confirming the key exists (or use
statuses.getOrDefault(resultUuid, null)) to avoid a NullPointerException when
the map is missing the entry.
- Line 246: The local variable name loadFlowStatuses in
DynamicSimulationServiceImpl is misleading; rename it to
dynamicSimulationStatuses (or simulationStatuses) where it's assigned from
getDynamicSimulationStatuses(computationResultUuids) and update all subsequent
references in the method to use the new name so the variable accurately reflects
it holds DynamicSimulationStatus values.

In `@src/main/java/org/gridsuite/study/server/service/PccMinService.java`:
- Around line 134-144: The RestTemplate call is using a relative path only, so
prepend the configured base URI (pccMinServerBaseUri) to the built path before
calling restTemplate.exchange; update the code around where PCC_MIN_URI +
DELIMITER + "results/statuses" is composed (in PccMinService) to build a full
URL (e.g., concatenate pccMinServerBaseUri and the path) and then call
restTemplate.exchange(fullUrl, HttpMethod.POST, httpEntity, new
ParameterizedTypeReference<Map<UUID, PccMinStatus>>() { }). Ensure you keep the
existing headers and HttpEntity<List<UUID>> httpEntity and return the response
body as before.

In
`@src/main/java/org/gridsuite/study/server/service/SecurityAnalysisService.java`:
- Around line 189-191: getSecurityAnalysisStatus currently calls
getSecurityAnalysisStatuses(List.of(resultUuid)) which will throw NPE when
resultUuid is null; add a null guard in getSecurityAnalysisStatus (check
resultUuid == null) and handle that case explicitly (e.g., return null or an
appropriate default SecurityAnalysisStatus) instead of calling List.of, so
callers like RootNetworkNodeInfoService which use getComputationResultUuid()
that may be null won't break the call chain; reference the methods
getSecurityAnalysisStatus and getSecurityAnalysisStatuses and the caller
RootNetworkNodeInfoService#getComputationResultUuid when making the change.

In
`@src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java`:
- Around line 179-193: The call in getSensitivityAnalysisStatuses builds a
relative path but calls restTemplate.exchange(path, ...) without prepending
sensitivityAnalysisServerBaseUri, causing failed requests; fix by resolving the
full URL before the exchange (e.g., prefixing the path with
sensitivityAnalysisServerBaseUri or using
UriComponentsBuilder.fromHttpUrl(sensitivityAnalysisServerBaseUri).path(path) to
build the absoluteUri) and then call restTemplate.exchange(absoluteUri,
HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID,
SensitivityAnalysisStatus>>() { }).getBody() so the restTemplate uses the full
base URI.

In
`@src/main/java/org/gridsuite/study/server/service/StateEstimationService.java`:
- Around line 134-147: getStateEstimationStatuses builds a relative path and
calls restTemplate.exchange(path, ...) without prepending the configured base
URI (stateEstimationServerServerBaseUri), causing requests to fail; fix by
concatenating or resolving stateEstimationServerServerBaseUri with the local
path before calling restTemplate.exchange (use the existing path variable and
the stateEstimationServerServerBaseUri field), then call
restTemplate.exchange(fullUri, HttpMethod.POST, httpEntity, new
ParameterizedTypeReference<Map<UUID, StateEstimationStatus>>() { }).getBody() so
the request uses the full service URI.

In `@src/main/java/org/gridsuite/study/server/service/VoltageInitService.java`:
- Around line 134-146: getVoltageInitStatuses builds only a relative path and
calls restTemplate.exchange(path, ...) so requests fail; update the method to
prepend the configured base URI (voltageInitServerBaseUri) when building the
request URI (e.g. combine voltageInitServerBaseUri with the current path or use
UriComponentsBuilder starting from voltageInitServerBaseUri) and then call
restTemplate.exchange with that full URI while keeping the existing HttpEntity,
headers, and ParameterizedTypeReference to return the Map<UUID,
VoltageInitStatus>.

---

Duplicate comments:
In `@src/main/java/org/gridsuite/study/server/service/LoadFlowService.java`:
- Around line 155-156: The call in LoadFlowService that returns
restTemplate.exchange(...).getBody() can return null; update the code in the
method that calls restTemplate.exchange (the POST to loadFlowServerBaseUri +
path in LoadFlowService) to guard against a null body by returning an empty
Map<UUID, LoadFlowStatus> when getBody() is null (e.g., assign the result to a
local variable and return result != null ? result : Collections.emptyMap()) so
downstream callers like assertNoLoadFlowRunning never see a null.

---

Nitpick comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`:
- Line 208: Replace the unnecessary call to buildAndExpand() with build() when
constructing the URI for statuses: in DynamicMarginCalculationClient where
uriComponents is assigned (var uriComponents =
UriComponentsBuilder.fromUriString(endPointUrl + "/statuses")...), use .build()
instead of .buildAndExpand() because there are no path variables to expand.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java`:
- Line 135: Replace the unnecessary HashSet allocation in
DynamicMarginCalculationService: instead of creating
Set<DynamicMarginCalculationStatus> values = new
HashSet<>(dynamicMarginCalculationStatuses.values()), use the collection
returned by dynamicMarginCalculationStatuses.values() directly (e.g. assign to
Collection<DynamicMarginCalculationStatus> or call .values().contains(...)
inline) to perform the contains check without the extra copy/overhead.

In
`@src/main/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisService.java`:
- Around line 137-143: The method assertNoDynamicSecurityAnalysisRunning creates
an unnecessary HashSet from dynamicSecurityAnalysisStatuses.values(); instead
check for RUNNING directly on the Collection returned by
getDynamicSecurityAnalysisStatuses(resultUuids) to avoid allocation—e.g. replace
the new HashSet<>(dynamicSecurityAnalysisStatuses.values()) usage with a direct
contains/stream anyMatch on dynamicSecurityAnalysisStatuses.values() and keep
throwing StudyException(COMPUTATION_RUNNING) when
DynamicSecurityAnalysisStatus.RUNNING is found.

In
`@src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java`:
- Around line 202-215: In ShortCircuitService.getShortCircuitAnalysisStatuses,
replace the null-unsafe resultUuids.isEmpty() check with a null-safe
CollectionUtils.isEmpty(resultUuids) to match other services (PccMinService,
StateEstimationService, VoltageInitService, SensitivityAnalysisService); ensure
you import org.springframework.util.CollectionUtils if not already present and
keep the early return Map.of() behavior when the collection is empty or null.

In `@src/main/java/org/gridsuite/study/server/service/VoltageInitService.java`:
- Line 13: VoltageInitService imports
org.apache.commons.collections4.CollectionUtils which is inconsistent with other
services; change the import to org.springframework.util.CollectionUtils and
update any calls if needed to match Spring's API (e.g.,
CollectionUtils.isEmpty/hasLength semantics) so usages in the VoltageInitService
class (look for references inside the VoltageInitService methods/constructor)
compile and follow the same convention as PccMinService, StateEstimationService,
and SensitivityAnalysisService.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2918cf6f-5027-4cfd-a02f-8d2fd133efae

📥 Commits

Reviewing files that changed from the base of the PR and between 61b4da4 and b19f1e9.

📒 Files selected for processing (22)
  • src/main/java/org/gridsuite/study/server/controller/StudyController.java
  • src/main/java/org/gridsuite/study/server/dto/ShortCircuitAnalysisStatus.java
  • src/main/java/org/gridsuite/study/server/repository/rootnetwork/RootNetworkNodeInfoRepository.java
  • src/main/java/org/gridsuite/study/server/service/LoadFlowService.java
  • src/main/java/org/gridsuite/study/server/service/PccMinService.java
  • src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java
  • src/main/java/org/gridsuite/study/server/service/SecurityAnalysisService.java
  • src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java
  • src/main/java/org/gridsuite/study/server/service/StateEstimationService.java
  • src/main/java/org/gridsuite/study/server/service/StudyService.java
  • src/main/java/org/gridsuite/study/server/service/VoltageInitService.java
  • src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClient.java
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java
  • src/main/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationService.java
  • src/main/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisService.java
  • src/main/java/org/gridsuite/study/server/service/dynamicsimulation/DynamicSimulationService.java
  • src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java
  • src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java
  • src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java
  • src/test/java/org/gridsuite/study/server/studycontroller/NodeControllerTest.java

Comment on lines 197 to +199
public DynamicMarginCalculationStatus getStatus(@NonNull UUID resultUuid) {
Objects.requireNonNull(resultUuid);
return getStatuses(List.of(resultUuid)).get(resultUuid);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Potential null return when status not found in map.

The .get(resultUuid) call will return null if the map from getStatuses() doesn't contain the requested UUID. This can happen if the remote service returns an incomplete response. Callers expecting a non-null DynamicMarginCalculationStatus may encounter NPEs.

🛡️ Suggested fix to handle missing status
 public DynamicMarginCalculationStatus getStatus(`@NonNull` UUID resultUuid) {
-    return getStatuses(List.of(resultUuid)).get(resultUuid);
+    Map<UUID, DynamicMarginCalculationStatus> statuses = getStatuses(List.of(resultUuid));
+    DynamicMarginCalculationStatus status = statuses.get(resultUuid);
+    if (status == null) {
+        throw new IllegalStateException("No status returned for result UUID: " + resultUuid);
+    }
+    return status;
 }
📝 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
public DynamicMarginCalculationStatus getStatus(@NonNull UUID resultUuid) {
Objects.requireNonNull(resultUuid);
return getStatuses(List.of(resultUuid)).get(resultUuid);
}
public DynamicMarginCalculationStatus getStatus(`@NonNull` UUID resultUuid) {
Map<UUID, DynamicMarginCalculationStatus> statuses = getStatuses(List.of(resultUuid));
DynamicMarginCalculationStatus status = statuses.get(resultUuid);
if (status == null) {
throw new IllegalStateException("No status returned for result UUID: " + resultUuid);
}
return status;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
around lines 197 - 199, getStatus currently may return null when
getStatuses(List.of(resultUuid)) doesn't contain resultUuid; change getStatus to
return Optional<DynamicMarginCalculationStatus> and wrap the map lookup with
Optional.ofNullable(getStatuses(List.of(resultUuid)).get(resultUuid)) so callers
must handle missing status explicitly; update any callers of
DynamicMarginCalculationClient.getStatus to handle Optional (or alternatively
throw a clear checked/unchecked exception with resultUuid in the message if you
prefer fail-fast semantics).

Comment on lines +201 to 217
public Map<UUID, DynamicMarginCalculationStatus> getStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}

String url = UriComponentsBuilder
.fromUriString(resultBaseUrl + "/{resultUuid}/status")
.buildAndExpand(resultUuid)
.toUriString();
String endPointUrl = buildEndPointUrl(getBaseUri(), DYNAMIC_MARGIN_CALCULATION_API_VERSION, DYNAMIC_MARGIN_CALCULATION_END_POINT_RESULT);

// call dynamic-margin-calculation REST API
return getRestTemplate().getForObject(url, DynamicMarginCalculationStatus.class);
var uriComponents = UriComponentsBuilder.fromUriString(endPointUrl + "/statuses").buildAndExpand();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);

return getRestTemplate().exchange(uriComponents.toUriString(), HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicMarginCalculationStatus>>() {
}).getBody();
}
Copy link
Copy Markdown

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
# Search for API documentation or tests that describe the /statuses endpoint behavior
rg -nP -C5 '(statuses|getStatuses).*endpoint|POST.*statuses' --type=java --type=md

Repository: gridsuite/study-server

Length of output: 48


🏁 Script executed:

# Find test files for DynamicMarginCalculationClient
fd -type f -name '*DynamicMarginCalculation*Test*.java' -o -name '*Test*DynamicMarginCalculation*.java'

Repository: gridsuite/study-server

Length of output: 237


🏁 Script executed:

# Search for test references to getStatuses method
rg -l 'getStatuses' --type=java

Repository: gridsuite/study-server

Length of output: 839


🏁 Script executed:

# Look for OpenAPI/Swagger specs or contract files
fd -type f \( -name '*.yaml' -o -name '*.yml' -o -name '*.json' -o -name '*contract*' -o -name '*openapi*' -o -name '*swagger*' \)

Repository: gridsuite/study-server

Length of output: 237


🏁 Script executed:

# Check how getStatuses is used in the codebase
rg 'getStatuses\(' --type=java -B 2 -A 5

Repository: gridsuite/study-server

Length of output: 10162


🏁 Script executed:

# Find test files for the client
fd 'DynamicMarginCalculation.*Test' -type f

Repository: gridsuite/study-server

Length of output: 237


🏁 Script executed:

# Examine the getStatus method in DynamicMarginCalculationClient to see if it handles null
rg -A 3 'public DynamicMarginCalculationStatus getStatus' src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java

Repository: gridsuite/study-server

Length of output: 218


🏁 Script executed:

# Look for how the client methods are tested
fd 'Test' -type f | xargs grep -l 'getStatuses\|DynamicMarginCalculation' | head -5

Repository: gridsuite/study-server

Length of output: 237


🏁 Script executed:

# Check the complete DynamicMarginCalculationClient implementation
cat src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java

Repository: gridsuite/study-server

Length of output: 11300


🏁 Script executed:

# Search for test files that test getStatuses or DynamicMarginCalculation
find . -name '*Test*.java' | xargs grep -l 'DynamicMarginCalculation\|getStatuses' 2>/dev/null

Repository: gridsuite/study-server

Length of output: 860


🏁 Script executed:

# Look for any documentation or comments about the statuses endpoint
rg -i 'statuses.*endpoint|endpoint.*statuses|contract|guarantee' --type=java -B 2 -A 2

Repository: gridsuite/study-server

Length of output: 2825


🏁 Script executed:

# Check similar client implementations to see if they handle partial results
rg -A 10 'public Map.*getStatuses' src/main/java/org/gridsuite/study/server/service/client/

Repository: gridsuite/study-server

Length of output: 5484


🏁 Script executed:

# Examine the DynamicMarginCalculationClientTest to see what it tests
cat src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java

Repository: gridsuite/study-server

Length of output: 19472


🏁 Script executed:

# Also check the service test to see how getStatuses is called and handled
cat src/test/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationServiceTest.java

Repository: gridsuite/study-server

Length of output: 8535


Verify remote API contract and handle potential null results.

The getStatus() method calls getStatuses() and immediately invokes .get(resultUuid) without null checking. If the remote /statuses endpoint returns:

  1. A null body, getStatus() will throw NullPointerException
  2. Partial results (missing entries for requested UUIDs), callers will receive null instead of a status

Either ensure the API contract guarantees complete coverage for all requested UUIDs, or add defensive null handling in getStatus() and callers that depend on map entries existing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
around lines 201 - 217, The remote /statuses POST may return a null body or omit
entries, so update DynamicMarginCalculationClient.getStatuses(List<UUID>) to
defensively handle null responses (e.g., if exchange().getBody() is null return
an empty Map or a map with requested UUIDs mapped to a default/UNKNOWN
DynamicMarginCalculationStatus) and ensure callers (notably getStatus(UUID))
check the returned map and handle missing keys instead of assuming non-null
values; adjust getStatus(UUID) to null-check the map and return an explicit
Optional/throw a clear exception or a default status when the requested UUID is
not present.

Comment on lines +215 to +216
return getRestTemplate().exchange(uriComponents.toUriString(), HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicMarginCalculationStatus>>() {
}).getBody();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Potential null return from REST call.

The getBody() method can return null in edge cases (e.g., certain HTTP responses). This would cause NPEs in calling code that expects a non-null map. Consider adding a null check and returning an empty map or throwing an exception.

🛡️ Suggested defensive null check
-    return getRestTemplate().exchange(uriComponents.toUriString(), HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicMarginCalculationStatus>>() {
-    }).getBody();
+    Map<UUID, DynamicMarginCalculationStatus> body = getRestTemplate().exchange(
+            uriComponents.toUriString(), 
+            HttpMethod.POST, 
+            httpEntity, 
+            new ParameterizedTypeReference<Map<UUID, DynamicMarginCalculationStatus>>() {}
+    ).getBody();
+    return body != null ? body : Map.of();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
around lines 215 - 216, The REST call currently returns
getRestTemplate().exchange(...).getBody() which can be null and cause NPEs; in
DynamicMarginCalculationClient replace the direct getBody() usage with a
defensive check: capture the response body into a local Map (from the exchange
call using the same ParameterizedTypeReference), then if the body is null return
Collections.emptyMap() (or throw a clear IllegalStateException if you prefer
failing fast) so callers always receive a non-null Map; ensure to import or
reference java.util.Collections for the empty map fallback.

Comment on lines 184 to +186
public DynamicSecurityAnalysisStatus getStatus(@NonNull UUID resultUuid) {
Objects.requireNonNull(resultUuid);
return getStatuses(List.of(resultUuid)).get(resultUuid);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add null safety check for the delegated batch call.

The delegation to getStatuses(List.of(resultUuid)).get(resultUuid) can return null if the server's response map does not contain the requested UUID. This would break the expected contract and cause NPEs in calling code that expects a non-null DynamicSecurityAnalysisStatus.

🛡️ Proposed fix to add null safety
 public DynamicSecurityAnalysisStatus getStatus(`@NonNull` UUID resultUuid) {
-    return getStatuses(List.of(resultUuid)).get(resultUuid);
+    DynamicSecurityAnalysisStatus status = getStatuses(List.of(resultUuid)).get(resultUuid);
+    if (status == null) {
+        throw new IllegalStateException("Status not found for result UUID: " + resultUuid);
+    }
+    return status;
 }
📝 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
public DynamicSecurityAnalysisStatus getStatus(@NonNull UUID resultUuid) {
Objects.requireNonNull(resultUuid);
return getStatuses(List.of(resultUuid)).get(resultUuid);
}
public DynamicSecurityAnalysisStatus getStatus(`@NonNull` UUID resultUuid) {
DynamicSecurityAnalysisStatus status = getStatuses(List.of(resultUuid)).get(resultUuid);
if (status == null) {
throw new IllegalStateException("Status not found for result UUID: " + resultUuid);
}
return status;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java`
around lines 184 - 186, The getStatus method delegates to
getStatuses(List.of(resultUuid)).get(resultUuid) which can return null; update
DynamicSecurityAnalysisClient.getStatus to handle a missing entry by checking
the map result from getStatuses(List.of(resultUuid)) and if the value for
resultUuid is null, throw a clear runtime exception (e.g., IllegalStateException
or NoSuchElementException) with a message containing the resultUuid, otherwise
return the non-null DynamicSecurityAnalysisStatus; reference the getStatus(...)
method and the getStatuses(...) call when making this change.

Comment on lines +188 to 205
public Map<UUID, DynamicSecurityAnalysisStatus> getStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}

String resultBaseUrl = buildEndPointUrl(getBaseUri(), DYNAMIC_SECURITY_ANALYSIS_API_VERSION, DYNAMIC_SECURITY_ANALYSIS_END_POINT_RESULT);

String url = UriComponentsBuilder.fromUriString(resultBaseUrl + "/{resultUuid}/status")
.buildAndExpand(resultUuid)
String url = UriComponentsBuilder.fromUriString(resultBaseUrl + "/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);

// call dynamic-security-analysis REST API
return getRestTemplate().getForObject(url, DynamicSecurityAnalysisStatus.class);
return getRestTemplate().exchange(url, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicSecurityAnalysisStatus >>() {
}).getBody();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add parameter validation and null check on REST response body.

Two issues:

  1. The resultUuids parameter lacks @NonNull annotation, inconsistent with other methods in this class.
  2. RestTemplate.exchange(...).getBody() can return null in certain failure scenarios, which would cause an NPE when the caller expects a non-null Map.
🛡️ Proposed fix to add validation and null safety
-public Map<UUID, DynamicSecurityAnalysisStatus> getStatuses(List<UUID> resultUuids) {
+public Map<UUID, DynamicSecurityAnalysisStatus> getStatuses(`@NonNull` List<UUID> resultUuids) {
     if (CollectionUtils.isEmpty(resultUuids)) {
         return Map.of();
     }

     String resultBaseUrl = buildEndPointUrl(getBaseUri(), DYNAMIC_SECURITY_ANALYSIS_API_VERSION, DYNAMIC_SECURITY_ANALYSIS_END_POINT_RESULT);

     String url = UriComponentsBuilder.fromUriString(resultBaseUrl + "/statuses")
             .toUriString();
     HttpHeaders headers = new HttpHeaders();
     headers.setContentType(MediaType.APPLICATION_JSON);

     HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);

     // call dynamic-security-analysis REST API
-    return getRestTemplate().exchange(url, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicSecurityAnalysisStatus >>() {
-    }).getBody();
+    Map<UUID, DynamicSecurityAnalysisStatus> body = getRestTemplate().exchange(url, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, DynamicSecurityAnalysisStatus >>() {
+    }).getBody();
+    if (body == null) {
+        throw new IllegalStateException("Received null response body from statuses endpoint");
+    }
+    return body;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java`
around lines 188 - 205, Annotate the getStatuses(List<UUID> resultUuids)
parameter with the same `@NonNull` used elsewhere in
DynamicSecurityAnalysisClient, and add a null-safe handling of the REST
response: capture the ResponseEntity<Map<UUID, DynamicSecurityAnalysisStatus>>
from getRestTemplate().exchange(...) into a variable, check its getBody() for
null and return Map.of() (or Objects.requireNonNullElse(body, Map.of())) when
null instead of returning getBody() directly; keep existing empty-input check
using CollectionUtils.isEmpty and reuse getRestTemplate() and the existing
ParameterizedTypeReference to locate the exchange call.

Comment on lines 134 to 144
String path = UriComponentsBuilder
.fromPath(PCC_MIN_URI + DELIMITER + "results/{resultUuid}/status")
.buildAndExpand(resultUuid).toUriString();
return restTemplate.getForObject(pccMinServerBaseUri + path, String.class);
.fromPath(PCC_MIN_URI + DELIMITER + "results/statuses")
.toUriString();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, PccMinStatus>>() {
}).getBody();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing base URI in RestTemplate call causes request to fail.

Line 142 invokes restTemplate.exchange(path, ...) with only the relative path, but the pccMinServerBaseUri is not prepended. This will cause the HTTP request to fail because RestTemplate doesn't know the target server.

🐛 Proposed fix
-        return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, PccMinStatus>>() {
+        return restTemplate.exchange(pccMinServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, PccMinStatus>>() {
         }).getBody();
📝 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
String path = UriComponentsBuilder
.fromPath(PCC_MIN_URI + DELIMITER + "results/{resultUuid}/status")
.buildAndExpand(resultUuid).toUriString();
return restTemplate.getForObject(pccMinServerBaseUri + path, String.class);
.fromPath(PCC_MIN_URI + DELIMITER + "results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, PccMinStatus>>() {
}).getBody();
}
String path = UriComponentsBuilder
.fromPath(PCC_MIN_URI + DELIMITER + "results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.exchange(pccMinServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, PccMinStatus>>() {
}).getBody();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/study/server/service/PccMinService.java` around
lines 134 - 144, The RestTemplate call is using a relative path only, so prepend
the configured base URI (pccMinServerBaseUri) to the built path before calling
restTemplate.exchange; update the code around where PCC_MIN_URI + DELIMITER +
"results/statuses" is composed (in PccMinService) to build a full URL (e.g.,
concatenate pccMinServerBaseUri and the path) and then call
restTemplate.exchange(fullUrl, HttpMethod.POST, httpEntity, new
ParameterizedTypeReference<Map<UUID, PccMinStatus>>() { }). Ensure you keep the
existing headers and HttpEntity<List<UUID>> httpEntity and return the response
body as before.

Comment on lines 189 to +191
public SecurityAnalysisStatus getSecurityAnalysisStatus(UUID resultUuid) {
return getSecurityAnalysisStatuses(List.of(resultUuid)).get(resultUuid);
}
Copy link
Copy Markdown

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
# Description: Find all callers of getSecurityAnalysisStatus to verify null handling expectations.

# Search for method calls with context
rg -nP --type=java -C5 '\.getSecurityAnalysisStatus\s*\(' \
  -g '!SecurityAnalysisService.java'

Repository: gridsuite/study-server

Length of output: 3460


🏁 Script executed:

#!/bin/bash
# Find the getComputationResultUuid method implementation
rg -nP --type=java -A10 'private.*UUID\s+getComputationResultUuid' src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java

Repository: gridsuite/study-server

Length of output: 1200


🏁 Script executed:

#!/bin/bash
# Also check the getSecurityAnalysisStatuses implementation to understand null handling
rg -nP --type=java -A15 'public.*getSecurityAnalysisStatuses' src/main/java/org/gridsuite/study/server/service/SecurityAnalysisService.java

Repository: gridsuite/study-server

Length of output: 925


Add null guard for resultUuid parameter.

List.of(resultUuid) will throw NullPointerException if resultUuid is null. The caller in RootNetworkNodeInfoService (line 854) obtains resultUuid from getComputationResultUuid(), which can return null when the entity's result UUID field is uninitialized. Either add a null check before calling List.of() or handle the null case explicitly to prevent breaking the call chain.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/SecurityAnalysisService.java`
around lines 189 - 191, getSecurityAnalysisStatus currently calls
getSecurityAnalysisStatuses(List.of(resultUuid)) which will throw NPE when
resultUuid is null; add a null guard in getSecurityAnalysisStatus (check
resultUuid == null) and handle that case explicitly (e.g., return null or an
appropriate default SecurityAnalysisStatus) instead of calling List.of, so
callers like RootNetworkNodeInfoService which use getComputationResultUuid()
that may be null won't break the call chain; reference the methods
getSecurityAnalysisStatus and getSecurityAnalysisStatuses and the caller
RootNetworkNodeInfoService#getComputationResultUuid when making the change.

Comment on lines +179 to 193
public Map<UUID, SensitivityAnalysisStatus> getSensitivityAnalysisStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}

String path = UriComponentsBuilder.fromPath(DELIMITER + SENSITIVITY_ANALYSIS_API_VERSION + "/results/{resultUuid}/status")
.buildAndExpand(resultUuid).toUriString();
String path = UriComponentsBuilder.fromPath(DELIMITER + SENSITIVITY_ANALYSIS_API_VERSION + "/results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);

return restTemplate.getForObject(sensitivityAnalysisServerBaseUri + path, String.class);
return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, SensitivityAnalysisStatus>>() {
}).getBody();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing base URI in RestTemplate call causes request to fail.

Line 191 invokes restTemplate.exchange(path, ...) with only the relative path, but the sensitivityAnalysisServerBaseUri is not prepended.

🐛 Proposed fix
-        return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, SensitivityAnalysisStatus>>() {
+        return restTemplate.exchange(sensitivityAnalysisServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, SensitivityAnalysisStatus>>() {
         }).getBody();
📝 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
public Map<UUID, SensitivityAnalysisStatus> getSensitivityAnalysisStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}
String path = UriComponentsBuilder.fromPath(DELIMITER + SENSITIVITY_ANALYSIS_API_VERSION + "/results/{resultUuid}/status")
.buildAndExpand(resultUuid).toUriString();
String path = UriComponentsBuilder.fromPath(DELIMITER + SENSITIVITY_ANALYSIS_API_VERSION + "/results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.getForObject(sensitivityAnalysisServerBaseUri + path, String.class);
return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, SensitivityAnalysisStatus>>() {
}).getBody();
}
public Map<UUID, SensitivityAnalysisStatus> getSensitivityAnalysisStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}
String path = UriComponentsBuilder.fromPath(DELIMITER + SENSITIVITY_ANALYSIS_API_VERSION + "/results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.exchange(sensitivityAnalysisServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, SensitivityAnalysisStatus>>() {
}).getBody();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java`
around lines 179 - 193, The call in getSensitivityAnalysisStatuses builds a
relative path but calls restTemplate.exchange(path, ...) without prepending
sensitivityAnalysisServerBaseUri, causing failed requests; fix by resolving the
full URL before the exchange (e.g., prefixing the path with
sensitivityAnalysisServerBaseUri or using
UriComponentsBuilder.fromHttpUrl(sensitivityAnalysisServerBaseUri).path(path) to
build the absoluteUri) and then call restTemplate.exchange(absoluteUri,
HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID,
SensitivityAnalysisStatus>>() { }).getBody() so the restTemplate uses the full
base URI.

Comment on lines +134 to 147
public Map<UUID, StateEstimationStatus> getStateEstimationStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}
String path = UriComponentsBuilder
.fromPath(DELIMITER + STATE_ESTIMATION_API_VERSION + "/results/{resultUuid}/status")
.buildAndExpand(resultUuid).toUriString();
return restTemplate.getForObject(stateEstimationServerServerBaseUri + path, String.class);
.fromPath(DELIMITER + STATE_ESTIMATION_API_VERSION + "/results/statuses")
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);
return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, StateEstimationStatus>>() {
}).getBody();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing base URI in RestTemplate call causes request to fail.

Line 145 invokes restTemplate.exchange(path, ...) with only the relative path, but the stateEstimationServerServerBaseUri is not prepended.

🐛 Proposed fix
-        return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, StateEstimationStatus>>() {
+        return restTemplate.exchange(stateEstimationServerServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, StateEstimationStatus>>() {
         }).getBody();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/study/server/service/StateEstimationService.java`
around lines 134 - 147, getStateEstimationStatuses builds a relative path and
calls restTemplate.exchange(path, ...) without prepending the configured base
URI (stateEstimationServerServerBaseUri), causing requests to fail; fix by
concatenating or resolving stateEstimationServerServerBaseUri with the local
path before calling restTemplate.exchange (use the existing path variable and
the stateEstimationServerServerBaseUri field), then call
restTemplate.exchange(fullUri, HttpMethod.POST, httpEntity, new
ParameterizedTypeReference<Map<UUID, StateEstimationStatus>>() { }).getBody() so
the request uses the full service URI.

Comment on lines +134 to 146
public Map<UUID, VoltageInitStatus> getVoltageInitStatuses(List<UUID> resultUuids) {
if (CollectionUtils.isEmpty(resultUuids)) {
return Map.of();
}
String path = UriComponentsBuilder.fromPath(DELIMITER + VOLTAGE_INIT_API_VERSION + "/results/statuses").toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<List<UUID>> httpEntity = new HttpEntity<>(resultUuids, headers);

return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, VoltageInitStatus>>() {
}).getBody();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing base URI in RestTemplate call causes request to fail.

Line 144 invokes restTemplate.exchange(path, ...) with only the relative path, but the voltageInitServerBaseUri is not prepended.

🐛 Proposed fix
-        return restTemplate.exchange(path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, VoltageInitStatus>>() {
+        return restTemplate.exchange(voltageInitServerBaseUri + path, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<Map<UUID, VoltageInitStatus>>() {
         }).getBody();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/study/server/service/VoltageInitService.java`
around lines 134 - 146, getVoltageInitStatuses builds only a relative path and
calls restTemplate.exchange(path, ...) so requests fail; update the method to
prepend the configured base URI (voltageInitServerBaseUri) when building the
request URI (e.g. combine voltageInitServerBaseUri with the current path or use
UriComponentsBuilder starting from voltageInitServerBaseUri) and then call
restTemplate.exchange with that full URI while keeping the existing HttpEntity,
headers, and ParameterizedTypeReference to return the Map<UUID,
VoltageInitStatus>.

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.

2 participants