Skip to content

Get computation statuses for branch#986

Open
sBouzols wants to merge 11 commits into
mainfrom
get_computation_statuses_for_branch
Open

Get computation statuses for branch#986
sBouzols wants to merge 11 commits into
mainfrom
get_computation_statuses_for_branch

Conversation

@sBouzols
Copy link
Copy Markdown
Contributor

@sBouzols sBouzols commented Apr 30, 2026

PR Summary

Allow to get multiple computation result statuses at once for each computation server.
This is useful for example when checking computation result statuses on multiple nodes (branch) in the network modification tree.

choices :
findAllById : Returns all instances of the type T with the given IDs.
If some or all ids are not found, no entities are returned for these IDs.

Then if we ask to a computation service statuses for 3 resultUuids for example, we could obtain 3 or less statuses.
Then the size of resultUuids and of the returned map could not be the same. The map is less or equally sized.

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

Warning

Rate limit exceeded

@sBouzols has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 41 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d688052-a813-4182-8e62-2290667faf48

📥 Commits

Reviewing files that changed from the base of the PR and between 4071800 and 6dfee92.

📒 Files selected for processing (15)
  • 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/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/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/impl/DynamicSimulationServiceImpl.java
  • src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java
  • src/test/java/org/gridsuite/study/server/service/client/timeseries/TimeSeriesClientTest.java
📝 Walkthrough

Walkthrough

This PR refactors computation services to use batch status retrieval APIs. Services now fetch statuses for multiple result UUIDs via POST /results/statuses and return typed status enums; single-result methods delegate to bulk calls. Controllers return enum names and tests update mocks for batch assertions.

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 renamed to ShortCircuitAnalysisStatus. Repository adds findAllByNodeInfoIdInAndRootNetworkId(List<UUID>, UUID) for batch node-info retrieval.
Core Service Batch Status Retrieval
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/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/VoltageInitService.java, src/main/java/org/gridsuite/study/server/service/shortcircuit/ShortCircuitService.java
Services adopt a consistent pattern: add getXxxStatuses(List<UUID>) that POSTs UUID lists to /results/statuses returning Map<UUID, XxxStatus>. Single-result getXxxStatus(UUID) delegates to bulk method. New assertNoXxxRunning(List<UUID>) methods check for RUNNING and throw StudyException(COMPUTATION_RUNNING). Imports updated for generic deserialization and collection utilities.
Dynamic Clients & Services Batch Support
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
Clients add getStatuses(List<UUID>) to POST UUID lists and deserialize typed status maps. Corresponding services delegate single-result calls to bulk methods and add assertNo...Running(List<UUID>) checks.
Root Network Node Info Batch Coordination
src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java
Adds getRootNetworkNodeInfos(List<UUID>, UUID) and getComputationResultUuids(List<UUID>, UUID, ComputationType) for batch retrieval. Replaces assertComputationNotRunning(UUID, UUID) with assertComputationsNotRunning(List<UUID>, UUID) and updates multiple status getters to return typed enums instead of String.
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 imports CompositeModificationsActionType and ModificationsActionType. Status endpoints for shortcircuit, voltage-init, sensitivity-analysis, state-estimation, and pcc-min now return enum names (.name()). StudyService delegates bulk computation-not-running checks to rootNetworkNodeInfoService.assertComputationsNotRunning(...).
Test Mock Updates
src/test/java/org/gridsuite/study/server/NetworkModificationTreeTest.java, src/test/java/org/gridsuite/study/server/studycontroller/NodeControllerTest.java
Mockito stubs updated to use assertComputationsNotRunning(any(), any()) instead of assertComputationNotRunning(any(), any()) in affected tests and helpers.

Suggested reviewers

  • Meklo
  • AbdelHedhili
  • khouadrired
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Get computation statuses for branch' directly reflects the main change: adding batch status retrieval methods for multiple computations across nodes.
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.
Description check ✅ Passed The PR description clearly explains the purpose: enabling bulk retrieval of computation result statuses across multiple nodes.

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


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
Comment thread src/main/java/org/gridsuite/study/server/service/LoadFlowService.java Outdated
Comment thread src/main/java/org/gridsuite/study/server/service/LoadFlowService.java Outdated
Comment thread src/main/java/org/gridsuite/study/server/service/LoadFlowService.java Outdated
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 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 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>.

@sBouzols sBouzols changed the title [WIP] Get computation statuses for branch Get computation statuses for branch May 21, 2026
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.

Caution

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

⚠️ Outside diff range comments (2)
src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java (1)

175-177: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a null guard before delegating in SensitivityAnalysisService#getSensitivityAnalysisStatus(UUID) (src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java:175-177)

RootNetworkNodeInfoService can pass a null resultUuid when the sensitivity computation UUID is missing, but the current implementation does List.of(resultUuid), which throws before the controller can reach the expected 204/noContent path.

🐛 Proposed fix
 public SensitivityAnalysisStatus getSensitivityAnalysisStatus(UUID resultUuid) {
+    if (resultUuid == null) {
+        return null;
+    }
     return getSensitivityAnalysisStatuses(List.of(resultUuid)).get(resultUuid);
 }
🤖 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 175 - 177, Add a null guard in
SensitivityAnalysisService#getSensitivityAnalysisStatus(UUID): if the incoming
resultUuid is null return null (or the appropriate "no content" sentinel)
immediately instead of calling List.of(resultUuid); then delegate to
getSensitivityAnalysisStatuses(List<UUID>) only when resultUuid is non-null.
This prevents List.of from throwing on a null UUID and allows the controller to
handle the 204/noContent path.
src/main/java/org/gridsuite/study/server/service/StudyService.java (1)

2507-2521: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add bounds checks before index-based access.

The method assumes copyUuids and copyChildren have at least as many elements as their corresponding source lists, but performs no validation:

  1. Line 2511: copyUuids.get(i) will throw IndexOutOfBoundsException if copyUuids.size() < modificationsUuids.size()
  2. Line 2517: copyChildren.get(i) will throw IndexOutOfBoundsException if copyChildren.size() < originalChildren.size()

Additionally, the code assumes findAllChildrenUuids returns children in a consistent, deterministic order for both original and copy operations. If the order differs or the duplication is partial, the UUID mappings will be incorrect.

🛡️ Proposed fix with size validation
 private void copyModificationsToExclude(UUID originNodeUuid,
                                         UUID targetNodeUuid,
                                         List<UUID> modificationsUuids,
                                         NetworkModificationsResult networkModificationResults) {
     Map<UUID, UUID> mappingModificationsUuids = new HashMap<>();
     List<UUID> copyUuids = networkModificationResults.modificationUuids();
 
+    if (copyUuids.size() != modificationsUuids.size()) {
+        throw new IllegalStateException(String.format(
+            "Modification duplication size mismatch: expected %d, got %d",
+            modificationsUuids.size(), copyUuids.size()));
+    }
+
     // Map root-level modifications
     for (int i = 0; i < modificationsUuids.size(); i++) {
         mappingModificationsUuids.put(modificationsUuids.get(i), copyUuids.get(i));
     }
 
     List<UUID> originalChildren = networkModificationService.findAllChildrenUuids(modificationsUuids);
     List<UUID> copyChildren = networkModificationService.findAllChildrenUuids(copyUuids);
+    if (copyChildren.size() != originalChildren.size()) {
+        throw new IllegalStateException(String.format(
+            "Children duplication size mismatch: expected %d, got %d",
+            originalChildren.size(), copyChildren.size()));
+    }
+
     for (int i = 0; i < originalChildren.size(); i++) {
         mappingModificationsUuids.put(originalChildren.get(i), copyChildren.get(i));
     }
 
     rootNetworkNodeInfoService.copyModificationsToExcludeFromTags(originNodeUuid, targetNodeUuid, mappingModificationsUuids);
 }
🤖 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/StudyService.java` around
lines 2507 - 2521, The loops in StudyService that map modifications (using
modificationsUuids -> copyUuids and originalChildren -> copyChildren) lack
bounds checks and assume both lists are same size/order; update the code in
StudyService to iterate only up to Math.min(source.size(), dest.size()) (e.g.,
use min of modificationsUuids and copyUuids, and min of originalChildren and
copyChildren) and handle size mismatches by logging an error or throwing a clear
exception before calling
rootNetworkNodeInfoService.copyModificationsToExcludeFromTags; also consider
validating deterministic ordering (or documenting/ensuring the contract of
networkModificationService.findAllChildrenUuids) so mappingModificationsUuids is
correct.
🤖 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.

Outside diff comments:
In
`@src/main/java/org/gridsuite/study/server/service/SensitivityAnalysisService.java`:
- Around line 175-177: Add a null guard in
SensitivityAnalysisService#getSensitivityAnalysisStatus(UUID): if the incoming
resultUuid is null return null (or the appropriate "no content" sentinel)
immediately instead of calling List.of(resultUuid); then delegate to
getSensitivityAnalysisStatuses(List<UUID>) only when resultUuid is non-null.
This prevents List.of from throwing on a null UUID and allows the controller to
handle the 204/noContent path.

In `@src/main/java/org/gridsuite/study/server/service/StudyService.java`:
- Around line 2507-2521: The loops in StudyService that map modifications (using
modificationsUuids -> copyUuids and originalChildren -> copyChildren) lack
bounds checks and assume both lists are same size/order; update the code in
StudyService to iterate only up to Math.min(source.size(), dest.size()) (e.g.,
use min of modificationsUuids and copyUuids, and min of originalChildren and
copyChildren) and handle size mismatches by logging an error or throwing a clear
exception before calling
rootNetworkNodeInfoService.copyModificationsToExcludeFromTags; also consider
validating deterministic ordering (or documenting/ensuring the contract of
networkModificationService.findAllChildrenUuids) so mappingModificationsUuids is
correct.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 54a48ae2-677c-42ca-b2f4-99f864e67efc

📥 Commits

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

📒 Files selected for processing (4)
  • src/main/java/org/gridsuite/study/server/controller/StudyController.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/StudyService.java

sBouzols added 6 commits May 22, 2026 17:32
Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
…framework.util.CollectionUtils for consistency across the codebase

Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
Signed-off-by: sBouzols <sylvain.bouzols@gmail.com>
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