From b89036321caeb9841f1f1a8417e01ccd350f5d68 Mon Sep 17 00:00:00 2001 From: klesaulnier <42617371+klesaulnier@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:59:43 +0100 Subject: [PATCH] Creating modifications should now impact ES index (#365) Signed-off-by: LE SAULNIER Kevin --- .../server/dto/SubstationInfos.java | 6 +- .../server/dto/VoltageLevelInfos.java | 6 +- .../dto/elasticsearch/EquipmentInfos.java | 1 - .../modifications/NetworkStoreListener.java | 85 ++++++++-- .../ModificationElasticsearchTest.java | 147 ++++++++++++++++++ .../server/utils/ModificationCreation.java | 58 +++++++ 6 files changed, 282 insertions(+), 21 deletions(-) create mode 100644 src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java diff --git a/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java b/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java index cb1ae27d5..83ac6de37 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/SubstationInfos.java @@ -6,10 +6,7 @@ */ package org.gridsuite.modification.server.dto; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; /** @@ -17,6 +14,7 @@ */ @SuperBuilder @NoArgsConstructor +@AllArgsConstructor @Getter @ToString @EqualsAndHashCode diff --git a/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java b/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java index 272eb9627..a4d244eb8 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/VoltageLevelInfos.java @@ -6,10 +6,7 @@ */ package org.gridsuite.modification.server.dto; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; /** @@ -17,6 +14,7 @@ */ @SuperBuilder @NoArgsConstructor +@AllArgsConstructor @Getter @ToString @EqualsAndHashCode diff --git a/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java b/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java index 9770e4e69..92b748e6d 100644 --- a/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java +++ b/src/main/java/org/gridsuite/modification/server/dto/elasticsearch/EquipmentInfos.java @@ -101,5 +101,4 @@ public static Set getSubstationsInfos(@NonNull Identifiable .name(vl.getSubstation().map(Substation::getNameOrId).orElse(null)).build()) .collect(Collectors.toSet()); } - } diff --git a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java index 928cda21b..e25fdcf17 100644 --- a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java +++ b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java @@ -6,6 +6,7 @@ */ package org.gridsuite.modification.server.modifications; +import com.google.common.collect.Iterables; import com.powsybl.iidm.network.*; import com.powsybl.network.store.client.NetworkStoreService; import lombok.Setter; @@ -40,6 +41,8 @@ public class NetworkStoreListener implements NetworkListener { private final List createdEquipments = new ArrayList<>(); + private final List modifiedEquipments = new ArrayList<>(); + private final Set networkImpacts = new LinkedHashSet<>(); // TODO : Move to the NetworkModificationApplicator class @@ -92,18 +95,6 @@ public Network getNetwork() { return network; } - @Override - public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) { - networkImpacts.add( - SimpleElementImpact.builder() - .impactType(SimpleElementImpact.SimpleImpactType.MODIFICATION) - .elementType(identifiable.getType()) - .elementId(identifiable.getId()) - .substationIds(getSubstationIds(identifiable)) - .build() - ); - } - private void addSimpleModificationImpact(Identifiable identifiable) { networkImpacts.add( SimpleElementImpact.builder() @@ -130,9 +121,66 @@ public void onElementReplaced(Identifiable identifiable, String attribute, Objec addSimpleModificationImpact(identifiable); } + @Override + public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) { + addSimpleModificationImpact(identifiable); + updateEquipmentIndexation(identifiable, attribute, networkUuid, network.getVariantManager().getWorkingVariantId()); + } + @Override public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) { addSimpleModificationImpact(identifiable); + updateEquipmentIndexation(identifiable, attribute, networkUuid, network.getVariantManager().getWorkingVariantId()); + } + + private void updateEquipmentIndexation(Identifiable identifiable, String attribute, UUID networkUuid, String variantId) { + modifiedEquipments.add(toEquipmentInfos(identifiable, networkUuid, variantId)); + + // because all each equipment carry its linked voltage levels/substations name within its document + // if attribute is "name" and identifiable type is VOLTAGE_LEVEL or SUBSTATION, we need to update all equipments linked to it + if (attribute.equals("name") && (identifiable.getType().equals(IdentifiableType.VOLTAGE_LEVEL) || identifiable.getType().equals(IdentifiableType.SUBSTATION))) { + updateLinkedEquipments(identifiable); + } + } + + private void updateLinkedEquipments(Identifiable identifiable) { + if (identifiable.getType().equals(IdentifiableType.VOLTAGE_LEVEL)) { + VoltageLevel updatedVoltageLevel = network.getVoltageLevel(identifiable.getId()); + // update all equipments linked to voltageLevel + updateEquipmentsLinkedToVoltageLevel(updatedVoltageLevel); + // update substation linked to voltageLevel + Optional linkedSubstation = updatedVoltageLevel.getSubstation(); + if (linkedSubstation.isPresent()) { + modifiedEquipments.add(toEquipmentInfos(linkedSubstation.get(), networkUuid, network.getVariantManager().getWorkingVariantId())); + } + } else if (identifiable.getType().equals(IdentifiableType.SUBSTATION)) { + Substation updatedSubstation = network.getSubstation(identifiable.getId()); + updateEquipmentsLinkedToSubstation(updatedSubstation); + } + } + + private void updateEquipmentsLinkedToSubstation(Substation substation) { + Iterable linkedVoltageLevels = substation.getVoltageLevels(); + // update all voltageLevels linked to substation + linkedVoltageLevels.forEach(vl -> modifiedEquipments.add(toEquipmentInfos(vl, networkUuid, network.getVariantManager().getWorkingVariantId()))); + // update all equipments linked to each of the voltageLevels + linkedVoltageLevels.forEach(vl -> + Iterables.concat( + vl.getConnectables(), + vl.getSwitches() + ).forEach(c -> + modifiedEquipments.add(toEquipmentInfos(c, networkUuid, network.getVariantManager().getWorkingVariantId())) + ) + ); + } + + private void updateEquipmentsLinkedToVoltageLevel(VoltageLevel voltageLevel) { + Iterables.concat( + voltageLevel.getConnectables(), + voltageLevel.getSwitches() + ).forEach(c -> + modifiedEquipments.add(toEquipmentInfos(c, networkUuid, network.getVariantManager().getWorkingVariantId())) + ); } @Override @@ -192,6 +240,18 @@ public NetworkModificationResult flushNetworkModifications() { .build(); } + private static EquipmentInfos toEquipmentInfos(Identifiable identifiable, UUID networkUuid, String variantId) { + return EquipmentInfos.builder() + .networkUuid(networkUuid) + .variantId(variantId) + .id(identifiable.getId()) + .name(identifiable.getNameOrId()) + .type(identifiable.getType().name()) + .voltageLevels(EquipmentInfos.getVoltageLevelsInfos(identifiable)) + .substations(EquipmentInfos.getSubstationsInfos(identifiable)) + .build(); + } + private void flushEquipmentInfos() { String variantId = network.getVariantManager().getWorkingVariantId(); Set presentEquipmentDeletionsIds = equipmentInfosService.findEquipmentInfosList(deletedEquipmentsIds, networkUuid, variantId).stream().map(EquipmentInfos::getId).collect(Collectors.toSet()); @@ -213,5 +273,6 @@ private void flushEquipmentInfos() { equipmentInfosService.deleteEquipmentInfosList(equipmentDeletionsIds, networkUuid, variantId); equipmentInfosService.addAllTombstonedEquipmentInfos(tombstonedEquipmentInfos); equipmentInfosService.addAllEquipmentInfos(createdEquipments); + equipmentInfosService.addAllEquipmentInfos(modifiedEquipments); } } diff --git a/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java b/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java new file mode 100644 index 000000000..100265029 --- /dev/null +++ b/src/test/java/org/gridsuite/modification/server/service/ModificationElasticsearchTest.java @@ -0,0 +1,147 @@ +package org.gridsuite.modification.server.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.iidm.network.LoadType; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.network.store.client.NetworkStoreService; +import com.powsybl.network.store.client.PreloadingStrategy; +import org.gridsuite.modification.server.dto.LoadCreationInfos; +import org.gridsuite.modification.server.dto.LoadModificationInfos; +import org.gridsuite.modification.server.dto.VoltageLevelModificationInfos; +import org.gridsuite.modification.server.elasticsearch.EquipmentInfosRepository; +import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService; +import org.gridsuite.modification.server.repositories.ModificationRepository; +import org.gridsuite.modification.server.utils.ModificationCreation; +import org.gridsuite.modification.server.utils.NetworkCreation; +import org.junit.Before; +import org.junit.jupiter.api.Tag; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +@SpringBootTest +@Tag("IntegrationTest") +public class ModificationElasticsearchTest { + private static UUID NETWORK_UUID = UUID.randomUUID(); + private static final UUID TEST_GROUP_ID = UUID.randomUUID(); + private static final UUID TEST_REPORT_ID = UUID.randomUUID(); + private static final String URI_NETWORK_MODIF_BASE = "/v1/network-modifications"; + private static final String URI_NETWORK_MODIF_PARAMS = "&groupUuid=" + TEST_GROUP_ID + "&reportUuid=" + TEST_REPORT_ID + "&reporterId=" + UUID.randomUUID(); + private static final String URI_NETWORK_MODIF = URI_NETWORK_MODIF_BASE + "?networkUuid=" + NETWORK_UUID + URI_NETWORK_MODIF_PARAMS; + + private static final String NEW_VARIANT = "NewVariant"; + private static final String NEW_VARIANT_2 = "NewVariant2"; + + @Autowired + MockMvc mockMvc; + + @Autowired + private ObjectMapper mapper; + + @Autowired + EquipmentInfosService equipmentInfosService; + + @Autowired + ModificationRepository modificationRepository; + + @MockBean + NetworkStoreService networkStoreService; + + @MockBean + ReportService reportService; + + @Autowired + private EquipmentInfosRepository equipmentInfosRepository; + + Network network; + + @Before + public void setUp() { + network = NetworkCreation.create(NETWORK_UUID, true); + when(networkStoreService.getNetwork(eq(NETWORK_UUID), nullable(PreloadingStrategy.class))).then((Answer) invocation -> network); + + // clean DB + modificationRepository.deleteAll(); + equipmentInfosService.deleteAll(); + } + + @Test + public void testModificationsToImpactElasticsearch() throws Exception { + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, NEW_VARIANT); + network.getVariantManager().setWorkingVariant(NEW_VARIANT); + + // load creation - assert name and vl name values + LoadCreationInfos loadCreationInfos = ModificationCreation.getCreationLoad("v1", "v1Load", "v1load_name", "1.1", LoadType.UNDEFINED); + String loadCreationJson = mapper.writeValueAsString(loadCreationInfos); + mockMvc.perform(post(URI_NETWORK_MODIF).content(loadCreationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + assertEquals("v1load_name", equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getName()); + assertTrue(equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getVoltageLevels().stream().anyMatch(vl -> vl.getName().equals("v1"))); + + // load modification - assert name modification + LoadModificationInfos loadModification = ModificationCreation.getModificationLoad("v1Load", null, "v1load_newname", null, null, null, null); + String loadModificationJson = mapper.writeValueAsString(loadModification); + mockMvc.perform(post(URI_NETWORK_MODIF).content(loadModificationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + assertEquals("v1load_newname", equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1Load"), NETWORK_UUID, NEW_VARIANT).get(0).getName()); + } + + @Test + public void testVLModificationsToImpactElasticsearch() throws Exception { + network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, NEW_VARIANT); + network.getVariantManager().setWorkingVariant(NEW_VARIANT); + + // vl modification - assert vl name modification, linked load modification and parent subsation modification + VoltageLevelModificationInfos vlModification = ModificationCreation.getModificationVoltageLevel("v1", "v1_newname"); + String vlModificationJson = mapper.writeValueAsString(vlModification); + mockMvc.perform(post(URI_NETWORK_MODIF).content(vlModificationJson).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT, "v1_newname"); + + // in a new variant, load modification - assert name modification - assert old data are still here + network.getVariantManager().cloneVariant(NEW_VARIANT, NEW_VARIANT_2); + network.getVariantManager().setWorkingVariant(NEW_VARIANT_2); + // vl modification - assert vl name modification, linked load modification and parent subsation modification + VoltageLevelModificationInfos vlModification2 = ModificationCreation.getModificationVoltageLevel("v1", "v1_newname_2"); + String vlModification2Json = mapper.writeValueAsString(vlModification2); + mockMvc.perform(post(URI_NETWORK_MODIF).content(vlModification2Json).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT, "v1_newname"); + checkSomeEquipmentsVoltageLevel(NETWORK_UUID, NEW_VARIANT_2, "v1_newname_2"); + } + + private void checkSomeEquipmentsVoltageLevel(UUID networkUuid, String variantId, String voltageLevelName) { + // assert targeted voltage level has been updated + assertEquals(voltageLevelName, equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of("v1"), networkUuid, variantId).get(0).getName()); + + // assert linked load has been updated (linked connectable) + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "v1Load", voltageLevelName)); + + // assert linked switch has been updated (linked equipment which is not a connectable) + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "v1dlcc", voltageLevelName)); + + // assert parent substation has been updated + assertTrue(checkEquipmentHasVoltageLevelWithName(networkUuid, variantId, "s1", voltageLevelName)); + } + + private boolean checkEquipmentHasVoltageLevelWithName(UUID networkUuid, String variantId, String equipmentId, String voltageLevelName) { + return equipmentInfosRepository.findByIdInAndNetworkUuidAndVariantId(List.of(equipmentId), networkUuid, variantId).get(0).getVoltageLevels().stream().anyMatch(vl -> vl.getName().equals(voltageLevelName)); + } +} diff --git a/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java b/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java index ca7b82248..76f661ec4 100644 --- a/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java +++ b/src/test/java/org/gridsuite/modification/server/utils/ModificationCreation.java @@ -8,6 +8,7 @@ package org.gridsuite.modification.server.utils; import com.powsybl.iidm.network.EnergySource; +import com.powsybl.iidm.network.LoadType; import com.powsybl.iidm.network.SwitchKind; import com.powsybl.iidm.network.extensions.ConnectablePosition; import org.gridsuite.modification.server.dto.*; @@ -94,4 +95,61 @@ public static GeneratorCreationInfos getCreationGenerator(String vlId, String ge .connectionDirection(ConnectablePosition.Direction.TOP) .build(); } + + public static LoadCreationInfos getCreationLoad(String vlId, String loadId, String loadName, String busOrBusBarSectionId, LoadType loadType) { + return LoadCreationInfos.builder() + .stashed(false) + .equipmentId(loadId) + .equipmentName(loadName) + .voltageLevelId(vlId) + .busOrBusbarSectionId(busOrBusBarSectionId) + .loadType(loadType) + .activePower(100.0) + .reactivePower(20.0) + .connectionName("top") + .connectionDirection(ConnectablePosition.Direction.TOP) + .build(); + } + + public static LoadModificationInfos getModificationLoad(String loadId, String vlId, String loadName, String busOrBusbarSectionId, LoadType loadType, Long activePower, Long reactivePower) { + LoadModificationInfos.LoadModificationInfosBuilder builder = LoadModificationInfos.builder() + .stashed(false) + .equipmentId(loadId); + + if (loadName != null) { + builder.equipmentName(AttributeModification.toAttributeModification(loadName, OperationType.SET)); + } + + if (vlId != null) { + builder.voltageLevelId(AttributeModification.toAttributeModification(vlId, OperationType.SET)); + } + + if (busOrBusbarSectionId != null) { + builder.busOrBusbarSectionId(AttributeModification.toAttributeModification(busOrBusbarSectionId, OperationType.SET)); + } + + if (loadType != null) { + builder.loadType(AttributeModification.toAttributeModification(LoadType.UNDEFINED, OperationType.SET)); + } + + if (activePower != null) { + builder.activePower(AttributeModification.toAttributeModification(activePower, OperationType.SET)); + } + + if (reactivePower != null) { + builder.reactivePower(AttributeModification.toAttributeModification(reactivePower, OperationType.SET)); + } + + return builder.build(); + } + + public static VoltageLevelModificationInfos getModificationVoltageLevel(String vlId, String vlName) { + VoltageLevelModificationInfos.VoltageLevelModificationInfosBuilder builder = VoltageLevelModificationInfos.builder() + .stashed(false) + .equipmentId(vlId); + + builder.equipmentName(AttributeModification.toAttributeModification(vlName, OperationType.SET)); + + return builder.build(); + } }