Skip to content

Commit

Permalink
reorder non stash modifications (#521)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Etienne LESOT <[email protected]>
Signed-off-by: Mathieu DEHARBE <[email protected]>
Signed-off-by: BOUHOURS Antoine <[email protected]>
Signed-off-by: Ayoub LABIDI <[email protected]>
Signed-off-by: REHILI Ghazwa <[email protected]>
Signed-off-by: Franck LECUYER <[email protected]>
Signed-off-by: Thang PHAM <[email protected]
Signed-off-by: Etienne Homer <[email protected]>
Co-authored-by: Etienne Homer <[email protected]>
Co-authored-by: Mathieu Deharbe <[email protected]>
Co-authored-by: Antoine Bouhours <[email protected]>
Co-authored-by: Ayoub LABIDI <[email protected]>
Co-authored-by: Ghazoua Rehili <[email protected]>
Co-authored-by: FranckLecuyer <[email protected]>
Co-authored-by: Thang PHAM <[email protected]>
  • Loading branch information
8 people authored Oct 23, 2024
1 parent 6a9d23c commit 15816de
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 77 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<liquibase-hibernate-package>org.gridsuite.modification.server</liquibase-hibernate-package>
<!-- FIXME: powsybl-network-store modules'version is overloaded in the dependencies section.The overloads and this property below have to be removed at next powsybl-ws-dependencies.version upgrade -->
<powsybl-network-store.version>1.19.0</powsybl-network-store.version>
<sonar.coverage.exclusions>**/migration/**/*</sonar.coverage.exclusions>
</properties>

<build>
Expand Down Expand Up @@ -210,7 +211,6 @@
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,11 @@ public ResponseEntity<Void> stashNetworkModifications(
@Parameter(description = "Group UUID") @RequestParam("groupUuid") UUID groupUuid,
@Parameter(description = "stash or unstash network modifications") @RequestParam(name = "stashed", defaultValue = "true") Boolean stashed) {
if (Boolean.TRUE.equals(stashed)) {
networkModificationService.stashNetworkModifications(networkModificationUuids);
networkModificationService.stashNetworkModifications(groupUuid, networkModificationUuids);
networkModificationService.reorderNetworkModifications(groupUuid, Boolean.FALSE);
} else {
networkModificationService.restoreNetworkModifications(networkModificationUuids);
networkModificationService.restoreNetworkModifications(groupUuid, networkModificationUuids);
networkModificationService.reorderNetworkModifications(groupUuid, Boolean.TRUE);
}
return ResponseEntity.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class ModificationGroupEntity extends AbstractManuallyAssignedIdentifierE
mappedBy = "group",
cascade = CascadeType.ALL
)
@OrderColumn
@OrderBy("modificationsOrder asc")
private List<ModificationEntity> modifications = new ArrayList<>();

public ModificationGroupEntity(UUID uuid) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.gridsuite.modification.server.migration;

import liquibase.change.custom.CustomSqlChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import liquibase.exception.DatabaseException;
import liquibase.exception.SetupException;
import liquibase.exception.ValidationErrors;
import liquibase.resource.ResourceAccessor;
import liquibase.statement.SqlStatement;
import liquibase.statement.core.UpdateStatement;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.IntStream;

/**
* @author Etienne Lesot <etienne.lesot at rte-france.com>
*/
public class ModificationOrderMigration implements CustomSqlChange {

private static final Logger LOGGER = LoggerFactory.getLogger(ModificationOrderMigration.class);

@Override
public SqlStatement[] generateStatements(Database database) throws CustomChangeException {
JdbcConnection connection = (JdbcConnection) database.getConnection();
List<SqlStatement> statements = new ArrayList<>();
try (PreparedStatement stmt = connection.prepareStatement("select distinct group_id from modification where group_id is not null")) {
ResultSet groupIds = stmt.executeQuery();
while (groupIds.next()) {
UUID groupId = UUID.fromString(groupIds.getString(1));
reorderNetworkModifications(groupId, true, connection, statements, database);
reorderNetworkModifications(groupId, false, connection, statements, database);
}
} catch (SQLException | DatabaseException e) {
throw new CustomChangeException(e);
}
return statements.toArray(new SqlStatement[0]);
}

private void reorderNetworkModifications(UUID groupId, boolean stashed, JdbcConnection connection, List<SqlStatement> statements, Database database) throws CustomChangeException {
List<UUID> entities = findAllByGroupId(groupId, stashed, connection);
List<Pair<UUID, Integer>> entitiesToUpdate = new ArrayList<>();
if (!entities.isEmpty()) {
if (Boolean.TRUE.equals(stashed)) {
IntStream.range(1, entities.size() + 1)
.forEach(i -> entitiesToUpdate.add(Pair.of(entities.get(i - 1), -i)));
} else {
IntStream.range(0, entities.size())
.forEach(i -> entitiesToUpdate.add(Pair.of(entities.get(i), i)));
}
}
createMigrationRequests(entitiesToUpdate, statements, database);
}

private List<UUID> findAllByGroupId(UUID groupId, boolean stashed, JdbcConnection connection) throws CustomChangeException {
try (PreparedStatement stmt = connection.prepareStatement("SELECT id FROM modification m WHERE m.group_id = ? AND m.stashed = ? order by modifications_order")) {
stmt.setObject(1, groupId);
stmt.setBoolean(2, stashed);
ResultSet resultSet = stmt.executeQuery();
List<UUID> entities = new ArrayList<>();
while (resultSet.next()) {
entities.add(UUID.fromString(resultSet.getString(1)));
}
return entities;
} catch (SQLException | DatabaseException e) {
throw new CustomChangeException(e);
}
}

private void createMigrationRequests(List<Pair<UUID, Integer>> entities, List<SqlStatement> statements, Database database) {
entities.forEach(pair -> statements.add(new UpdateStatement(database.getDefaultCatalogName(), database.getDefaultSchemaName(), "modification")
.addNewColumnValue("modifications_order", pair.getValue())
.setWhereClause(String.format("id='%s'", pair.getKey()))));
}

@Override
public String getConfirmationMessage() {
return "modification order was successfully updated";
}

@Override
public void setUp() throws SetupException {
LOGGER.info("Set up migration for modification order");
}

@Override
public void setFileOpener(ResourceAccessor resourceAccessor) {
LOGGER.info("Set file opener for modification order");
}

@Override
public ValidationErrors validate(Database database) {
return new ValidationErrors();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.gridsuite.modification.server.entities.ModificationEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
Expand All @@ -27,12 +28,21 @@ public interface ModificationRepository extends JpaRepository<ModificationEntity
@Query(value = "SELECT new ModificationEntity(m.id, m.type, m.date, m.stashed, m.activated, m.messageType, m.messageValues) FROM ModificationEntity m WHERE m.group.id = ?1 order by m.modificationsOrder")
List<ModificationEntity> findAllBaseByGroupId(UUID uuid);

@Query(value = "SELECT new ModificationEntity(m.id, m.type, m.date, m.stashed, m.activated, m.messageType, m.messageValues) FROM ModificationEntity m WHERE m.group.id = ?1 order by m.modificationsOrder desc")
List<ModificationEntity> findAllBaseByGroupIdReverse(UUID uuid);

@Query(value = "SELECT m FROM ModificationEntity m WHERE m.group.id = ?1 AND m.stashed = ?2 order by m.modificationsOrder")
List<ModificationEntity> findAllByGroupId(@Param("groupId") UUID groupId, @Param("stashed") Boolean stashed);

@Query(value = "SELECT new ModificationEntity(m.id, m.type) FROM ModificationEntity m WHERE m.id IN (?1)")
List<ModificationEntity> findMetadataIn(List<UUID> uuids);

@Query(value = "SELECT m FROM ModificationEntity m WHERE m.id IN (?1) ORDER BY m.modificationsOrder")
List<ModificationEntity> findAllByIdIn(List<UUID> uuids);

@Query(value = "SELECT m FROM ModificationEntity m WHERE m.id IN (?1) ORDER BY m.modificationsOrder desc")
List<ModificationEntity> findAllByIdInReverse(List<UUID> uuids);

@Query(value = "SELECT cast(modifications_id AS VARCHAR) FROM tabular_modification_modifications WHERE tabular_modification_entity_id = :uuid ORDER BY modifications_order", nativeQuery = true)
List<UUID> findSubModificationIdsByTabularModificationIdOrderByModificationsOrder(UUID uuid);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,27 @@ public UUID createNetworkCompositeModification(@NonNull List<UUID> modificationU
}

private void saveModificationsNonTransactional(UUID groupUuid, List<? extends ModificationEntity> modifications) {
int order = modificationRepository.countByGroupIdAndStashed(groupUuid, false);
var modificationGroupEntity = this.modificationGroupRepository
.findById(groupUuid)
.orElseGet(() -> modificationGroupRepository.save(new ModificationGroupEntity(groupUuid)));
modifications.forEach(m -> {
for (ModificationEntity m : modifications) {
modificationGroupEntity.addModification(m);
// We need here to call the save() method on the modification entity cause the ids of the ModificationEntity's are used in further treatments in the same transaction.
// As we generate the id in Java with @GeneratedValue(strategy = GenerationType.AUTO), saving the entity in the JPA world is enough to generate the id (no need to flush it).
// Without the saving, the id generation would be done only at the flush() and wouldn't be available for the further treatments.
modificationRepository.save(m);
});
m.setModificationsOrder(order);
order++;
}
modificationRepository.saveAll(modifications);
}

@Transactional
// TODO Remove transaction when errors will no longer be sent to the front
public List<ModificationEntity> moveModifications(UUID destinationGroupUuid, UUID originGroupUuid, List<UUID> modificationsToMoveUUID, UUID referenceModificationUuid) {
// read origin group and modifications
ModificationGroupEntity originModificationGroupEntity = getModificationGroup(originGroupUuid);
List<ModificationEntity> originModificationEntities = originModificationGroupEntity.getModifications();

List<ModificationEntity> originModificationEntities = originModificationGroupEntity.getModifications()
.stream()
.filter(modificationEntity -> !modificationEntity.getStashed())
.collect(Collectors.toList());
// To remove null entities when @orderColumn is not a contiguous sequence starting from 0 (to be fixed?)
// (there are several places in this file where we filter non-null modification entities)
originModificationEntities.removeIf(Objects::isNull);
Expand Down Expand Up @@ -139,6 +141,9 @@ private void insertModifications(List<ModificationEntity> modificationsList, Lis
.findFirst()
.orElseThrow(() -> new NetworkModificationException(MOVE_MODIFICATION_ERROR));
modificationsList.addAll(insertionIndex, modificationsToAdd);
for (int order = 0; order < modificationsList.size(); order++) {
modificationsList.get(order).setModificationsOrder(order);
}
}

private List<ModificationEntity> removeModifications(List<ModificationEntity> modificationsList, List<UUID> orderedIdsToRemove) {
Expand Down Expand Up @@ -207,17 +212,19 @@ public List<ModificationInfos> getModifications(UUID groupUuid, boolean onlyMeta
}

public List<ModificationInfos> getModificationsMetadata(UUID groupUuid, boolean onlyStashed) {
Stream<ModificationEntity> modificationEntityStream = modificationRepository
.findAllBaseByGroupId(getModificationGroup(groupUuid).getId())
.stream();
if (onlyStashed) {
return modificationEntityStream.filter(m -> m.getStashed())
.map(this::getModificationInfos)
.collect(Collectors.toList());
return modificationRepository
.findAllBaseByGroupIdReverse(getModificationGroup(groupUuid).getId())
.stream()
.filter(ModificationEntity::getStashed)
.map(this::getModificationInfos)
.collect(Collectors.toList());
} else {
return modificationEntityStream
.map(this::getModificationInfos)
.collect(Collectors.toList());
return modificationRepository
.findAllBaseByGroupId(getModificationGroup(groupUuid).getId())
.stream()
.map(this::getModificationInfos)
.collect(Collectors.toList());
}
}

Expand Down Expand Up @@ -408,25 +415,48 @@ public List<ModificationInfos> getActiveModificationsInfos(@NonNull UUID groupUu
}

@Transactional
public void stashNetworkModifications(@NonNull List<UUID> modificationUuids) {
public void stashNetworkModifications(@NonNull List<UUID> modificationUuids, int stashedModificationCount) {
int stashModificationOrder = -stashedModificationCount - 1;
List<ModificationEntity> modificationEntities = new ArrayList<>();
for (UUID modificationUuid : modificationUuids) {
ModificationEntity modificationEntity = this.modificationRepository
.findById(modificationUuid)
.orElseThrow(() -> new NetworkModificationException(MODIFICATION_NOT_FOUND, String.format(MODIFICATION_NOT_FOUND_MESSAGE, modificationUuid)));
modificationEntity.setStashed(true);
this.modificationRepository.save(modificationEntity);
modificationEntity.setModificationsOrder(stashModificationOrder);
modificationEntities.add(modificationEntity);
stashModificationOrder--;
}
this.modificationRepository.saveAll(modificationEntities);
}

@Transactional
public void restoreNetworkModifications(@NonNull List<UUID> modificationUuids) {
for (UUID modificationUuid : modificationUuids) {
ModificationEntity modificationEntity = this.modificationRepository
.findById(modificationUuid)
.orElseThrow(() -> new NetworkModificationException(MODIFICATION_NOT_FOUND, String.format(MODIFICATION_NOT_FOUND_MESSAGE, modificationUuid)));
modificationEntity.setStashed(false);
this.modificationRepository.save(modificationEntity);
public void reorderNetworkModifications(UUID groupId, Boolean stashed) {
List<ModificationEntity> entities = this.modificationRepository.findAllByGroupId(groupId, stashed);
if (!entities.isEmpty()) {
if (Boolean.TRUE.equals(stashed)) {
IntStream.range(1, entities.size() + 1)
.forEach(i -> entities.get(i - 1).setModificationsOrder(-i));
} else {
IntStream.range(0, entities.size())
.forEach(i -> entities.get(i).setModificationsOrder(i));
}
}
this.modificationRepository.saveAll(entities);
}

@Transactional
public void restoreNetworkModifications(@NonNull List<UUID> modificationUuids, int unstashedSize) {
int modificationOrder = unstashedSize;
List<ModificationEntity> modifications = modificationRepository.findAllByIdInReverse(modificationUuids);
if (modifications.size() != modificationUuids.size()) {
throw new NetworkModificationException(MODIFICATION_NOT_FOUND);
}
for (ModificationEntity modification : modifications) {
modification.setStashed(false);
modification.setModificationsOrder(modificationOrder++);
}
this.modificationRepository.saveAll(modifications);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,19 @@ public void updateNetworkModificationActivation(@NonNull List<UUID> modification
}

@Transactional
public void stashNetworkModifications(@NonNull List<UUID> modificationUuids) {
networkModificationRepository.stashNetworkModifications(modificationUuids);
public void stashNetworkModifications(UUID groupUuid, @NonNull List<UUID> modificationUuids) {
networkModificationRepository.stashNetworkModifications(modificationUuids, networkModificationRepository.getModificationsCount(groupUuid, true));
}

@Transactional
public void restoreNetworkModifications(@NonNull List<UUID> modificationUuids) {
networkModificationRepository.restoreNetworkModifications(modificationUuids);
public void reorderNetworkModifications(UUID groupId, Boolean stashed) {
networkModificationRepository.reorderNetworkModifications(groupId, stashed);
}

@Transactional
public void restoreNetworkModifications(UUID groupUuid, @NonNull List<UUID> modificationUuids) {
networkModificationRepository.restoreNetworkModifications(modificationUuids,
networkModificationRepository.getModificationsCount(groupUuid, false));
}

// No transactional because we need to save modification in DB also in case of error
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="etienne lesot" id="1726146482277-3">
<customChange class="org.gridsuite.modification.server.migration.ModificationOrderMigration"/>
</changeSet>
</databaseChangeLog>
3 changes: 3 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,6 @@ databaseChangeLog:
- include:
relativeToChangelogFile: true
file: changesets/changelog_20240912T130742Z.xml
- include:
relativeToChangelogFile: true
file: changesets/changelog_20241015T130742Z.xml
Loading

0 comments on commit 15816de

Please sign in to comment.