Skip to content

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

Open
thangqp wants to merge 3 commits intomainfrom
ds_move_params_to_ds_server
Open

Moving Dynamic Simulation params to dynamic-simulation-server#969
thangqp wants to merge 3 commits intomainfrom
ds_move_params_to_ds_server

Conversation

@thangqp
Copy link
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

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Refactors dynamic-simulation parameter handling from DTO/entity-based models to UUID-referenced JSON strings. Removes multiple DTOs and a dedicated parameters entity, adds UUID column on Study, introduces parameter management APIs (create/get/update/duplicate/delete/default) and updates controller/service/client signatures to use parameter UUIDs and serialized JSON.

Changes

Cohort / File(s) Summary
Controller
src/main/java/org/gridsuite/study/server/controller/StudyController.java
Switched request/response bodies to raw String for parameters; removed optional parameters body from runDynamicSimulation; added optional mapping query param to getDynamicSimulationModels.
Removed DTOs
src/main/java/org/gridsuite/study/server/dto/dynamicsimulation/*.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/*
Deleted DynamicSimulationParametersInfos and related curve/network/solver DTOs and enum; removed their JSON helper utilities.
Repository / Entity
src/main/java/org/gridsuite/study/server/repository/StudyEntity.java, src/main/java/org/gridsuite/study/server/repository/DynamicSimulationParametersEntity.java
Dropped DynamicSimulationParametersEntity and provider field; replaced one-to-one association with dynamicSimulationParametersUuid UUID column on Study.
Service API Surface
src/main/java/org/gridsuite/study/server/service/StudyService.java, .../dynamicsimulation/DynamicSimulationService.java, .../dynamicsimulation/impl/DynamicSimulationServiceImpl.java
Removed DTO-based parameter handling and default-provider wiring; added parameter-management methods (create/get/update/duplicate/delete/createDefault/getProvider); changed run signatures to accept parametersUuid and List<EventInfos> (removed provider/DTO params).
Clients
src/main/java/org/gridsuite/study/server/service/client/dynamicsimulation/*, .../dynamicsecurityanalysis/*, .../dynamicmargincalculation/*, RestClient.java
DynamicSimulation client: added parameters API (getProvider/getParameters/create/update/duplicate/delete/createDefault) and changed run to accept parametersUuid + events. Security/margin clients removed provider param and adjusted query usage. RestClient gained setBaseUri.
Consumer / Other Services
src/main/java/org/gridsuite/study/server/service/ConsumerService.java, .../dynamicsecurityanalysis/*, .../dynamicmargincalculation/*
Updated flows to create/duplicate/consume parameter UUIDs instead of DTO objects; adapted calls to new client/service signatures and removed provider parameter where applicable.
URI Builder Updates
src/main/java/org/gridsuite/study/server/service/client/.../FilterService.java, DynamicMappingClientImpl.java, TimeSeriesClientImpl.java
Replaced UriComponentsBuilder.fromHttpUrl(...) with fromUriString(...) for URI construction in multiple clients.
DB Migration
src/main/resources/db/changelog/changesets/changelog_20260319T184344Z.xml, .../db.changelog-master.yaml
Added migration: new dynamic_simulation_parameters_uuid column on study, drop old FK/table/columns for previous parameters entity.
User DTO
src/main/java/org/gridsuite/study/server/dto/UserProfileInfos.java
Added UUID dynamicSimulationParameterId to reference user-specific default parameters.
Tests — client/service/controller
src/test/java/.../service/client/dynamicsimulation/*, src/test/java/.../service/dynamicsimulation/*, src/test/java/.../StudyControllerDynamicSimulationTest.java, src/test/java/.../StudyTestBase.java, src/test/java/.../StudyTest.java
Refactored tests to use JSON strings and parameter UUIDs; added parameter-management test coverage; updated many mocks/expectations to the new method signatures and increased parameter-creation/duplication verification 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 test utility providing sample EventInfos lists for parameter-related tests.
Misc tests updates
various test files under src/test/java/...
Adjusted Mockito spies/names, tightened argument matchers, and updated expectations across dynamic-security/margin tests to reflect removed provider and changed parameter positions.

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, rootNetworkUuid, nodeUuid, debug, userId)
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/vX/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
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is empty, providing no information about the changeset. Add a meaningful description explaining what changed, why it changed, and any important notes for reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.99% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: moving dynamic simulation parameters handling from the study-server to dynamic-simulation-server.

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

Warning

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

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

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

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

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

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>
@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
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

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant