Skip to content

Introduce modification container abstraction#823

Open
Meklo wants to merge 14 commits into
mainfrom
marcellinh/modifications_containers
Open

Introduce modification container abstraction#823
Meklo wants to merge 14 commits into
mainfrom
marcellinh/modifications_containers

Conversation

@Meklo
Copy link
Copy Markdown
Contributor

@Meklo Meklo commented May 28, 2026

PR Summary

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Review Change Stack

Warning

Review limit reached

@Meklo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes and 1 second. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e18ffdcc-6420-4faf-9032-5c030ad0b163

📥 Commits

Reviewing files that changed from the base of the PR and between 9b4fde7 and 0b5f757.

📒 Files selected for processing (3)
  • src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java
  • src/main/resources/db/changelog/db.changelog-master.yaml
  • src/test/java/org/gridsuite/modification/server/modifications/tabularmodifications/TabularGeneratorModificationsTest.java
📝 Walkthrough

Walkthrough

Refactors modification ownership from group-only to container-aware (GROUP/COMPOSITE): new container abstractions, entity JPA changes, container-scoped repository queries and CTEs, container-aware move/reorder logic, controller/service signature changes, DB migration, and test updates.

Changes

Polymorphic Container Model

Layer / File(s) Summary
Container abstraction contracts
src/main/java/org/gridsuite/modification/server/entities/ModificationContainerType.java, src/main/java/org/gridsuite/modification/server/entities/ModificationContainer.java
Adds ModificationContainerType enum (GROUP, COMPOSITE) and ModificationContainer interface with identity, type, child list accessors, and mutation methods including a default descendant reattach.
Entity model refactoring
src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java, src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java, src/main/java/org/gridsuite/modification/server/entities/CompositeModificationEntity.java, src/main/java/org/gridsuite/modification/server/entities/ContainerOps.java
ModificationEntity removes group and adds containerId/containerType plus attach/detach helpers and index. ModificationGroupEntity and CompositeModificationEntity implement ModificationContainer with @JoinColumn(container_id) + @SQLRestriction mappings; ContainerOps centralizes child insertion and renumbering.
Application entity and repository queries
src/main/java/org/gridsuite/modification/server/entities/ModificationApplicationEntity.java, src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java, src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java
ModificationApplicationEntity.toModificationApplicationInfos() uses containerId. Repositories switch group-scoped queries to containerId/containerType variants, add bulk/container-type queries, and rewrite composite descendant resolution and depth queries with recursive CTEs. Repository delete/find signatures updated accordingly.
Cross-container move logic
src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java
Implements container-type-aware moveModifications overload (source/target type + id), handles same-container reorder vs cross-container insertion, prevents composite cycles, conditionally deletes modification-application records when moving from GROUP, auto-creates missing GROUP containers, and adds getContainerType(UUID) and composite-descendant deletion helper.
Service and controller API updates
src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java, src/main/java/org/gridsuite/modification/server/NetworkModificationController.java, src/main/java/org/gridsuite/modification/server/CompositeController.java, src/main/java/org/gridsuite/modification/server/elasticsearch/ModificationApplicationInfosService.java
NetworkModificationService.moveModifications now accepts source/target container IDs and canApply, resolves container types, and conditionally triggers re-application only when moving into a GROUP from a different container. Controller parameter names/descriptions updated; CompositeController.moveSubModification endpoint removed. Elasticsearch service uses container-based deletes.
Database schema migration & config
src/main/resources/db/changelog/changesets/changelog_20260522T151740Z.xml, src/main/resources/db/changelog/db.changelog-master.yaml, src/main/resources/config/application.yaml
Liquibase changeset adds modification.container_id and modification.container_type, backfills values from group_id and composite relations, creates (container_type, container_id) index, drops old composite link table and group_id column. Master changelog include added. Hibernate SQL logging enabled.
Tests
src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java, src/test/java/org/gridsuite/modification/server/service/*
Tests updated to use container-aware move overloads and to assert containerId instead of group; SQL statement-count expectations adjusted where necessary; repository/test stubbing updated to new fetch signatures.

Suggested reviewers

  • Mathieu-Deharbe
  • SlimaneAmar
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is empty (only contains the template header), failing to provide any information about the changes, rationale, or implementation details. Add a meaningful description explaining what the modification container abstraction is, why it was introduced, and how it changes the existing group-based model.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.34% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Introduce modification container abstraction' clearly and specifically describes the main architectural change: moving from group-based to a generic container abstraction.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

🧹 Nitpick comments (1)
src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java (1)

21-23: ⚡ Quick win

Align parameter names with the container model.

groupUuid is now misleading in both signatures since the method contract is container-based. Renaming to containerIds (or modificationContainerIds) will make call sites clearer and reduce migration confusion.

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

In
`@src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java`
around lines 21 - 23, Rename the misleading parameter name groupUuid to a
container-based name in the two repository method signatures: change the
parameter in deleteAllByNetworkUuidAndModificationContainerIdIn(UUID
networkUuid, List<UUID> groupUuid) and
deleteAllByModificationContainerIdIn(List<UUID> groupUuid) to something like
containerIds or modificationContainerIds to reflect the container model; update
all call sites and tests that reference these method signatures to use the new
parameter name so compilation and code clarity are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/gridsuite/modification/server/entities/CompositeModificationEntity.java`:
- Around line 80-90: setModifications currently clears the `@OneToMany` collection
(this.modifications.clear()) which causes Hibernate to mark existing children as
orphans and schedule deletes before you re-add them; instead, modify the
association incrementally in setModifications(List<ModificationEntity>
newChildren): validate newChildren non-null, iterate newChildren to update each
child's setModificationsOrder(i) and attachToContainer(this) for new or existing
children, add only genuinely new entities to this.modifications, and remove only
those children present in this.modifications but absent from newChildren so
orphanRemoval behaves correctly; this preserves managed instances and avoids
delete/insert churn and constraint/order issues.

In
`@src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java`:
- Around line 142-149: The attachToContainer/detachFromContainer helpers in
ModificationEntity currently only update containerType but must also keep the
mapped scalar FK containerId in sync; update
attachToContainer(ModificationContainer) to assign this.containerId =
container.getId() (or the container's identifier accessor) and update
detachFromContainer() to set this.containerId = null so the in-memory entity
matches the collection-managed FK (this is required because
ModificationApplicationEntity.toModificationApplicationInfos() reads
getModification().getContainerId()).

In
`@src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java`:
- Around line 68-78: setModifications currently clears the modifications list
then re-attaches only survivors, leaving omitted children with stale container
metadata; modify setModifications in ModificationGroupEntity to first compute
the set of removed children (elements in the existing modifications collection
that are not present in the incoming newChildren list) and for each removed
child call the appropriate detach logic on ModificationEntity (e.g. clear
container id/type or invoke a detachFromContainer method) before
clearing/replacing the collection, then attach and setModificationsOrder on the
surviving newChildren as today; ensure you update the modifications collection
only after detaching removed children so container ownership remains consistent.

In
`@src/main/java/org/gridsuite/modification/server/NetworkModificationController.java`:
- Line 18: Remove the unused import of ModificationContainerType from
NetworkModificationController: locate the import line "import
org.gridsuite.modification.server.entities.ModificationContainerType;" in the
NetworkModificationController class and delete it so Checkstyle no longer fails
for an unused import.
- Around line 104-116: The MOVE branch passes sourceContainerId
(originGroupUuid) directly to networkModificationService.moveModifications even
when it's null; restore the previous fallback by defaulting sourceContainerId to
targetContainerId when null for MOVE. Update the switch MOVE case to use
(sourceContainerId == null ? targetContainerId : sourceContainerId) before
calling networkModificationService.moveModifications (and keep passing
targetContainerId, beforeModificationUuid, modificationContextInfos.getFirst(),
modificationContextInfos.getSecond()) so same-container moves work when
originGroupUuid is omitted.

In
`@src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java`:
- Around line 110-175: Fix the Checkstyle failures in the multi-line
`@NativeQuery` text blocks by normalizing indentation and blank-line placement:
ensure each triple-quoted SQL block aligns with the annotation (no leading
spaces inside the text block), remove or add blank lines so there are no extra
blank lines inside the text blocks, and keep consistent indentation for all
three methods findOnlyCompositeChildrenUuids, findAllChildrenUuids, and
getCompositesMaxDepth (and the preceding CompositeDepth interface section) so
the opening and closing triple quotes are directly under `@NativeQuery` and the
SQL lines have consistent left alignment; after fixing the text-block formatting
re-run the formatter/checkstyle to confirm the errors on the affected blocks are
resolved.

In
`@src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java`:
- Line 227: The code in NetworkModificationRepository creates
destinationModificationEntities with improper spacing and missing braces; split
the combined statements into two properly spaced lines (one for creating the
ArrayList from destinationModificationGroupEntity.getModifications() and a
separate line calling removeIf(Objects::isNull)), normalize spaces so there is a
single space around operators and after commas, and add braces for the
single-line if statements referenced (the two ifs around lines noted) so they
become full-blocks with { ... } — apply the same formatting and brace fixes to
the other occurrences mentioned (the similar hunk at the other lines 335 and
384-385) to satisfy SingleSpaceSeparator, EmptyLineSeparator, and NeedBraces
checkstyle rules.
- Around line 188-191: The loop over modifications is double-incrementing the
order: modificationGroupEntity.addModification(m, order++) consumes one
post-increment and then m.setModificationsOrder(order); order++ increments
again, producing gaps. Fix by using a single increment operation: pass the
current order into addModification and set the same value on m, then increment
order once (or use a single post-increment consistently). Update the loop around
modificationGroupEntity.addModification(...), ModificationEntity m,
setModificationsOrder(...) and the order variable so only one increment occurs
per iteration.
- Around line 1064-1071: The getContainerType method in
NetworkModificationRepository should not return null for unknown container
UUIDs; replace the final return null with throwing a
NetworkModificationException (include a clear message like "Unknown container
UUID: <uuid>") so callers receive a domain error. Locate getContainerType and
after checking modificationGroupRepository.findById(containerUuid) and
compositeModificationRepository.findById(containerUuid), throw new
NetworkModificationException including the containerUuid and a descriptive
message instead of returning null; ensure the method signature and imports
reference NetworkModificationException and that any callers (e.g., move flow)
will handle or propagate this domain exception.
- Around line 266-287: The same-container branch drops stashed children because
sourceChildren is created by filtering out stashed items before reordering; when
sameContainer is true you must preserve stashed modifications. Change the logic
so you build a fullChildren list from source.getModifications() that includes
stashed items and use that full list for publishChildren (and for re-inserting
order via insertModifications), while still computing moved via
removeModifications(fullChildren, modificationUuids) or by deriving moved
separately; ensure functions referenced (source.getModifications(),
removeModifications, insertModifications, publishChildren, sameContainer)
operate on the full list so stashed children are not detached during
same-container reorders.

In
`@src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java`:
- Line 36: The import of ModificationGroupRepository in
NetworkModificationService is unused and causing a Checkstyle failure; open
NetworkModificationService.java and remove the line importing
org.gridsuite.modification.server.repositories.ModificationGroupRepository
(ensure there are no references to ModificationGroupRepository in methods of
NetworkModificationService or replace them with the correct repository type if
mistakenly used), then recompile/run checks to confirm the Checkstyle error is
resolved.

In `@src/main/resources/config/application.yaml`:
- Line 18: The default config enables Hibernate SQL logging via the show_sql
property (hibernate.show_sql), which should not be on in the default runtime
profile; remove or set show_sql: false from the base application.yaml and
instead place show_sql: true in a non-production profile (e.g.,
application-dev.yaml or under a spring.profiles: dev section) so only the
development/local profile (referencing the show_sql / hibernate.show_sql
property) emits SQL logs.

In `@src/main/resources/db/changelog/changesets/changelog_20260522T151740Z.xml`:
- Around line 4-40: The migration leaves modification.container_id and
modification.container_type nullable; add a step to make them NOT NULL after the
backfill and ensure there are no leftover NULLs: first run the existing backfill
UPDATEs (they reference group_id, composite_modification_sub_modifications, and
set container_id/container_type), then add a precondition or validation that
SELECT COUNT(*) FROM modification WHERE container_id IS NULL OR container_type
IS NULL returns zero, and finally run ALTER TABLE modification ALTER COLUMN
container_id SET NOT NULL and ALTER COLUMN container_type SET NOT NULL (or the
equivalent <modifyDataType>/<addNotNullConstraint> in your changelog), keeping
the index modification_container_idx and drop steps unchanged; if any rows
remain, fail the migration so you can handle or backfill them explicitly.

In
`@src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java`:
- Line 90: Restore the Mockito stubbing for modificationMock.getContainerId() in
testReindexElements so groupUuid is set and asserted: re-add a line stubbing
modificationMock.getContainerId() to return groupMock (or the UUID/value used
elsewhere), then update the test to assert that
ModificationApplicationEntity.toModificationApplicationInfos() propagates that
container id (groupUuid) into the resulting infos; target the modificationMock
and groupMock stubs and the testReindexElements method where
toModificationApplicationInfos() is exercised.

---

Nitpick comments:
In
`@src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java`:
- Around line 21-23: Rename the misleading parameter name groupUuid to a
container-based name in the two repository method signatures: change the
parameter in deleteAllByNetworkUuidAndModificationContainerIdIn(UUID
networkUuid, List<UUID> groupUuid) and
deleteAllByModificationContainerIdIn(List<UUID> groupUuid) to something like
containerIds or modificationContainerIds to reflect the container model; update
all call sites and tests that reference these method signatures to use the new
parameter name so compilation and code clarity are preserved.
🪄 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: 8e51e903-6868-420a-bb45-cff1571d66ed

📥 Commits

Reviewing files that changed from the base of the PR and between 33db2f2 and 2511fb3.

📒 Files selected for processing (19)
  • src/main/java/org/gridsuite/modification/server/CompositeController.java
  • src/main/java/org/gridsuite/modification/server/NetworkModificationController.java
  • src/main/java/org/gridsuite/modification/server/elasticsearch/ModificationApplicationInfosService.java
  • src/main/java/org/gridsuite/modification/server/entities/CompositeModificationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ContainerOps.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationApplicationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationContainer.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationContainerType.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java
  • src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java
  • src/main/java/org/gridsuite/modification/server/repositories/ModificationRepository.java
  • src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java
  • src/main/java/org/gridsuite/modification/server/service/NetworkModificationService.java
  • src/main/resources/config/application.yaml
  • src/main/resources/db/changelog/changesets/changelog_20260522T151740Z.xml
  • src/main/resources/db/changelog/db.changelog-master.yaml
  • src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java
  • src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java
💤 Files with no reviewable changes (1)
  • src/main/java/org/gridsuite/modification/server/CompositeController.java

Comment on lines +68 to +78
public void setModifications(List<ModificationEntity> newChildren) {
this.modifications.clear();
if (newChildren == null || newChildren.isEmpty()) {
return;
}
for (int i = 0; i < newChildren.size(); i++) {
ModificationEntity child = newChildren.get(i);
child.attachToContainer(this);
child.setModificationsOrder(i);
this.modifications.add(child);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Detach children that drop out of setModifications().

clear() unlinks every current child, but only the surviving ones are re-attached. On ModificationGroupEntity there is no orphan removal, so anything omitted from newChildren can keep stale container metadata while the collection update clears container_id. That leaves the new (container_id, container_type) ownership model inconsistent.

Suggested direction
 public void setModifications(List<ModificationEntity> newChildren) {
+    this.modifications.forEach(ModificationEntity::detachFromContainer);
     this.modifications.clear();
     if (newChildren == null || newChildren.isEmpty()) {
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java`
around lines 68 - 78, setModifications currently clears the modifications list
then re-attaches only survivors, leaving omitted children with stale container
metadata; modify setModifications in ModificationGroupEntity to first compute
the set of removed children (elements in the existing modifications collection
that are not present in the incoming newChildren list) and for each removed
child call the appropriate detach logic on ModificationEntity (e.g. clear
container id/type or invoke a detachFromContainer method) before
clearing/replacing the collection, then attach and setModificationsOrder on the
surviving newChildren as today; ensure you update the modifications collection
only after detaching removed children so container ownership remains consistent.

order_updates: true
jdbc:
batch_size: 512
show_sql: 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 | ⚡ Quick win

Avoid enabling hibernate.show_sql in the default runtime profile.

Turning this on globally can leak sensitive values to logs and increases overhead/noise in production.

Suggested change
-        show_sql: true
+        # keep disabled in default profile; enable only in local/debug profile
+        show_sql: false

Move show_sql: true to a dedicated local/dev profile configuration if needed.

📝 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
show_sql: true
# keep disabled in default profile; enable only in local/debug profile
show_sql: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/config/application.yaml` at line 18, The default config
enables Hibernate SQL logging via the show_sql property (hibernate.show_sql),
which should not be on in the default runtime profile; remove or set show_sql:
false from the base application.yaml and instead place show_sql: true in a
non-production profile (e.g., application-dev.yaml or under a spring.profiles:
dev section) so only the development/local profile (referencing the show_sql /
hibernate.show_sql property) emits SQL logs.

Comment on lines +4 to +40
<addColumn tableName="modification">
<column name="container_id" type="UUID"/>
<column name="container_type" type="VARCHAR(32)"/>
</addColumn>
<sql>
UPDATE modification
SET container_id = group_id,
container_type = 'GROUP'
WHERE group_id IS NOT NULL
AND container_id IS NULL;
</sql>


<sql>
UPDATE modification m
SET container_id = (
SELECT j.id
FROM composite_modification_sub_modifications j
WHERE j.modification_id = m.id
),
container_type = 'COMPOSITE'
WHERE m.container_id IS NULL
AND EXISTS (
SELECT 1
FROM composite_modification_sub_modifications j
WHERE j.modification_id = m.id
);
</sql>

<createIndex tableName="modification" indexName="modification_container_idx">
<column name="container_type"/>
<column name="container_id"/>
</createIndex>
<dropForeignKeyConstraint baseTableName="modification"
constraintName="group_id_fk_constraint"/>
<dropColumn tableName="modification" columnName="group_id"/>
<dropTable tableName="composite_modification_sub_modifications"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce container columns as mandatory after backfill.

This migration leaves container_id / container_type nullable even though the new model addresses modifications through these fields. Null leftovers would create orphaned rows invisible to container-based queries.

Suggested hardening
         <sql>
             UPDATE modification m
             SET container_id = (
                 SELECT j.id
                 FROM composite_modification_sub_modifications j
                 WHERE j.modification_id = m.id
             ),
                 container_type = 'COMPOSITE'
             WHERE m.container_id IS NULL
               AND EXISTS (
                 SELECT 1
                 FROM composite_modification_sub_modifications j
                 WHERE j.modification_id = m.id
             );
         </sql>

+        <preConditions onFail="HALT">
+            <sqlCheck expectedResult="0">
+                SELECT COUNT(*) FROM modification
+                WHERE container_id IS NULL OR container_type IS NULL
+            </sqlCheck>
+        </preConditions>
+
+        <addNotNullConstraint tableName="modification" columnName="container_id" columnDataType="UUID"/>
+        <addNotNullConstraint tableName="modification" columnName="container_type" columnDataType="VARCHAR(32)"/>
+
         <createIndex tableName="modification" indexName="modification_container_idx">
             <column name="container_type"/>
             <column name="container_id"/>
         </createIndex>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/db/changelog/changesets/changelog_20260522T151740Z.xml`
around lines 4 - 40, The migration leaves modification.container_id and
modification.container_type nullable; add a step to make them NOT NULL after the
backfill and ensure there are no leftover NULLs: first run the existing backfill
UPDATEs (they reference group_id, composite_modification_sub_modifications, and
set container_id/container_type), then add a precondition or validation that
SELECT COUNT(*) FROM modification WHERE container_id IS NULL OR container_type
IS NULL returns zero, and finally run ALTER TABLE modification ALTER COLUMN
container_id SET NOT NULL and ALTER COLUMN container_type SET NOT NULL (or the
equivalent <modifyDataType>/<addNotNullConstraint> in your changelog), keeping
the index modification_container_idx and drop steps unchanged; if any rows
remain, fail the migration so you can handle or backfill them explicitly.

Mockito.when(modificationMock.getId()).thenReturn(UUID.randomUUID());
Mockito.when(groupMock.getId()).thenReturn(groupUuid);
Mockito.when(modificationMock.getGroup()).thenReturn(groupMock);
//Mockito.when(modificationMock.getContainerId()).thenReturn(groupMock);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore getContainerId() stubbing to keep this test meaningful.

At Line 90, the removed stub leaves groupUuid effectively unverified in testReindexElements. Since ModificationApplicationEntity.toModificationApplicationInfos() now reads modification.getContainerId(), this test should stub that value explicitly and assert it is propagated.

Proposed fix
-        Mockito.when(groupMock.getId()).thenReturn(groupUuid);
-        //Mockito.when(modificationMock.getContainerId()).thenReturn(groupMock);
+        Mockito.when(modificationMock.getContainerId()).thenReturn(groupUuid);
         verify(modificationApplicationInfosRepository, times(1)).saveAll(modificationListCaptor.capture());
         assertThat(modificationListCaptor.getValue()).usingRecursiveComparison().isEqualTo(allModifications.stream().map(ModificationApplicationEntity::toModificationApplicationInfos).toList());
+        assertThat(modificationListCaptor.getValue()).allSatisfy(infos -> assertEquals(groupUuid, infos.getGroupUuid()));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java`
at line 90, Restore the Mockito stubbing for modificationMock.getContainerId()
in testReindexElements so groupUuid is set and asserted: re-add a line stubbing
modificationMock.getContainerId() to return groupMock (or the UUID/value used
elsewhere), then update the test to assert that
ModificationApplicationEntity.toModificationApplicationInfos() propagates that
container id (groupUuid) into the resulting infos; target the modificationMock
and groupMock stubs and the testReindexElements method where
toModificationApplicationInfos() is exercised.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java (1)

227-239: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Delay destination creation until the move set is known to be non-empty.

Line 229 loads or creates the target container before Line 237 knows whether any requested UUID actually belongs to the source. A move request containing only unknown UUIDs will return an empty result but still persist a brand-new empty destination group.

Suggested change
         ModificationContainer source = loadContainer(sourceType, sourceContainerId);
         boolean sameContainer = sourceType == targetType && sourceContainerId.equals(targetContainerId);
-        ModificationContainer target = sameContainer ? source : loadOrCreateContainer(targetType, targetContainerId);
 
         List<ModificationEntity> sourceChildren = source.getModifications().stream()
                 .filter(Objects::nonNull)
                 .filter(m -> !Boolean.TRUE.equals(m.getStashed()))
@@
         if (moved.isEmpty()) {
             return List.of();
         }
+
+        ModificationContainer target = sameContainer ? source : loadOrCreateContainer(targetType, targetContainerId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java`
around lines 227 - 239, The code currently calls
loadOrCreateContainer(targetType, targetContainerId) before knowing if any
modifications will be moved, causing empty target groups to be persisted; change
the flow so you first load the source via loadContainer(sourceType,
sourceContainerId), build sourceChildren, compute moved =
removeModifications(sourceChildren, modificationUuids) and if moved is empty
return early, and only then (when moved is non-empty and sameContainer is false)
call loadOrCreateContainer(targetType, targetContainerId) to obtain the target
ModificationContainer; update any logic that assumed target was already
available to run after this deferred creation.
♻️ Duplicate comments (3)
src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java (1)

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

Restore getContainerId() stubbing to keep this test meaningful.

The commented-out stub leaves containerId unverified in testReindexElements. Since ModificationApplicationEntity.toModificationApplicationInfos() now reads modification.getContainerId(), this test should stub that value explicitly and assert it is propagated.

Proposed fix
-        //Mockito.when(modificationMock.getContainerId()).thenReturn(groupMock);
+        Mockito.when(modificationMock.getContainerId()).thenReturn(groupUuid);

Add assertion after line 102:

         verify(modificationApplicationInfosRepository, times(1)).saveAll(modificationListCaptor.capture());
         assertThat(modificationListCaptor.getValue()).usingRecursiveComparison().isEqualTo(allModifications.stream().map(ModificationApplicationEntity::toModificationApplicationInfos).toList());
+        assertThat(modificationListCaptor.getValue()).allSatisfy(infos -> assertEquals(groupUuid, infos.getGroupUuid()));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java`
at line 90, Restore the Mockito stub for modificationMock.getContainerId() in
testReindexElements so the test supplies a deterministic containerId (e.g.,
return groupMock) because
ModificationApplicationEntity.toModificationApplicationInfos() now reads
modification.getContainerId(); add a
Mockito.when(modificationMock.getContainerId()).thenReturn(groupMock) near the
other stubs for modificationMock and then add an assertion after the existing
assertions that the produced ModificationApplicationInfo(s) contain the expected
containerId value to verify propagation.
src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java (1)

55-66: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Detach children that drop out of setModifications().

clear() unlinks every current child, but only the surviving ones are re-attached. Children omitted from newChildren keep stale container_id metadata while the collection update clears the reference. With insertable = false, updatable = false on the @JoinColumn (line 32), this entity cannot update container_id, leaving the orphaned children with inconsistent ownership.

Suggested direction
 public void setModifications(List<ModificationEntity> newChildren) {
+    // Detach children being removed
+    if (newChildren != null && !newChildren.isEmpty()) {
+        Set<UUID> survivorIds = newChildren.stream()
+            .map(ModificationEntity::getId)
+            .collect(Collectors.toSet());
+        this.modifications.stream()
+            .filter(child -> !survivorIds.contains(child.getId()))
+            .forEach(child -> child.detachFromContainer());
+    } else {
+        this.modifications.forEach(ModificationEntity::detachFromContainer);
+    }
     this.modifications.clear();
     if (newChildren == null || newChildren.isEmpty()) {
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java`
around lines 55 - 66, setModifications currently clears the collection then
re-attaches survivors, leaving omitted children with stale container metadata;
before calling this.modifications.clear() compute which existing children are
not present in newChildren and explicitly detach them (e.g., call a detach
method or clear their container reference) so their container_id is nulled, then
attach survivors with child.attachToContainer(this) and setModificationsOrder(i)
as you already do.
src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java (1)

231-247: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Preserve stashed children when reordering inside the same container.

sourceChildren strips out every stashed modification, and Line 247 writes that filtered list back to the container. A same-container move therefore detaches all stashed children from the container instead of just reordering the unstashed ones.

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

In
`@src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java`
around lines 231 - 247, sourceChildren currently filters out stashed
modifications from source.getModifications(), then on same-container reorders
writes the filtered list back with source.setModifications(), which
inadvertently removes stashed children; change the same-container path to
preserve stashed items by performing removeModifications/insertModifications
only on the unstashed subset without overwriting the container's full
modification list—i.e., use source.getModifications() to reconstruct the final
list by replacing only the moved unstashed entries (using removeModifications,
insertModifications and beforeModificationUuid) and then call
source.setModifications(...) with the merged list so stashed ModificationEntity
(getStashed()) elements remain in place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java`:
- Around line 227-239: The code currently calls
loadOrCreateContainer(targetType, targetContainerId) before knowing if any
modifications will be moved, causing empty target groups to be persisted; change
the flow so you first load the source via loadContainer(sourceType,
sourceContainerId), build sourceChildren, compute moved =
removeModifications(sourceChildren, modificationUuids) and if moved is empty
return early, and only then (when moved is non-empty and sameContainer is false)
call loadOrCreateContainer(targetType, targetContainerId) to obtain the target
ModificationContainer; update any logic that assumed target was already
available to run after this deferred creation.

---

Duplicate comments:
In
`@src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java`:
- Around line 55-66: setModifications currently clears the collection then
re-attaches survivors, leaving omitted children with stale container metadata;
before calling this.modifications.clear() compute which existing children are
not present in newChildren and explicitly detach them (e.g., call a detach
method or clear their container reference) so their container_id is nulled, then
attach survivors with child.attachToContainer(this) and setModificationsOrder(i)
as you already do.

In
`@src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java`:
- Around line 231-247: sourceChildren currently filters out stashed
modifications from source.getModifications(), then on same-container reorders
writes the filtered list back with source.setModifications(), which
inadvertently removes stashed children; change the same-container path to
preserve stashed items by performing removeModifications/insertModifications
only on the unstashed subset without overwriting the container's full
modification list—i.e., use source.getModifications() to reconstruct the final
list by replacing only the moved unstashed entries (using removeModifications,
insertModifications and beforeModificationUuid) and then call
source.setModifications(...) with the merged list so stashed ModificationEntity
(getStashed()) elements remain in place.

In
`@src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java`:
- Line 90: Restore the Mockito stub for modificationMock.getContainerId() in
testReindexElements so the test supplies a deterministic containerId (e.g.,
return groupMock) because
ModificationApplicationEntity.toModificationApplicationInfos() now reads
modification.getContainerId(); add a
Mockito.when(modificationMock.getContainerId()).thenReturn(groupMock) near the
other stubs for modificationMock and then add an assertion after the existing
assertions that the produced ModificationApplicationInfo(s) contain the expected
containerId value to verify propagation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8b53da69-abd1-49da-a313-c80d53b3e03e

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef549b and 9b4fde7.

📒 Files selected for processing (15)
  • src/main/java/org/gridsuite/modification/server/NetworkModificationController.java
  • src/main/java/org/gridsuite/modification/server/entities/CompositeModificationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ContainerOps.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationContainer.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationGroupEntity.java
  • src/main/java/org/gridsuite/modification/server/repositories/ModificationApplicationRepository.java
  • src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java
  • src/main/java/org/gridsuite/modification/server/service/SupervisionService.java
  • src/test/java/org/gridsuite/modification/server/CompositeControllerTest.java
  • src/test/java/org/gridsuite/modification/server/modifications/CompositeModificationsTest.java
  • src/test/java/org/gridsuite/modification/server/modifications/tabularmodifications/TabularGeneratorModificationsTest.java
  • src/test/java/org/gridsuite/modification/server/service/ModificationIndexationTest.java
  • src/test/java/org/gridsuite/modification/server/service/ModificationRepositoryTest.java
  • src/test/java/org/gridsuite/modification/server/service/SupervisionTest.java
💤 Files with no reviewable changes (1)
  • src/main/java/org/gridsuite/modification/server/entities/ContainerOps.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/org/gridsuite/modification/server/entities/ModificationContainer.java
  • src/main/java/org/gridsuite/modification/server/entities/CompositeModificationEntity.java
  • src/main/java/org/gridsuite/modification/server/entities/ModificationEntity.java

@sonarqubecloud
Copy link
Copy Markdown

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