Skip to content

Moving Dynamic Simulation params to dynamic-simulation-server#969

Merged
thangqp merged 8 commits intomainfrom
ds_move_params_to_ds_server
Apr 2, 2026
Merged

Moving Dynamic Simulation params to dynamic-simulation-server#969
thangqp merged 8 commits intomainfrom
ds_move_params_to_ds_server

Conversation

@thangqp
Copy link
Copy Markdown
Contributor

@thangqp thangqp commented Mar 20, 2026

PR Summary

thangqp added 2 commits March 20, 2026 09:26
Signed-off-by: Thang PHAM <phamthang37@gmail.com>
Signed-off-by: Thang PHAM <phamthang37@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 381c8756-2cbc-4ca8-80d1-daefb35be0c0

📥 Commits

Reviewing files that changed from the base of the PR and between cc6f307 and 2cac2a3.

📒 Files selected for processing (1)
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java

📝 Walkthrough

Walkthrough

Dynamic-simulation parameters were converted from in-code DTO/entity objects to UUID-referenced serialized JSON. Controllers, services, and clients were updated to use parameter UUIDs and raw JSON strings; many DTOs/entities were removed; new parameter-management APIs and a DB migration were added.

Changes

Cohort / File(s) Summary
Controller
src/main/java/org/gridsuite/study/server/controller/StudyController.java
Endpoints changed to accept/return raw String parameters, removed request-body parameter from run endpoint, and added optional mapping query param to model listing.
Removed DTOs
src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/...
Deleted parameter-related DTOs and helpers: DynamicSimulationParametersInfos, CurveInfos, NetworkInfos, SolverInfos, IdaSolverInfos, SimSolverInfos, AbstractSolverInfos, SolverTypeInfos and their JSON utils.
Repository / Entity
src/main/java/org/gridsuite/study/server/repository/DynamicSimulationParametersEntity.java, src/main/java/org/gridsuite/study/server/repository/StudyEntity.java
Removed DynamicSimulationParametersEntity and provider field; StudyEntity now stores dynamicSimulationParametersUuid (UUID) column instead of one-to-one relation.
Service surface
src/main/java/org/gridsuite/study/server/service/..., .../dynamicsimulation/*
Study/DynamicSimulation services updated to use parameter UUIDs and serialized parameter strings; added create/get/update/duplicate/delete/default parameter methods; run APIs now accept parametersUuid and events.
Clients
src/main/java/org/gridsuite/study/server/service/client/...
DynamicSimulation client: added parameter-management endpoints (getProvider, getParameters, create/update/duplicate/delete/createDefault) and changed run to accept parametersUuid + events. DynamicSecurityAnalysis/DynamicMarginCalculation clients dropped provider param and adjusted queries/bodies. RestClient gained setBaseUri.
Consumer / Other services
src/main/java/org/gridsuite/study/server/service/ConsumerService.java, .../dynamicmargincalculation/*, .../dynamicsecurityanalysis/*
ConsumerService and other services adjusted to create/duplicate/use parameter UUIDs and to call new parameter-management APIs.
URI builder updates
various src/main/java/.../service/client/*Impl.java
Replaced UriComponentsBuilder.fromHttpUrl(...) with fromUriString(...) in multiple client implementations.
DB Migration
src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml, .../db.changelog-master.yaml
Added Liquibase changeset to add dynamic_simulation_parameters_uuid to study, drop old FK/table/columns, and included it in master changelog.
User DTO
src/main/java/org/gridsuite/study/server/dto/UserProfileInfos.java
Added UUID dynamicSimulationParameterId field to store user-specific default parameter reference.
Tests — added/updated
src/test/java/.../service/client/dynamicsimulation/*, .../service/dynamicsimulation/*, .../StudyControllerDynamicSimulationTest.java, .../StudyTestBase.java, .../StudyTest.java
Refactored tests to use JSON strings and parameter UUIDs, added parameter-management tests, tightened Mockito matchers and updated verify counts.
Tests — removed DTO tests
src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/*Test.java
Removed unit tests for deleted DTOs (CurveInfosTest, NetworkInfosTest, SolverInfosTest).
Test utilities
src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/ParameterTestUtils.java
Added helper producing sample EventInfos lists for parameter-related tests.
Misc tests updates
various src/test/java/...
Adjusted spy names/usages and updated mocks/expectations across dynamic-security/margin/simulation tests to reflect signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as StudyController
    participant StudyService
    participant DynamicSimulationService
    participant DynamicSimulationClient
    participant ExtSim as ExternalSimulationAPI

    Client->>Controller: POST /studies/{id}/runDynamicSimulation?parametersUuid={uuid}\nbody: events JSON
    Controller->>StudyService: runDynamicSimulation(studyUuid, nodeUuid, rootNetworkUuid, userId, debug)
    StudyService->>DynamicSimulationService: runDynamicSimulation(nodeUuid, rootNetworkUuid, networkUuid, variantId, reportUuid, parametersUuid, events, userId, debug)
    DynamicSimulationService->>DynamicSimulationClient: run(receiver, networkUuid, variantId, reportInfos, parametersUuid, events, userId, debug)
    DynamicSimulationClient->>ExtSim: POST /api/dynamicsimulation/run?parametersUuid={uuid}\nbody: events
    ExtSim-->>DynamicSimulationClient: 202 Accepted / resultUuid
    DynamicSimulationClient-->>DynamicSimulationService: resultUuid
    DynamicSimulationService-->>StudyService: resultUuid
    StudyService-->>Controller: resultUuid
    Controller-->>Client: 202 Accepted with resultUuid
Loading

Suggested reviewers

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

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is empty, providing no context or explanation for the substantial architectural changes in this pull request. Add a detailed PR description explaining the rationale, impact, and any migration notes for reviewers.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: moving dynamic simulation parameters from the study-server to the dynamic-simulation-server.

✏️ 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.

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: 3

Caution

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

⚠️ Outside diff range comments (3)
src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java (1)

154-162: ⚠️ Potential issue | 🟡 Minor

Add null validation for dynamicSimulationParametersUuid.

The parameter dynamicSimulationParametersUuid is annotated with @NonNull but lacks the corresponding Objects.requireNonNull() validation that other @NonNull parameters in this method have. This inconsistency could lead to less clear error messages if null is accidentally passed.

Proposed fix
     public UUID run(`@NonNull` String receiver, `@NonNull` UUID networkUuid, String variantId,
                     `@NonNull` ReportInfos reportInfos, `@NonNull` UUID dynamicSimulationParametersUuid,
                     `@NonNull` UUID dynamicSecurityAnalysisParametersUuid,
                     `@NonNull` UUID parametersUuid, String userId, boolean debug) {
         Objects.requireNonNull(receiver);
         Objects.requireNonNull(networkUuid);
         Objects.requireNonNull(reportInfos);
+        Objects.requireNonNull(dynamicSimulationParametersUuid);
         Objects.requireNonNull(dynamicSecurityAnalysisParametersUuid);
         Objects.requireNonNull(parametersUuid);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
around lines 154 - 162, The method run in DynamicMarginCalculationClient is
missing a null-check for the `@NonNull` parameter dynamicSimulationParametersUuid;
add an Objects.requireNonNull(dynamicSimulationParametersUuid) call in the same
validation block where receiver, networkUuid, reportInfos,
dynamicSecurityAnalysisParametersUuid, and parametersUuid are checked so the
method consistently validates all `@NonNull` parameters and throws a clear NPE if
dynamicSimulationParametersUuid is null.
src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java (2)

306-321: ⚠️ Potential issue | 🟡 Minor

WireMock stub missing dynamicSimulationParametersUuid query parameter validation.

The stub validates dynamicSecurityAnalysisParametersUuid and parametersUuid but omits validation for dynamicSimulationParametersUuid. According to the production code, this parameter is sent as a query parameter (.queryParam("dynamicSimulationParametersUuid", dynamicSimulationParametersUuid)). The stub will accept any value for this parameter, reducing test coverage.

🐛 Proposed fix to add missing query param validation
 wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
         .withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo("variantId"))
+        .withQueryParam("dynamicSimulationParametersUuid", equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString()))
         .withQueryParam("dynamicSecurityAnalysisParametersUuid", equalTo(DYNAMIC_SECURITY_ANALYSIS_PARAMETERS_UUID.toString()))
         .withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java`
around lines 306 - 321, The WireMock stub in DynamicMarginCalculationClientTest
is missing validation for the dynamicSimulationParametersUuid query parameter;
update the stub chain (the WireMock.post(...).withQueryParam(...) calls) to
include .withQueryParam("dynamicSimulationParametersUuid",
equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString())) so the stub matches the
production call made by dynamicMarginCalculationClient.run and uses the existing
DYNAMIC_SIMULATION_PARAMETERS_UUID test constant.

327-345: ⚠️ Potential issue | 🟡 Minor

Error case WireMock stub also missing dynamicSimulationParametersUuid validation.

Same issue as the success case - the error stub should validate the dynamicSimulationParametersUuid query parameter for consistency.

🐛 Proposed fix
 wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
         .withQueryParam(QUERY_PARAM_VARIANT_ID, absent())
         .withQueryParam("provider", absent())
+        .withQueryParam("dynamicSimulationParametersUuid", equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString()))
         .withQueryParam("dynamicSecurityAnalysisParametersUuid", equalTo(DYNAMIC_SECURITY_ANALYSIS_PARAMETERS_UUID.toString()))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java`
around lines 327 - 345, The WireMock error stub in
DynamicMarginCalculationClientTest is missing validation for the
dynamicSimulationParametersUuid query param; update the stub configured via
wireMockServer.stubFor(...) (same block that uses url, QUERY_PARAM_VARIANT_ID,
QUERY_PARAM_RECEIVER, etc.) to include
.withQueryParam("dynamicSimulationParametersUuid",
equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString())) so the error case
validates the dynamicSimulationParametersUuid the same way the success case
does.
🧹 Nitpick comments (4)
src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml (1)

6-7: Minor: Inconsistent table name casing.

The changeset uses lowercase study on line 6 but uppercase STUDY on lines 11, 14, and 17. While Liquibase and most databases handle this gracefully, consistent casing improves readability.

Suggested fix for consistency
     <changeSet author="phamquy (generated)" id="1773945844517-17">
-        <dropForeignKeyConstraint baseTableName="STUDY" constraintName="DYNAMICSIMULATIONPARAMETERS_ID_FK"/>
+        <dropForeignKeyConstraint baseTableName="study" constraintName="DYNAMICSIMULATIONPARAMETERS_ID_FK"/>
     </changeSet>
     <changeSet author="phamquy (generated)" id="1773945844517-18">
-        <dropTable tableName="DYNAMIC_SIMULATION_PARAMETERS"/>
+        <dropTable tableName="dynamic_simulation_parameters"/>
     </changeSet>
     <changeSet author="phamquy (generated)" id="1773945844517-19">
-        <dropColumn columnName="DYNAMIC_SIMULATION_PARAMETERS_ENTITY_ID" tableName="STUDY"/>
+        <dropColumn columnName="dynamic_simulation_parameters_entity_id" tableName="study"/>
     </changeSet>
     <changeSet author="phamquy (generated)" id="1773945844517-20">
-        <dropColumn columnName="DYNAMIC_SIMULATION_PROVIDER" tableName="STUDY"/>
+        <dropColumn columnName="dynamic_simulation_provider" tableName="study"/>
     </changeSet>

Also applies to: 11-11, 14-14, 17-17

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

In `@src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml`
around lines 6 - 7, The XML changeset has inconsistent tableName casing: some
tags use tableName="study" and others tableName="STUDY"; update all tableName
attributes in this changeset to use a single consistent casing (pick one
convention—prefer lowercase "study") for every change element (e.g., the
<addColumn tableName="study"> and the other change elements referencing STUDY)
so all occurrences match exactly for readability and consistency.
src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClient.java (1)

31-45: Consider consistent @NonNull annotations across similar parameter methods.

getProvider uses @NonNull UUID parametersUuid but getParameters, updateParameters, duplicateParameters, and deleteParameters don't annotate their UUID parameters. Consider applying @NonNull consistently for uniform null-safety contracts.

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

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClient.java`
around lines 31 - 45, The UUID parameter null-safety is inconsistent: add
`@NonNull` to the UUID parameters of getParameters(UUID),
updateParameters(UUID,...), duplicateParameters(UUID) and deleteParameters(UUID)
so their signatures match getProvider(`@NonNull` UUID) and enforce a uniform
contract; keep existing annotations on createParameters(String) and leave userId
as-is unless you want it non-null too.
src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java (1)

104-105: Use a real JSON payload and verify it is forwarded unchanged.

This endpoint now persists raw JSON strings. With PARAMETERS_JSON = "parametersJson" and createParameters(any()), the test does not exercise the real payload shape and won’t catch body mangling on the controller path.

🧪 Strengthen the round-trip assertion
-    private static final String PARAMETERS_JSON = "parametersJson";
+    private static final String PARAMETERS_JSON = "{\"startTime\":0,\"stopTime\":500}";
@@
-        doAnswer(invocation -> PARAMETERS_UUID)
-                .when(spyDynamicSimulationService).createParameters(any());
+        doAnswer(invocation -> PARAMETERS_UUID)
+                .when(spyDynamicSimulationService).createParameters(eq(jsonParameters));
@@
         studyClient.perform(post(STUDY_BASE_URL + DELIMITER + STUDY_DYNAMIC_SIMULATION_END_POINT_PARAMETERS, studyUuid)
                         .header(HEADER_USER_ID_NAME, HEADER_USER_ID_VALUE)
                         .contentType(MediaType.APPLICATION_JSON)
                         .content(jsonParameters))
                     .andExpect(status().isOk()).andReturn();
+        verify(spyDynamicSimulationService).createParameters(eq(jsonParameters));

Also applies to: 693-706

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

In
`@src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java`
around lines 104 - 105, Replace the placeholder PARAMETERS_JSON usage and the
generic createParameters(any()) stub in StudyControllerDynamicSimulationTest
with a real JSON string (e.g. a small nested object/array) and use that exact
string in the request; then capture the argument passed to the mocked
createParameters (via ArgumentCaptor or Mockito.verify) and assert the captured
value equals the original JSON string so the controller forwards the raw body
unchanged (reference PARAMETERS_JSON, createParameters(...), and the test method
in StudyControllerDynamicSimulationTest).
src/main/java/org/gridsuite/study/server/controller/StudyController.java (1)

1834-1837: Add explicit JSON media type to DS parameters GET endpoint.

The endpoint returns ResponseEntity<String> without explicit produces declaration, allowing content-type negotiation to default to plain text. Pinning MediaType.APPLICATION_JSON_VALUE ensures client-side content-type safety and aligns with similar endpoints in the controller that already declare explicit media types.

Note: Similar parameter GET endpoints across the controller (voltage-init, loadflow, dynamic-security-analysis, security-analysis, etc.) also lack explicit media-type declarations—consider a broader pass to standardize this pattern.

♻️ Proposed change
-    `@GetMapping`(value = "/studies/{studyUuid}/dynamic-simulation/parameters")
+    `@GetMapping`(value = "/studies/{studyUuid}/dynamic-simulation/parameters", produces = MediaType.APPLICATION_JSON_VALUE)
     `@Operation`(summary = "Get dynamic simulation parameters on study")
     `@ApiResponses`(value = {`@ApiResponse`(responseCode = "200", description = "The dynamic simulation parameters")})
     public ResponseEntity<String> getDynamicSimulationParameters(
             `@PathVariable`("studyUuid") UUID studyUuid,
             `@RequestHeader`(HEADER_USER_ID) String userId) {
-        return ResponseEntity.ok().body(studyService.getDynamicSimulationParameters(studyUuid, userId));
+        return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
+                .body(studyService.getDynamicSimulationParameters(studyUuid, userId));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`
around lines 1834 - 1837, The GET endpoint method getDynamicSimulationParameters
currently returns ResponseEntity<String> without an explicit produces media
type; update its mapping annotation (the `@GetMapping/`@RequestMapping for
getDynamicSimulationParameters) to include produces =
MediaType.APPLICATION_JSON_VALUE so the response Content-Type is pinned to
application/json and matches other parameter endpoints; apply the same pattern
for other parameter GET endpoints (e.g., voltage-init, loadflow,
dynamic-security-analysis, security-analysis) as a follow-up pass to standardize
content negotiation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/study/server/service/StudyService.java`:
- Around line 3006-3018: Replace the direct call to
studyEntity.getDynamicSimulationParametersUuid() used as an argument to
dynamicSimulationService.runDynamicSimulation with the defensive helper to
guarantee a non-null UUID: call
getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity) and pass its
result into runDynamicSimulation; update the reference near where
dynamicSimulationResultUuid is created so runDynamicSimulation receives the
non-null UUID instead of the raw
studyEntity.getDynamicSimulationParametersUuid() value.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java`:
- Around line 127-157: Update the WireMock stubs in DynamicSimulationClientTest
so they assert the forwarded HEADER_USER_ID header is present and equals the
expected value; specifically, for each
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url)) ...) (the stubs
used for the success, not-found, and server-error cases) add a header matcher
that checks HEADER_USER_ID equals "userId" (or the test's userId variable) so
the stubs only match when DynamicSimulationClientImpl.getParameters(...)
actually forwards the header.
- Around line 316-352: The test's error path is a false positive because the
WireMock error stub incorrectly uses withHeader(QUERY_PARAM_DEBUG, ...) instead
of withQueryParam(QUERY_PARAM_DEBUG, ...) and doesn't verify the request body
(`events`), so WireMock falls back to 404; update both error stubs to use
withQueryParam("debug", equalTo("true")) (matching the real client behavior) and
add a request body matcher that verifies `events` (e.g.
withRequestBody(equalToJson(objectMapper.writeValueAsString(events)))); finally
change the asserted exception from HttpClientErrorException.NotFound.class to
HttpServerErrorException.class when calling dynamicSimulationClient.run(...) so
the test expects the 500 server error.

---

Outside diff comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`:
- Around line 154-162: The method run in DynamicMarginCalculationClient is
missing a null-check for the `@NonNull` parameter dynamicSimulationParametersUuid;
add an Objects.requireNonNull(dynamicSimulationParametersUuid) call in the same
validation block where receiver, networkUuid, reportInfos,
dynamicSecurityAnalysisParametersUuid, and parametersUuid are checked so the
method consistently validates all `@NonNull` parameters and throws a clear NPE if
dynamicSimulationParametersUuid is null.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java`:
- Around line 306-321: The WireMock stub in DynamicMarginCalculationClientTest
is missing validation for the dynamicSimulationParametersUuid query parameter;
update the stub chain (the WireMock.post(...).withQueryParam(...) calls) to
include .withQueryParam("dynamicSimulationParametersUuid",
equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString())) so the stub matches the
production call made by dynamicMarginCalculationClient.run and uses the existing
DYNAMIC_SIMULATION_PARAMETERS_UUID test constant.
- Around line 327-345: The WireMock error stub in
DynamicMarginCalculationClientTest is missing validation for the
dynamicSimulationParametersUuid query param; update the stub configured via
wireMockServer.stubFor(...) (same block that uses url, QUERY_PARAM_VARIANT_ID,
QUERY_PARAM_RECEIVER, etc.) to include
.withQueryParam("dynamicSimulationParametersUuid",
equalTo(DYNAMIC_SIMULATION_PARAMETERS_UUID.toString())) so the error case
validates the dynamicSimulationParametersUuid the same way the success case
does.

---

Nitpick comments:
In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`:
- Around line 1834-1837: The GET endpoint method getDynamicSimulationParameters
currently returns ResponseEntity<String> without an explicit produces media
type; update its mapping annotation (the `@GetMapping/`@RequestMapping for
getDynamicSimulationParameters) to include produces =
MediaType.APPLICATION_JSON_VALUE so the response Content-Type is pinned to
application/json and matches other parameter endpoints; apply the same pattern
for other parameter GET endpoints (e.g., voltage-init, loadflow,
dynamic-security-analysis, security-analysis) as a follow-up pass to standardize
content negotiation.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClient.java`:
- Around line 31-45: The UUID parameter null-safety is inconsistent: add
`@NonNull` to the UUID parameters of getParameters(UUID),
updateParameters(UUID,...), duplicateParameters(UUID) and deleteParameters(UUID)
so their signatures match getProvider(`@NonNull` UUID) and enforce a uniform
contract; keep existing annotations on createParameters(String) and leave userId
as-is unless you want it non-null too.

In `@src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml`:
- Around line 6-7: The XML changeset has inconsistent tableName casing: some
tags use tableName="study" and others tableName="STUDY"; update all tableName
attributes in this changeset to use a single consistent casing (pick one
convention—prefer lowercase "study") for every change element (e.g., the
<addColumn tableName="study"> and the other change elements referencing STUDY)
so all occurrences match exactly for readability and consistency.

In
`@src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java`:
- Around line 104-105: Replace the placeholder PARAMETERS_JSON usage and the
generic createParameters(any()) stub in StudyControllerDynamicSimulationTest
with a real JSON string (e.g. a small nested object/array) and use that exact
string in the request; then capture the argument passed to the mocked
createParameters (via ArgumentCaptor or Mockito.verify) and assert the captured
value equals the original JSON string so the controller forwards the raw body
unchanged (reference PARAMETERS_JSON, createParameters(...), and the test method
in StudyControllerDynamicSimulationTest).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c70644d-b3cf-4168-8cd8-2d1ec27f4338

📥 Commits

Reviewing files that changed from the base of the PR and between b41b0b8 and 897204b.

📒 Files selected for processing (44)
  • src/main/java/org/gridsuite/study/server/controller/StudyController.java
  • src/main/java/org/gridsuite/study/server/dto/UserProfileInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/DynamicSimulationParametersInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/curve/CurveInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/network/NetworkInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/AbstractSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/IdaSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SimSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverTypeInfos.java
  • src/main/java/org/gridsuite/study/server/repository/DynamicSimulationParametersEntity.java
  • src/main/java/org/gridsuite/study/server/repository/StudyEntity.java
  • src/main/java/org/gridsuite/study/server/service/ConsumerService.java
  • src/main/java/org/gridsuite/study/server/service/FilterService.java
  • src/main/java/org/gridsuite/study/server/service/StudyService.java
  • src/main/java/org/gridsuite/study/server/service/client/RestClient.java
  • src/main/java/org/gridsuite/study/server/service/client/dynamicmapping/impl/DynamicMappingClientImpl.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/client/timeseries/impl/TimeSeriesClientImpl.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/resources/db/changelog/changesets/changelog_20260319T184344Z.xml
  • src/main/resources/db/changelog/db.changelog-master.yaml
  • src/test/java/org/gridsuite/study/server/StudyControllerDynamicMarginCalculationTest.java
  • src/test/java/org/gridsuite/study/server/StudyControllerDynamicSecurityAnalysisTest.java
  • src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/curve/CurveInfosTest.java
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/network/NetworkInfosTest.java
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverInfosTest.java
  • src/test/java/org/gridsuite/study/server/service/StudyServiceDynamicSimulationTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClientTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClientTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/ParameterTestUtils.java
  • src/test/java/org/gridsuite/study/server/service/dynamicmargincalculation/DynamicMarginCalculationServiceTest.java
  • src/test/java/org/gridsuite/study/server/service/dynamicsecurityanalysis/DynamicSecurityAnalysisServiceTest.java
  • src/test/java/org/gridsuite/study/server/service/dynamicsimulation/DynamicSimulationServiceTest.java
  • src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java
  • src/test/java/org/gridsuite/study/server/studycontroller/StudyTestBase.java
💤 Files with no reviewable changes (12)
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/curve/CurveInfosTest.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverTypeInfos.java
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/network/NetworkInfosTest.java
  • src/test/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverInfosTest.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/curve/CurveInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/AbstractSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/IdaSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SolverInfos.java
  • src/main/java/org/gridsuite/study/server/repository/DynamicSimulationParametersEntity.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/solver/SimSolverInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/DynamicSimulationParametersInfos.java
  • src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/network/NetworkInfos.java

Comment on lines +3006 to +3018
// get dynamic simulation result uuid
UUID dynamicSimulationParametersUuid = studyEntity.getDynamicSimulationParametersUuid();

// load configured events persisted in the study server DB
List<EventInfos> events = dynamicSimulationEventService.getEventsByNodeId(nodeUuid);

// override configured parameters by provided parameters (only provided fields)
DynamicSimulationParametersInfos mergeParameters = new DynamicSimulationParametersInfos();
// attach events to the merged parameters
mergeParameters.setEvents(events);

if (configuredParameters != null) {
PropertyUtils.copyNonNullProperties(configuredParameters, mergeParameters);
}
if (parameters != null) {
PropertyUtils.copyNonNullProperties(parameters, mergeParameters);
}
UUID reportUuid = networkModificationTreeService.getComputationReports(nodeUuid, rootNetworkUuid).getOrDefault(DYNAMIC_SIMULATION.name(), UUID.randomUUID());
networkModificationTreeService.updateComputationReportUuid(nodeUuid, rootNetworkUuid, DYNAMIC_SIMULATION, reportUuid);

// launch dynamic simulation
UUID networkUuid = rootNetworkService.getNetworkUuid(rootNetworkUuid);
String variantId = networkModificationTreeService.getVariantId(nodeUuid, rootNetworkUuid);
UUID dynamicSimulationResultUuid = dynamicSimulationService.runDynamicSimulation(studyEntity.getDynamicSimulationProvider(),
nodeUuid, rootNetworkUuid, networkUuid, variantId, reportUuid, mergeParameters, userId, debug);
UUID dynamicSimulationResultUuid = dynamicSimulationService.runDynamicSimulation(nodeUuid, rootNetworkUuid, networkUuid, variantId, reportUuid, dynamicSimulationParametersUuid, events, userId, debug);
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how null parametersUuid is handled in DynamicSimulationService.runDynamicSimulation
ast-grep --pattern $'runDynamicSimulation($$$) {
  $$$
}'

# Check if there are null checks or default creation in the service
rg -n -A 10 "runDynamicSimulation" --type java -g '*DynamicSimulationService*.java'

Repository: gridsuite/study-server

Length of output: 5191


🏁 Script executed:

#!/bin/bash
# Get the full implementation of runDynamicSimulation
sed -n '114,200p' src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java

# Also search for any null checks on parametersUuid
rg -n "parametersUuid" --type java -A 5 -B 5 src/main/java/org/gridsuite/study/server/service/dynamicsimulation/

Repository: gridsuite/study-server

Length of output: 19174


Use defensive pattern for dynamic simulation parameters UUID.

studyEntity.getDynamicSimulationParametersUuid() is passed directly to runDynamicSimulation without null handling. Replace with getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity) (lines 105-110 of DynamicSimulationServiceImpl) to ensure a non-null UUID is passed, matching the defensive pattern used elsewhere in the codebase.

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

In `@src/main/java/org/gridsuite/study/server/service/StudyService.java` around
lines 3006 - 3018, Replace the direct call to
studyEntity.getDynamicSimulationParametersUuid() used as an argument to
dynamicSimulationService.runDynamicSimulation with the defensive helper to
guarantee a non-null UUID: call
getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity) and pass its
result into runDynamicSimulation; update the reference near where
dynamicSimulationResultUuid is created so runDynamicSimulation receives the
non-null UUID instead of the raw
studyEntity.getDynamicSimulationParametersUuid() value.

Comment on lines +127 to +157
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.ok()
.withBody(parametersJson)
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));
// call service to test
String resultParametersJson = dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId");

// check result
assertThat(resultParametersJson).isEqualTo(parametersJson);

// --- Not Found --- //
// configure mock server response
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.notFound()));

// check result
assertThrows(
HttpClientErrorException.NotFound.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);

// --- Error --- //
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.serverError()));

// check result
assertThrows(
HttpServerErrorException.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);
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 | 🟡 Minor

Assert the forwarded userId header in this test.

DynamicSimulationClientImpl.getParameters(...) now conditionally sends HEADER_USER_ID, but these stubs match even when that header is missing. If header forwarding regresses, all three branches still stay green.

🔧 Tighten the WireMock expectations
-        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+                .withHeader(HEADER_USER_ID, equalTo("userId"))
                 .willReturn(WireMock.ok()
                         .withBody(parametersJson)
                         .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                 ));
@@
-        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+                .withHeader(HEADER_USER_ID, equalTo("userId"))
                 .willReturn(WireMock.notFound()));
@@
-        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+        wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
+                .withHeader(HEADER_USER_ID, equalTo("userId"))
                 .willReturn(WireMock.serverError()));
📝 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
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.ok()
.withBody(parametersJson)
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));
// call service to test
String resultParametersJson = dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId");
// check result
assertThat(resultParametersJson).isEqualTo(parametersJson);
// --- Not Found --- //
// configure mock server response
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.notFound()));
// check result
assertThrows(
HttpClientErrorException.NotFound.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);
// --- Error --- //
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.willReturn(WireMock.serverError()));
// check result
assertThrows(
HttpServerErrorException.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.ok()
.withBody(parametersJson)
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));
// call service to test
String resultParametersJson = dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId");
// check result
assertThat(resultParametersJson).isEqualTo(parametersJson);
// --- Not Found --- //
// configure mock server response
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.notFound()));
// check result
assertThrows(
HttpClientErrorException.NotFound.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);
// --- Error --- //
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.serverError()));
// check result
assertThrows(
HttpServerErrorException.class,
() -> dynamicSimulationClient.getParameters(PARAMETERS_UUID, "userId")
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java`
around lines 127 - 157, Update the WireMock stubs in DynamicSimulationClientTest
so they assert the forwarded HEADER_USER_ID header is present and equals the
expected value; specifically, for each
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo(url)) ...) (the stubs
used for the success, not-found, and server-error cases) add a header matcher
that checks HEADER_USER_ID equals "userId" (or the test's userId variable) so
the stubs only match when DynamicSimulationClientImpl.getParameters(...)
actually forwards the header.

Comment on lines 316 to 352
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo(VARIANT_ID))
.withQueryParam("provider", equalTo(DYNAWO_PROVIDER))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.ok()
.withBody(objectMapper.writeValueAsString(RESULT_UUID))
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));

// call service to test
UUID resultUuid = dynamicSimulationClient.run(DYNAWO_PROVIDER, "receiver", NETWORK_UUID, VARIANT_ID,
new ReportInfos(REPORT_UUID, NODE_UUID), parameters, "userId", false);
UUID resultUuid = dynamicSimulationClient.run("receiver", NETWORK_UUID, VARIANT_ID,
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", false);

// check result
assertThat(resultUuid).isEqualTo(RESULT_UUID);

// --- Error --- //
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo("variantIdFailed"))
.withQueryParam("provider", equalTo("Dynawo"))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withHeader(QUERY_PARAM_DEBUG, equalTo("true"))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.serverError()));

// check result
assertThrows(
HttpClientErrorException.NotFound.class,
() -> dynamicSimulationClient.run("Dynawo", "receiver", NETWORK_UUID, "variantIdFailed",
new ReportInfos(REPORT_UUID, NODE_UUID), parameters, "userId", true)
() -> dynamicSimulationClient.run("receiver", NETWORK_UUID, "variantIdFailed",
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", true)
);
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Implementation contract:"
rg -n -C2 'queryParam\(QUERY_PARAM_DEBUG|HttpEntity<List<EventInfos>>|postForObject' \
  src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java

echo
echo "Current test matchers:"
rg -n -C2 'testRun\(|withHeader\(QUERY_PARAM_DEBUG|withQueryParam\(QUERY_PARAM_DEBUG|withRequestBody' \
  src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java

Repository: gridsuite/study-server

Length of output: 2874


Fix the run error path—test is a false positive.

The implementation sends debug as a query parameter and events as the request body, but the error stub checks debug as a header and ignores the body. WireMock's fallback 404 satisfies the test instead of the intended 500 path. Additionally, the exception type is incorrect: HttpServerErrorException.class not HttpClientErrorException.NotFound.class.

Apply these fixes to both stubs
         wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
+                .withRequestBody(WireMock.equalToJson(objectMapper.writeValueAsString(events)))
                 .withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo(VARIANT_ID))
                 .withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
                 .withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
                 .withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
                 .withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
                 .withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
                 .withHeader(HEADER_USER_ID, equalTo("userId"))
                 .willReturn(WireMock.ok()
                         .withBody(objectMapper.writeValueAsString(RESULT_UUID))
                         .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                 ));

         wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
                 .withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo("variantIdFailed"))
                 .withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
                 .withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
                 .withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
                 .withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
-                .withHeader(QUERY_PARAM_DEBUG, equalTo("true"))
+                .withQueryParam(QUERY_PARAM_DEBUG, equalTo("true"))
+                .withRequestBody(WireMock.equalToJson(objectMapper.writeValueAsString(events)))
                 .withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
                 .withHeader(HEADER_USER_ID, equalTo("userId"))
                 .willReturn(WireMock.serverError()));

-        assertThrows(
-            HttpClientErrorException.NotFound.class,
+        assertThrows(
+            HttpServerErrorException.class,
             () -> dynamicSimulationClient.run("receiver", NETWORK_UUID, "variantIdFailed",
                 new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", true)
         );
📝 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
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo(VARIANT_ID))
.withQueryParam("provider", equalTo(DYNAWO_PROVIDER))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.ok()
.withBody(objectMapper.writeValueAsString(RESULT_UUID))
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));
// call service to test
UUID resultUuid = dynamicSimulationClient.run(DYNAWO_PROVIDER, "receiver", NETWORK_UUID, VARIANT_ID,
new ReportInfos(REPORT_UUID, NODE_UUID), parameters, "userId", false);
UUID resultUuid = dynamicSimulationClient.run("receiver", NETWORK_UUID, VARIANT_ID,
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", false);
// check result
assertThat(resultUuid).isEqualTo(RESULT_UUID);
// --- Error --- //
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo("variantIdFailed"))
.withQueryParam("provider", equalTo("Dynawo"))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withHeader(QUERY_PARAM_DEBUG, equalTo("true"))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.serverError()));
// check result
assertThrows(
HttpClientErrorException.NotFound.class,
() -> dynamicSimulationClient.run("Dynawo", "receiver", NETWORK_UUID, "variantIdFailed",
new ReportInfos(REPORT_UUID, NODE_UUID), parameters, "userId", true)
() -> dynamicSimulationClient.run("receiver", NETWORK_UUID, "variantIdFailed",
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", true)
);
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withRequestBody(WireMock.equalToJson(objectMapper.writeValueAsString(events)))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo(VARIANT_ID))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.ok()
.withBody(objectMapper.writeValueAsString(RESULT_UUID))
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
));
// call service to test
UUID resultUuid = dynamicSimulationClient.run("receiver", NETWORK_UUID, VARIANT_ID,
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", false);
// check result
assertThat(resultUuid).isEqualTo(RESULT_UUID);
// --- Error --- //
wireMockServer.stubFor(WireMock.post(WireMock.urlPathTemplate(url))
.withQueryParam(QUERY_PARAM_VARIANT_ID, equalTo("variantIdFailed"))
.withQueryParam(QUERY_PARAM_RECEIVER, equalTo("receiver"))
.withQueryParam(QUERY_PARAM_REPORT_UUID, equalTo(REPORT_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORTER_ID, equalTo(NODE_UUID.toString()))
.withQueryParam(QUERY_PARAM_REPORT_TYPE, equalTo(StudyService.ReportType.DYNAMIC_SIMULATION.reportKey))
.withQueryParam(QUERY_PARAM_DEBUG, equalTo("true"))
.withRequestBody(WireMock.equalToJson(objectMapper.writeValueAsString(events)))
.withQueryParam("parametersUuid", equalTo(PARAMETERS_UUID.toString()))
.withHeader(HEADER_USER_ID, equalTo("userId"))
.willReturn(WireMock.serverError()));
// check result
assertThrows(
HttpServerErrorException.class,
() -> dynamicSimulationClient.run("receiver", NETWORK_UUID, "variantIdFailed",
new ReportInfos(REPORT_UUID, NODE_UUID), PARAMETERS_UUID, events, "userId", true)
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java`
around lines 316 - 352, The test's error path is a false positive because the
WireMock error stub incorrectly uses withHeader(QUERY_PARAM_DEBUG, ...) instead
of withQueryParam(QUERY_PARAM_DEBUG, ...) and doesn't verify the request body
(`events`), so WireMock falls back to 404; update both error stubs to use
withQueryParam("debug", equalTo("true")) (matching the real client behavior) and
add a request body matcher that verifies `events` (e.g.
withRequestBody(equalToJson(objectMapper.writeValueAsString(events)))); finally
change the asserted exception from HttpClientErrorException.NotFound.class to
HttpServerErrorException.class when calling dynamicSimulationClient.run(...) so
the test expects the 500 server error.

Signed-off-by: Thang PHAM <phamthang37@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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/org/gridsuite/study/server/controller/StudyController.java (1)

1905-1916: ⚠️ Potential issue | 🟠 Major

Fail fast on stale bodies for /dynamic-simulation/run.

This keeps the same public route, but it no longer consumes per-run parameters. The updated tests still POST a body and pass, which means older callers will silently lose their overrides instead of getting a clear contract error. Either keep backward compatibility for one release or reject non-empty bodies with 400.

💡 One way to make the contract break explicit
 public ResponseEntity<Void> runDynamicSimulation(`@Parameter`(description = "studyUuid") `@PathVariable`("studyUuid") UUID studyUuid,
                                                  `@Parameter`(description = "rootNetworkUuid") `@PathVariable`("rootNetworkUuid") UUID rootNetworkUuid,
                                                  `@Parameter`(description = "nodeUuid") `@PathVariable`("nodeUuid") UUID nodeUuid,
                                                  `@Parameter`(description = "debug") `@RequestParam`(name = "debug", required = false, defaultValue = "false") boolean debug,
+                                                 `@RequestBody`(required = false) String deprecatedParameters,
                                                  `@RequestHeader`(HEADER_USER_ID) String userId) {
+    if (StringUtils.isNotBlank(deprecatedParameters)) {
+        throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
+                "Dynamic-simulation parameters must be set through the parameters endpoint before running");
+    }
     studyService.assertIsNodeNotReadOnly(nodeUuid);
     studyService.assertCanRunOnConstructionNode(studyUuid, nodeUuid, List.of(DYNAWO_PROVIDER), studyService::getDynamicSimulationProvider);
     studyService.runDynamicSimulation(studyUuid, nodeUuid, rootNetworkUuid, userId, debug);
     return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).build();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`
around lines 1905 - 1916, The runDynamicSimulation endpoint currently ignores
request bodies which lets old callers silently lose overrides; modify the
StudyController.runDynamicSimulation method to fail-fast on non-empty bodies by
adding a `@RequestBody`(required = false) String body parameter and, at the start
of the method, if body != null && !body.isBlank() throw new
ResponseStatusException(HttpStatus.BAD_REQUEST, "Request body not supported for
this endpoint"); keep the existing calls to
studyService.assertIsNodeNotReadOnly, assertCanRunOnConstructionNode and
studyService.runDynamicSimulation unchanged so callers get a clear 400 when they
send a body rather than silently dropping parameters.
🧹 Nitpick comments (1)
src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java (1)

104-105: Use a real JSON fixture in the parameter round-trip test.

"parametersJson" is plain text, not serialized JSON. Because the production path now treats these parameters as JSON payloads, this test won't catch regressions in JSON validation/serialization. Replacing it with an actual JSON object string would make the new contract much better covered.

Also applies to: 689-723

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

In
`@src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java`
around lines 104 - 105, The test uses the constant PARAMETERS_JSON
("parametersJson") which is plain text rather than a serialized JSON payload, so
update the fixture in StudyControllerDynamicSimulationTest to use a real JSON
string (e.g., a JSON object with expected fields) for the parameter round-trip
assertions; replace occurrences of PARAMETERS_JSON in the test (and the other
instances noted around lines 689-723) with a properly escaped JSON object string
that matches the production contract, ensuring the test sends/validates the same
JSON structure the controller expects.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java`:
- Around line 105-110: The method
getDynamicSimulationParametersUuidOrElseCreateDefault in
DynamicSimulationServiceImpl must not perform a remote create during a read;
remove the call to dynamicSimulationClient.createDefaultParameters from that
getter and make it purely a read (return the UUID or null). Implement a separate
initialization/migration routine (e.g.,
initializeDynamicSimulationParametersIfMissing) that is invoked from
write/upgrade flows (or from StudyService during a controlled migration path)
and runs under a DB transaction or explicit lock to create the remote default
once, persist the returned UUID into the StudyEntity, and handle retry/race
conditions; ensure StudyService.getDynamicSimulationParameters calls the new
transactional initializer when migration is required rather than letting the
getter POST remotely.

---

Outside diff comments:
In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`:
- Around line 1905-1916: The runDynamicSimulation endpoint currently ignores
request bodies which lets old callers silently lose overrides; modify the
StudyController.runDynamicSimulation method to fail-fast on non-empty bodies by
adding a `@RequestBody`(required = false) String body parameter and, at the start
of the method, if body != null && !body.isBlank() throw new
ResponseStatusException(HttpStatus.BAD_REQUEST, "Request body not supported for
this endpoint"); keep the existing calls to
studyService.assertIsNodeNotReadOnly, assertCanRunOnConstructionNode and
studyService.runDynamicSimulation unchanged so callers get a clear 400 when they
send a body rather than silently dropping parameters.

---

Nitpick comments:
In
`@src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java`:
- Around line 104-105: The test uses the constant PARAMETERS_JSON
("parametersJson") which is plain text rather than a serialized JSON payload, so
update the fixture in StudyControllerDynamicSimulationTest to use a real JSON
string (e.g., a JSON object with expected fields) for the parameter round-trip
assertions; replace occurrences of PARAMETERS_JSON in the test (and the other
instances noted around lines 689-723) with a properly escaped JSON object string
that matches the production contract, ensuring the test sends/validates the same
JSON structure the controller expects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ffbbecc6-76be-4e84-bf10-058867204a93

📥 Commits

Reviewing files that changed from the base of the PR and between 897204b and c9c5686.

📒 Files selected for processing (10)
  • src/main/java/org/gridsuite/study/server/controller/StudyController.java
  • src/main/java/org/gridsuite/study/server/service/StudyService.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/dynamicsimulation/DynamicSimulationService.java
  • src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java
  • src/test/java/org/gridsuite/study/server/StudyControllerDynamicSimulationTest.java
  • src/test/java/org/gridsuite/study/server/service/StudyServiceDynamicSimulationTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java
  • src/test/java/org/gridsuite/study/server/service/dynamicsimulation/DynamicSimulationServiceTest.java
✅ Files skipped from review due to trivial changes (1)
  • src/test/java/org/gridsuite/study/server/service/dynamicsimulation/DynamicSimulationServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/test/java/org/gridsuite/study/server/service/StudyServiceDynamicSimulationTest.java
  • src/test/java/org/gridsuite/study/server/service/client/dynamicsimulation/DynamicSimulationClientTest.java
  • src/main/java/org/gridsuite/study/server/service/StudyService.java

Comment on lines +105 to +110
public UUID getDynamicSimulationParametersUuidOrElseCreateDefault(StudyEntity studyEntity) {
if (studyEntity.getDynamicSimulationParametersUuid() == null) {
// not supposed to happen because we create it as the study creation
studyEntity.setDynamicSimulationParametersUuid(dynamicSimulationClient.createDefaultParameters());
}
return studyEntity.getDynamicSimulationParametersUuid();
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

Don't lazily create remote default parameters from read paths.

getDynamicSimulationParametersUuidOrElseCreateDefault(...) performs a remote create when the UUID is null. StudyService.getDynamicSimulationParameters(...) already calls this helper from a GET flow (src/main/java/org/gridsuite/study/server/service/StudyService.java:2871-2876), so the first read on an older study now mutates remote state. This is also a check-then-act race: two concurrent requests can each create a default parameter set and only one UUID is retained locally. Please initialize/migrate this UUID once under a transaction/lock instead of opportunistically POSTing here.

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

In
`@src/main/java/org/gridsuite/study/server/service/dynamicsimulation/impl/DynamicSimulationServiceImpl.java`
around lines 105 - 110, The method
getDynamicSimulationParametersUuidOrElseCreateDefault in
DynamicSimulationServiceImpl must not perform a remote create during a read;
remove the call to dynamicSimulationClient.createDefaultParameters from that
getter and make it purely a read (return the UUID or null). Implement a separate
initialization/migration routine (e.g.,
initializeDynamicSimulationParametersIfMissing) that is invoked from
write/upgrade flows (or from StudyService during a controlled migration path)
and runs under a DB transaction or explicit lock to create the remote default
once, persist the returned UUID into the StudyEntity, and handle retry/race
conditions; ensure StudyService.getDynamicSimulationParameters calls the new
transactional initializer when migration is required rather than letting the
getter POST remotely.

thangqp and others added 2 commits March 31, 2026 10:50
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: 3

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

3040-3052: ⚠️ Potential issue | 🟠 Major

Materialize the DS parameters UUID before the first run.

This still forwards studyEntity.getDynamicSimulationParametersUuid() directly to runDynamicSimulation(...), even though the getter path already uses getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity). A fresh study can therefore fail on its first DS execution unless the params endpoint has been hit first.

Suggested fix
-        UUID dynamicSimulationParametersUuid = studyEntity.getDynamicSimulationParametersUuid();
+        UUID dynamicSimulationParametersUuid =
+            dynamicSimulationService.getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/service/StudyService.java` around
lines 3040 - 3052, The code is passing
studyEntity.getDynamicSimulationParametersUuid() directly to
runDynamicSimulation, which can be null for fresh studies; instead call the
routine that ensures a params UUID exists
(getDynamicSimulationParametersUuidOrElseCreateDefault) and assign its result to
dynamicSimulationParametersUuid before invoking
dynamicSimulationService.runDynamicSimulation so the created/returned UUID is
materialized and passed in; update the reference in the runDynamicSimulation
call (keep variable name dynamicSimulationParametersUuid and callers
networkModificationTreeService.getComputationReports,
networkModificationTreeService.updateComputationReportUuid, and
dynamicSimulationService.runDynamicSimulation unchanged).

3152-3162: ⚠️ Potential issue | 🟠 Major

Create default DSA/DMC parameter UUIDs before launching these jobs.

Both run paths now pass raw studyEntity.get*ParametersUuid() values downstream, but those fields are still lazily created elsewhere in this class. That means a first DSA/DMC execution can send null UUIDs for required parameter sets on a fresh or duplicated study.

Suggested fix
-        UUID dynamicSecurityAnalysisParametersUuid = studyEntity.getDynamicSecurityAnalysisParametersUuid();
+        UUID dynamicSecurityAnalysisParametersUuid =
+            dynamicSecurityAnalysisService.getDynamicSecurityAnalysisParametersUuidOrElseCreateDefault(studyEntity);
-        UUID dynamicSimulationParametersUuid = studyEntity.getDynamicSimulationParametersUuid();
+        UUID dynamicSimulationParametersUuid =
+            dynamicSimulationService.getDynamicSimulationParametersUuidOrElseCreateDefault(studyEntity);

-        UUID dynamicSecurityAnalysisParametersUuid = studyEntity.getDynamicSecurityAnalysisParametersUuid();
+        UUID dynamicSecurityAnalysisParametersUuid =
+            dynamicSecurityAnalysisService.getDynamicSecurityAnalysisParametersUuidOrElseCreateDefault(studyEntity);

-        UUID dynamicMarginCalculationParametersUuid = studyEntity.getDynamicMarginCalculationParametersUuid();
+        UUID dynamicMarginCalculationParametersUuid =
+            dynamicMarginCalculationService.getDynamicMarginCalculationParametersUuidOrElseCreateDefault(studyEntity);

Also applies to: 3253-3270

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`:
- Around line 1849-1854: The controller currently ignores the boolean result
from studyService.setDynamicSimulationParameters and always returns 200; update
StudyController.setDynamicSimulationParameters to use the returned boolean and
follow the pattern used by other setters (e.g., setVoltageInitParameters,
setLoadFlowParameters, setDynamicSecurityAnalysisParameters) so that when
studyService.setDynamicSimulationParameters(studyUuid, dsParameter, userId)
returns true the endpoint returns ResponseEntity.noContent().build() and when
false returns ResponseEntity.ok().build().
- Around line 1857-1862: The GET endpoint returns a raw String which makes the
response contract ambiguous; update the mapping to explicitly declare JSON by
adding produces = MediaType.APPLICATION_JSON_VALUE to the `@GetMapping` on
getDynamicSimulationParameters and import org.springframework.http.MediaType,
ensuring the response Content-Type is application/json while still returning
studyService.getDynamicSimulationParameters(studyUuid) from the
getDynamicSimulationParameters method.

In `@src/main/java/org/gridsuite/study/server/service/StudyService.java`:
- Around line 2913-2925: In setDynamicSimulationParameters, after invalidating
dynamic security analysis, also invalidate dynamic margin calculation and emit
its status change: call
invalidateDynamicMarginCalculationStatusOnAllNodes(studyUuid) and then
notificationService.emitStudyChanged(studyUuid, null, null,
NotificationService.UPDATE_TYPE_DYNAMIC_MARGIN_CALCULATION_STATUS); place these
calls in the setDynamicSimulationParameters method (alongside
invalidateDynamicSecurityAnalysisStatusOnAllNodes and its emit) so any existing
DMC result/status is cleared and its status notification is emitted.

---

Duplicate comments:
In `@src/main/java/org/gridsuite/study/server/service/StudyService.java`:
- Around line 3040-3052: The code is passing
studyEntity.getDynamicSimulationParametersUuid() directly to
runDynamicSimulation, which can be null for fresh studies; instead call the
routine that ensures a params UUID exists
(getDynamicSimulationParametersUuidOrElseCreateDefault) and assign its result to
dynamicSimulationParametersUuid before invoking
dynamicSimulationService.runDynamicSimulation so the created/returned UUID is
materialized and passed in; update the reference in the runDynamicSimulation
call (keep variable name dynamicSimulationParametersUuid and callers
networkModificationTreeService.getComputationReports,
networkModificationTreeService.updateComputationReportUuid, and
dynamicSimulationService.runDynamicSimulation unchanged).
🪄 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: 3af2dbb5-0bd2-4883-bcfb-ab26da047af0

📥 Commits

Reviewing files that changed from the base of the PR and between c9c5686 and b7aa47b.

📒 Files selected for processing (2)
  • src/main/java/org/gridsuite/study/server/controller/StudyController.java
  • src/main/java/org/gridsuite/study/server/service/StudyService.java

Comment on lines 1849 to 1854
public ResponseEntity<Void> setDynamicSimulationParameters(
@PathVariable("studyUuid") UUID studyUuid,
@RequestBody(required = false) DynamicSimulationParametersInfos dsParameter,
@RequestBody(required = false) String dsParameter,
@RequestHeader(HEADER_USER_ID) String userId) {
studyService.setDynamicSimulationParameters(studyUuid, dsParameter, userId);
return ResponseEntity.ok().build();
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Dynamic simulation parameter setter declarations/usages:"
rg -n -C2 '\bsetDynamicSimulationParameters\s*\(' --type java

echo
echo "Existing controller branches that map service results to HTTP 200/204:"
rg -n -C2 'ResponseEntity\.noContent\(\)\.build\(\)' src/main/java/org/gridsuite/study/server/controller/StudyController.java

Repository: gridsuite/study-server

Length of output: 15539


Fix inconsistent response codes for setDynamicSimulationParameters.

The service method returns a boolean, but this controller endpoint ignores it and always returns HTTP 200. All other parameter setters in this controller follow the pattern: return 204 No Content when the boolean is true, and 200 OK when false. Update this method to match that convention.

Affected pattern in other setters
// Line 980 (setVoltageInitParameters)
return studyService.setVoltageInitParameters(studyUuid, voltageInitParameters, userId) ? ResponseEntity.noContent().build() : ResponseEntity.ok().build();

// Line 1128 (setLoadFlowParameters)
return studyService.setLoadFlowParameters(studyUuid, lfParameter, userId) ? ResponseEntity.noContent().build() : ResponseEntity.ok().build();

// Line 2009-2011 (setDynamicSecurityAnalysisParameters)
return studyService.setDynamicSecurityAnalysisParameters(studyUuid, dsaParameter, userId) ?
        ResponseEntity.noContent().build() :
        ResponseEntity.ok().build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`
around lines 1849 - 1854, The controller currently ignores the boolean result
from studyService.setDynamicSimulationParameters and always returns 200; update
StudyController.setDynamicSimulationParameters to use the returned boolean and
follow the pattern used by other setters (e.g., setVoltageInitParameters,
setLoadFlowParameters, setDynamicSecurityAnalysisParameters) so that when
studyService.setDynamicSimulationParameters(studyUuid, dsParameter, userId)
returns true the endpoint returns ResponseEntity.noContent().build() and when
false returns ResponseEntity.ok().build().

Comment on lines 1857 to 1862
@GetMapping(value = "/studies/{studyUuid}/dynamic-simulation/parameters")
@Operation(summary = "Get dynamic simulation parameters on study")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation parameters")})
public ResponseEntity<DynamicSimulationParametersInfos> getDynamicSimulationParameters(
public ResponseEntity<String> getDynamicSimulationParameters(
@PathVariable("studyUuid") UUID studyUuid) {
return ResponseEntity.ok().body(studyService.getDynamicSimulationParameters(studyUuid));
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

Keep the parameter GET response explicitly JSON.

Once this endpoint returns a raw String instead of a DTO, the JSON response contract is no longer explicit. That can change how clients and generated API docs interpret the same payload.

💡 Suggested fix
-    `@GetMapping`(value = "/studies/{studyUuid}/dynamic-simulation/parameters")
+    `@GetMapping`(value = "/studies/{studyUuid}/dynamic-simulation/parameters", produces = MediaType.APPLICATION_JSON_VALUE)
     `@Operation`(summary = "Get dynamic simulation parameters on study")
     `@ApiResponses`(value = {`@ApiResponse`(responseCode = "200", description = "The dynamic simulation parameters")})
     public ResponseEntity<String> getDynamicSimulationParameters(
             `@PathVariable`("studyUuid") UUID studyUuid) {
-        return ResponseEntity.ok().body(studyService.getDynamicSimulationParameters(studyUuid));
+        return ResponseEntity.ok()
+                .contentType(MediaType.APPLICATION_JSON)
+                .body(studyService.getDynamicSimulationParameters(studyUuid));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/controller/StudyController.java`
around lines 1857 - 1862, The GET endpoint returns a raw String which makes the
response contract ambiguous; update the mapping to explicitly declare JSON by
adding produces = MediaType.APPLICATION_JSON_VALUE to the `@GetMapping` on
getDynamicSimulationParameters and import org.springframework.http.MediaType,
ensuring the response Content-Type is application/json while still returning
studyService.getDynamicSimulationParameters(studyUuid) from the
getDynamicSimulationParameters method.

Comment on lines +2913 to +2925
public boolean setDynamicSimulationParameters(UUID studyUuid, String dsParameter, String userId) {
StudyEntity studyEntity = getStudy(studyUuid);
boolean userProfileIssue = createOrUpdateDynamicSimulationParameters(studyEntity, dsParameter, userId);
invalidateDynamicSimulationStatusOnAllNodes(studyUuid);
notificationService.emitStudyChanged(studyUuid, null, null, NotificationService.UPDATE_TYPE_DYNAMIC_SIMULATION_STATUS);

// Dynamic security analysis depends on dynamic simulation => must invalidate
invalidateDynamicSecurityAnalysisStatusOnAllNodes(studyUuid);
notificationService.emitStudyChanged(studyUuid, null, null, NotificationService.UPDATE_TYPE_DYNAMIC_SECURITY_ANALYSIS_STATUS);

notificationService.emitElementUpdated(studyUuid, userId);
notificationService.emitComputationParamsChanged(studyUuid, DYNAMIC_SIMULATION);
return userProfileIssue;
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

Invalidate dynamic margin calculation when DS parameters change.

runDynamicMarginCalculation(...) still consumes dynamicSimulationParametersUuid, so this setter currently leaves any existing DMC result/status stale after a DS parameter update. Invalidate DMC here and emit its status notification too.

Suggested fix
         invalidateDynamicSimulationStatusOnAllNodes(studyUuid);
         notificationService.emitStudyChanged(studyUuid, null, null, NotificationService.UPDATE_TYPE_DYNAMIC_SIMULATION_STATUS);

         // Dynamic security analysis depends on dynamic simulation => must invalidate
         invalidateDynamicSecurityAnalysisStatusOnAllNodes(studyUuid);
         notificationService.emitStudyChanged(studyUuid, null, null, NotificationService.UPDATE_TYPE_DYNAMIC_SECURITY_ANALYSIS_STATUS);
+
+        // Dynamic margin calculation also depends on dynamic simulation parameters
+        invalidateDynamicMarginCalculationStatusOnAllNodes(studyUuid);
+        notificationService.emitStudyChanged(studyUuid, null, null, NotificationService.UPDATE_TYPE_DYNAMIC_MARGIN_CALCULATION_STATUS);

         notificationService.emitElementUpdated(studyUuid, userId);
         notificationService.emitComputationParamsChanged(studyUuid, DYNAMIC_SIMULATION);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/study/server/service/StudyService.java` around
lines 2913 - 2925, In setDynamicSimulationParameters, after invalidating dynamic
security analysis, also invalidate dynamic margin calculation and emit its
status change: call
invalidateDynamicMarginCalculationStatusOnAllNodes(studyUuid) and then
notificationService.emitStudyChanged(studyUuid, null, null,
NotificationService.UPDATE_TYPE_DYNAMIC_MARGIN_CALCULATION_STATUS); place these
calls in the setDynamicSimulationParameters method (alongside
invalidateDynamicSecurityAnalysisStatusOnAllNodes and its emit) so any existing
DMC result/status is cleared and its status notification is emitted.

Signed-off-by: Thang PHAM <phamthang37@gmail.com>
Copy link
Copy Markdown
Contributor

@khouadrired khouadrired left a comment

Choose a reason for hiding this comment

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

LGTM

Signed-off-by: Thang PHAM <phamthang37@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: 2

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java (1)

153-179: ⚠️ Potential issue | 🟠 Major

Guard required run inputs before URL/query construction.

At Line 165 and Lines 167-169, parametersUuid and reportInfos are used without validation. If either is null, request construction can fail late (or produce an invalid request), which is harder to diagnose.

Suggested fix
 `@Override`
 public UUID run(String receiver, UUID networkUuid, String variantId, ReportInfos reportInfos, UUID parametersUuid, List<EventInfos> events, String userId, boolean debug) {
     Objects.requireNonNull(networkUuid);
+    Objects.requireNonNull(reportInfos);
+    Objects.requireNonNull(parametersUuid);
     String endPointUrl = buildEndPointUrl(getBaseUri(), DYNAMIC_SIMULATION_API_VERSION, DYNAMIC_SIMULATION_END_POINT_RUN);
@@
-    HttpEntity<List<EventInfos>> httpEntity = new HttpEntity<>(events, headers);
+    HttpEntity<List<EventInfos>> httpEntity = new HttpEntity<>(events == null ? List.of() : events, headers);
     return getRestTemplate().postForObject(uriComponent.toUriString(), httpEntity, UUID.class);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`
around lines 153 - 179, In run (DynamicSimulationClientImpl.run) validate
required inputs before building the URL: add null checks (e.g.,
Objects.requireNonNull) for parametersUuid and reportInfos and also ensure
reportInfos.reportUuid() and reportInfos.nodeUuid() are non-null (or throw an
IllegalArgumentException with a clear message) so URI/queryParam construction
won't receive nulls; keep existing checks for networkUuid and receiver as-is and
perform these validations at the top of the method before using parametersUuid,
reportInfos.reportUuid(), or reportInfos.nodeUuid().
🧹 Nitpick comments (1)
src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java (1)

46-148: Extract one parametersBaseUrl helper to reduce duplication.

buildEndPointUrl(getBaseUri(), DYNAMIC_SIMULATION_API_VERSION, DYNAMIC_SIMULATION_END_POINT_PARAMETER) is repeated several times (Lines 47, 60, 83, 117, 140). Centralizing it will make future endpoint/version changes safer.

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

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`
around lines 46 - 148, Extract a private helper method (e.g.
getParametersBaseUrl()) that returns buildEndPointUrl(getBaseUri(),
DYNAMIC_SIMULATION_API_VERSION, DYNAMIC_SIMULATION_END_POINT_PARAMETER) and
replace all local occurrences of parametersBaseUrl in methods like
getParametersWithUuidUrl, getProvider, getParameters, createParameters,
duplicateParameters, deleteParameters and createDefaultParameters with a call to
this helper; update getParametersWithUuidUrl to call getParametersBaseUrl() so
the base URL construction is centralized and duplication is removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`:
- Around line 153-156: The run(...) method is missing an upfront null check for
the required dynamicSimulationParametersUuid parameter; add a fail-fast
validation at the start of DynamicMarginCalculationClient.run that verifies
dynamicSimulationParametersUuid is not null (e.g., Objects.requireNonNull or
throw new IllegalArgumentException) so you don't build a downstream request with
a missing simulation-parameters UUID; keep the same behavior for the other
already-validated UUIDs and apply the check before the request body construction
where dynamicSimulationParametersUuid is added.

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`:
- Around line 134-135: The inline comment above the REST call in
DynamicSimulationClientImpl incorrectly mentions "dynamic-margin-calculation";
update that comment to reference the correct service ("dynamic-simulation") or
remove the stale comment entirely so it accurately describes the
getRestTemplate().delete(url) call to the dynamic-simulation endpoint (locate
the comment immediately above the getRestTemplate().delete(url) invocation in
DynamicSimulationClientImpl).

---

Outside diff comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`:
- Around line 153-179: In run (DynamicSimulationClientImpl.run) validate
required inputs before building the URL: add null checks (e.g.,
Objects.requireNonNull) for parametersUuid and reportInfos and also ensure
reportInfos.reportUuid() and reportInfos.nodeUuid() are non-null (or throw an
IllegalArgumentException with a clear message) so URI/queryParam construction
won't receive nulls; keep existing checks for networkUuid and receiver as-is and
perform these validations at the top of the method before using parametersUuid,
reportInfos.reportUuid(), or reportInfos.nodeUuid().

---

Nitpick comments:
In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/impl/DynamicSimulationClientImpl.java`:
- Around line 46-148: Extract a private helper method (e.g.
getParametersBaseUrl()) that returns buildEndPointUrl(getBaseUri(),
DYNAMIC_SIMULATION_API_VERSION, DYNAMIC_SIMULATION_END_POINT_PARAMETER) and
replace all local occurrences of parametersBaseUrl in methods like
getParametersWithUuidUrl, getProvider, getParameters, createParameters,
duplicateParameters, deleteParameters and createDefaultParameters with a call to
this helper; update getParametersWithUuidUrl to call getParametersBaseUrl() so
the base URL construction is centralized and duplication is removed.
🪄 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: e12045e4-04cc-4ae0-b5f0-a065092b8c3a

📥 Commits

Reviewing files that changed from the base of the PR and between b7aa47b and cc6f307.

📒 Files selected for processing (4)
  • 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/resources/db/changelog/changesets/changelog_20260319T184344Z.xml
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/gridsuite/study/server/service/client/dynamicsecurityanalysis/DynamicSecurityAnalysisClient.java

Comment on lines +153 to +156
public UUID run(@NonNull String receiver, @NonNull UUID networkUuid, String variantId,
@NonNull ReportInfos reportInfos, @NonNull UUID dynamicSimulationParametersUuid,
@NonNull UUID dynamicSecurityAnalysisParametersUuid,
@NonNull UUID parametersUuid, String userId, boolean debug) {
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 | 🟡 Minor

Fail fast on the new required simulation-parameters UUID.

dynamicSimulationParametersUuid is mandatory at Line 154 and always added to the request at Line 173, but it is the only required UUID here that is not guarded up front. If a null slips through, this client will build a bad downstream request instead of failing immediately.

Suggested fix
     Objects.requireNonNull(receiver);
     Objects.requireNonNull(networkUuid);
     Objects.requireNonNull(reportInfos);
+    Objects.requireNonNull(dynamicSimulationParametersUuid);
     Objects.requireNonNull(dynamicSecurityAnalysisParametersUuid);
     Objects.requireNonNull(parametersUuid);

Also applies to: 173-173

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

In
`@src/main/java/org/gridsuite/study/server/service/client/dynamicmargincalculation/DynamicMarginCalculationClient.java`
around lines 153 - 156, The run(...) method is missing an upfront null check for
the required dynamicSimulationParametersUuid parameter; add a fail-fast
validation at the start of DynamicMarginCalculationClient.run that verifies
dynamicSimulationParametersUuid is not null (e.g., Objects.requireNonNull or
throw new IllegalArgumentException) so you don't build a downstream request with
a missing simulation-parameters UUID; keep the same behavior for the other
already-validated UUIDs and apply the check before the request body construction
where dynamicSimulationParametersUuid is added.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 2, 2026

@thangqp thangqp merged commit bbbfed2 into main Apr 2, 2026
4 checks passed
@thangqp thangqp deleted the ds_move_params_to_ds_server branch April 2, 2026 10:16
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