Skip to content

Delete modifications by their ids, wherever they sit - #943

Open
flomillot wants to merge 14 commits into
mainfrom
refactor/delete-modifications-by-list
Open

flomillot wants to merge 14 commits into
mainfrom
refactor/delete-modifications-by-list

Conversation

@flomillot

@flomillot flomillot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

DELETE /network-modifications now takes only the ids of the modifications to delete: the groupUuid parameter is removed, along with the deletion of a whole group content, which had no client. A modification is found by id whether it sits in a group, in a composite or is shared. Unknown ids are ignored instead of answering 404 when none is found, like deleting an unknown group.

Deleting modifications now removes them from the loaded container holding them, instead of each caller doing it, and reads the content of the deleted composites in one query instead of two per level.

Deleting modifications, groups or stashed modifications, and replacing a composite content, now unlink the deleted modification references from directory-server, nested ones included. This fixes the references left in directory-server when they were deleted inside a composite, e.g. when deleting or replacing a shared composite. The references are unlinked once the deletion is committed, not to hold the transaction during the calls to directory-server.

Drop the group parameter of DELETE /network-modifications, and the unused
deletion of a whole group content: the modifications are found by id, in a
group, nested in a composite or shared.

Deleting a modification removes it from the loaded container holding it, and
reads the content of the deleted composites in one query instead of one per
level.

Signed-off-by: Florent MILLOT <75525996+flomillot@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4498ffb3-fcc5-4356-b004-97b9b003e06f
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Return early on an empty list instead of guarding each caller, read the
composite ids straight from the deleted modifications, and check from
outside whether a container content is loaded, so that an uninitialized
proxy is not loaded by the check.

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
Explain the PERSIST cascade that would cancel the deletion, split the
container filter, and drop the replace composite test.

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
…st it with the existing deletion test

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Deletion ordering can violate foreign keys, and late user validation can leave Elasticsearch inconsistent after rejected requests.

2 open issues
What changed in this PR

This PR makes modification deletion ID-based across groups, composites, and shared modifications, and extends directory-reference cleanup to nested content.

Changes:

  • Removes group-scoped deletion parameters and ignores unknown IDs.
  • Centralizes container cleanup and batches composite-descendant reads.
  • Unlinks deleted references and updates API tests and query counts.
File Description
src/​test/​java/​org/​gridsuite/​modification/​server/​utils/​ApiUtils.java Removes group-scoped deletion helpers.
src/​test/​java/​org/​gridsuite/​modification/​server/​service/​ModificationRepositoryTest.java Updates deletion calls and query counts.
src/​test/​java/​org/​gridsuite/​modification/​server/​service/​ModificationIndexationTest.java Updates service calls.
src/​test/​java/​org/​gridsuite/​modification/​server/​modifications/​tabularmodifications/​TabularGeneratorModificationsTest.java Adapts tabular deletion tests.
src/​test/​java/​org/​gridsuite/​modification/​server/​modifications/​tabularcreations/​TabularGeneratorCreationsTest.java Updates repository deletion call.
src/​test/​java/​org/​gridsuite/​modification/​server/​ModificationControllerTest.java Tests ID-based deletion and reference cleanup.
src/​main/​java/​org/​gridsuite/​modification/​server/​service/​NetworkModificationService.java Adds transactional reference cleanup.
src/​main/​java/​org/​gridsuite/​modification/​server/​service/​DirectoryService.java Tolerates missing elements when unlinking.
src/​main/​java/​org/​gridsuite/​modification/​server/​repositories/​NetworkModificationRepository.java Centralizes subtree deletion and container cleanup.
src/​main/​java/​org/​gridsuite/​modification/​server/​repositories/​ModificationRepository.java Adds descendant-entity query.
src/​main/​java/​org/​gridsuite/​modification/​server/​NetworkModificationController.java Updates deletion parameters and user forwarding.
src/​main/​java/​org/​gridsuite/​modification/​server/​error/​NetworkModificationExceptionHandler.java Removes obsolete error mappings.
src/​main/​java/​org/​gridsuite/​modification/​server/​error/​ModificationBusinessErrorCode.java Removes obsolete deletion errors.
src/​main/​java/​org/​gridsuite/​modification/​server/​entities/​AbstractModificationContainerEntity.java Adds loaded-content detection.

Review effort: Balanced

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
if (deletedReferences.isEmpty()) {
return;
}
if (userId == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Delete shared composite from gridexplore now fails with 400 error
(userId is missing in the endpoint header of explore-server)

@flomillot flomillot Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

deleteIndexedModificationGroups(groupUuids);
networkModificationRepository.deleteModificationGroups(groupUuids);
List<ModificationReferenceData> deletedReferences = networkModificationRepository.deleteModificationGroups(groupUuids);
removeDeletedElementReferences(deletedReferences, userId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

references deleted with one directory-server HTTP call inside Transactional method !?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah now that it's Transactional I should refactor this too, thanks

@Transactional
public void deleteNetworkModifications(@NonNull List<UUID> modificationsUuids, String userId) {
List<ModificationReferenceData> deletedReferences = networkModificationRepository.deleteModifications(modificationsUuids);
removeDeletedElementReferences(deletedReferences, userId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the same remark here (HTTP call inside DB transaction)
another remark : in this case if directory server returns 404 error for exemple ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

…on is committed

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
@flomillot
flomillot requested a review from ghazwarhili October 23, 2026 07:58
@ghazwarhili

Copy link
Copy Markdown
Contributor

#943 (review) Could you fix Copilot’s feedback please @flomillot ?

@basseche
basseche self-requested a review October 23, 2026 08:51
@Query("""
WITH descendants AS (
SELECT m.id AS id FROM ModificationEntity m WHERE m.container.id IN (:containerIds)
UNION ALL

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

with Union All we can have duplicate ids. Shouldn't we use Union instead ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Based on findAllDescendantModificationIdsByContainerIds, that was already with UNION ALL, but your're right.
I change then both.
Also add a .distinct().

@flomillot

Copy link
Copy Markdown
Contributor Author

#943 (review) Could you fix Copilot’s feedback please @flomillot ?

I answered, no fix needed

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
…ications-by-list

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>

# Conflicts:
#	src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java
#	src/test/java/org/gridsuite/modification/server/ModificationControllerTest.java
Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
@flomillot
flomillot requested a review from basseche October 23, 2026 10:38
Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>
.retrieve()
.toBodilessEntity();
} catch (HttpClientErrorException.NotFound _) {
// the shared element is gone, and its references with it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What about here ? Log or throw ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nothing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can add a log but I dont think its necessary


@Transactional
public void deleteModificationGroups(List<UUID> groupUuids) {
public void deleteModificationGroups(List<UUID> groupUuids, String userId) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

here we remove the group in elasticsearch before the userId header check => the group could stay in the database with no index

=> too risky (I think we have a design issue here)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same problem everywhere in the repos

/**
* After the commit, not to hold the deletion transaction during the calls to directory-server
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)

We haven’t used it anywhere else in the code, so we might need TL approval before introducing it here

@etiennehomer @SlimaneAmar

if (deletedReferences.isEmpty()) {
return;
}
if (userId == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i'm not sure about this check here it seems too late it should be done up front maybe or by making the header required before any work starts

=> I don't know it seems a design prb with this userId check

@flomillot flomillot Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's OK because I kept the Transactional, so everything is canceled (except ElasticSearch as you said). If I do it before, I need to do a recursive query in the DB just for that check.

public ResponseEntity<Void> deleteStashedModificationFromGroups(@Parameter(description = "Group UUIDs") @RequestBody List<UUID> groupUuids,
@RequestHeader(HEADER_USER_ID) String userId) {
networkModificationService.deleteStashedModificationFromGroups(groupUuids);
networkModificationService.deleteStashedModificationFromGroups(groupUuids, userId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

deleteStashedModificationFromGroups +
removeElementReferences(groupUuid, userId) for each groupuuid ?

=> delete stashed and active modifications ?

@flomillot flomillot Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The links are deleted and it's a bug. It's cleaned and removed in #944.

String.format("Some of these modifications %s (to be deleted) were not found", modificationsUuids),
Map.of("ids", modificationsUuids));
@Transactional
public void deleteNetworkModifications(@NonNull List<UUID> modificationsUuids, String userId) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MODIFICATIONS_NOT_FOUND and
MODIFICATION_WITH_GROUP_DELETION_FORBIDDEN are removed

=> deleting non existent modifications now returns 200 instead of 404, the group-deletion safe guard removed
=> The refacto changes the API behavior in my opinion
=> should be explicitly documented in the PR and validated with PO and TL maybe

@flomillot flomillot Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As said above I asked the TL before removing the group guard, it's validated.
Will be read by code owner anyway.
Its already in the PR description : "Unknown ids are ignored instead of answering 404 when none is found, like deleting an unknown group." So now both endpoint are coherent.

WITH descendants AS (
SELECT m.id AS id FROM ModificationEntity m WHERE m.container.id IN (:containerIds)
UNION ALL
UNION

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

UNION ALL changes to UNION why ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

)
SELECT m FROM ModificationEntity m JOIN descendants d ON m.id = d.id
""")
List<ModificationEntity> findAllDescendantModificationsByContainerIds(@Param("containerIds") Collection<UUID> containerIds);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it is seems the same or a duplicated query like findAllDescendantModificationIdsByContainerIds

@flomillot flomillot Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

One return the entities, the other one only the IDs. It avoid a JOIN and a stream later on.

…ications-by-list

Signed-off-by: Florent MILLOT <florent.millot_externe@rte-france.com>

# Conflicts:
#	src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

4 participants