Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
<sonar.coverage.exclusions>**/migration/**/*</sonar.coverage.exclusions>
<sonar.organization>gridsuite</sonar.organization>
<sonar.projectKey>org.gridsuite:network-modification-server</sonar.projectKey>
<network-modification.version>1.7.0</network-modification.version>
<network-modification.version>1.8.0-SNAPSHOT</network-modification.version>
<filter.version>1.30.0-SNAPSHOT</filter.version>
</properties>

<build>
Expand Down Expand Up @@ -98,6 +99,11 @@
<artifactId>gridsuite-network-modification</artifactId>
<version>${network-modification.version}</version>
</dependency>
<dependency>
<groupId>org.gridsuite</groupId>
<artifactId>gridsuite-filter</artifactId>
<version>${filter.version}</version>
</dependency>
<dependency><!-- To remove when integrate in next release of gridsuite-dependencies or powsybl-ws-dependencies -->
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-bom</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import lombok.Setter;
import org.gridsuite.modification.dto.FilterInfos;
import org.gridsuite.modification.dto.byfilter.assignment.*;
import org.gridsuite.modification.modifications.data.assignment.DataType;
import org.gridsuite.modification.server.entities.equipment.modification.VariationFilterEntity;
import org.gridsuite.modification.server.entities.equipment.modification.byfilter.AbstractAssignmentEntity;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import lombok.NoArgsConstructor;
import org.gridsuite.modification.dto.FilterInfos;
import org.gridsuite.modification.dto.byfilter.formula.FormulaInfos;
import org.gridsuite.modification.dto.byfilter.formula.Operator;
import org.gridsuite.modification.dto.byfilter.formula.ReferenceFieldOrValue;
import org.gridsuite.modification.modifications.data.assignment.Operator;
import org.gridsuite.modification.modifications.data.assignment.ReferenceFieldOrValue;
import org.gridsuite.modification.server.entities.equipment.modification.VariationFilterEntity;
import org.gridsuite.modification.server.entities.equipment.modification.byfilter.AbstractAssignmentEntity;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

Expand All @@ -56,6 +57,8 @@ public class NetworkModificationApplicator {

@Getter private final FilterService filterService;

private final FilterLoader filterLoader;

@Getter private final LoadFlowService loadFlowService;

private final LargeNetworkModificationExecutionService largeNetworkModificationExecutionService;
Expand All @@ -72,7 +75,7 @@ public class NetworkModificationApplicator {

public NetworkModificationApplicator(NetworkStoreService networkStoreService, EquipmentInfosService equipmentInfosService,
ModificationApplicationInfosService applicationInfosService,
ReportService reportService, FilterService filterService,
ReportService reportService, FilterService filterService, FilterLoader filterLoader,
LoadFlowService loadFlowService,
NetworkModificationObserver networkModificationObserver,
LargeNetworkModificationExecutionService largeNetworkModificationExecutionService) {
Expand All @@ -81,6 +84,7 @@ public NetworkModificationApplicator(NetworkStoreService networkStoreService, Eq
this.applicationInfosService = applicationInfosService;
this.reportService = reportService;
this.filterService = filterService;
this.filterLoader = filterLoader;
this.loadFlowService = loadFlowService;
this.networkModificationObserver = networkModificationObserver;
this.largeNetworkModificationExecutionService = largeNetworkModificationExecutionService;
Expand Down Expand Up @@ -196,6 +200,7 @@ private ApplicationStatus apply(ModificationApplicationGroup modificationGroupIn
UUID reporterId = modificationGroupInfos.reportInfos().getNodeUuid();
reportNode = ReportNode.newRootReportNode()
.withAllResourceBundlesFromClasspath()
.withLocale(Locale.ENGLISH)
.withMessageTemplate("network.modification.server.nodeUuid")
.withUntypedValue("nodeUuid", reporterId.toString())
.build();
Expand Down Expand Up @@ -223,7 +228,7 @@ private ApplicationStatus apply(ModificationApplicationGroup modificationGroupIn
private ApplicationStatus apply(ModificationInfos modificationInfos, Network network, ReportNode reportNode) {
ReportNode subReportNode = modificationInfos.createSubReportNode(reportNode);
try {
networkModificationObserver.observeApply(modificationInfos.getType(), () -> apply(modificationInfos.toModification(), network, subReportNode));
networkModificationObserver.observeApply(modificationInfos.getType(), () -> apply(modificationInfos.toModification(filterLoader), network, subReportNode));
} catch (Exception e) {
handleException(subReportNode, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.google.common.collect.Lists;
import lombok.NonNull;
import org.apache.commons.collections4.CollectionUtils;
import org.gridsuite.filter.wip.FilterLoader;
import org.gridsuite.modification.ModificationType;
import org.gridsuite.modification.dto.CompositeModificationInfos;
import org.gridsuite.modification.dto.ModificationInfos;
Expand Down Expand Up @@ -75,6 +76,7 @@ public class NetworkModificationRepository {
private final CompositeContainerRepository compositeContainerRepository;

private final ModificationApplicationInfosService modificationApplicationInfosService;
private final FilterLoader filterLoader;

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

Expand All @@ -96,7 +98,8 @@ public NetworkModificationRepository(ModificationGroupRepository modificationGro
CompositeModificationRepository compositeModificationRepository,
CompositeContainerRepository compositeContainerRepository,
ModificationContainerRepository modificationContainerRepository,
ModificationApplicationInfosService modificationApplicationInfosService) {
ModificationApplicationInfosService modificationApplicationInfosService,
FilterLoader filterLoader) {
this.modificationGroupRepository = modificationGroupRepository;
this.modificationRepository = modificationRepository;
this.generatorCreationRepository = generatorCreationRepository;
Expand All @@ -116,6 +119,7 @@ public NetworkModificationRepository(ModificationGroupRepository modificationGro
this.compositeContainerRepository = compositeContainerRepository;
this.modificationContainerRepository = modificationContainerRepository;
this.modificationApplicationInfosService = modificationApplicationInfosService;
this.filterLoader = filterLoader;
}

private NetworkModificationServerException getModificationContainerNotFoundException(String containerId, ModificationContainerType containerType) {
Expand Down Expand Up @@ -612,14 +616,14 @@ public ModificationInfos getModificationInfo(UUID modificationUuid) {

@Transactional(readOnly = true)
public AbstractModification getStandaloneNetworkModification(UUID modificationUuid) {
return toModificationsInfosOptimized(getModificationEntity(modificationUuid)).toModification();
return toModificationsInfosOptimized(getModificationEntity(modificationUuid)).toModification(filterLoader);
}

@Transactional(readOnly = true)
public Map<UUID, AbstractModification> getStandaloneNetworkModifications(List<UUID> modificationUuids, boolean errorOnModificationNotFound) {
return getModificationEntities(modificationUuids, errorOnModificationNotFound).stream()
.map(this::toModificationsInfosOptimized)
.collect(Collectors.toMap(ModificationInfos::getUuid, ModificationInfos::toModification));
.collect(Collectors.toMap(ModificationInfos::getUuid, m -> m.toModification(filterLoader)));
}

public List<ModificationEntity> getModificationEntities(List<UUID> modificationUuids, boolean errorOnModificationNotFound) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.modification.server.service;

import org.gridsuite.filter.wip.Filter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.List;
import java.util.UUID;

/**
* @author joris mancini <joris.mancini_externe at rte-france.com>
*/
@Service
public class FilterLoader implements org.gridsuite.filter.wip.FilterLoader {

private static final String FILTER_SERVER_API_VERSION = "v1";

private static final String DELIMITER = "/";

private static String filterServerBaseUri;

private final RestTemplate restTemplate;

public FilterLoader(@Value("${gridsuite.services.filter-server.base-uri:http://filter-server/}") String filterServerBaseUri,
RestTemplate restTemplate) {
setFilterServerBaseUri(filterServerBaseUri);
this.restTemplate = restTemplate;
}

public static void setFilterServerBaseUri(String filterServerBaseUri) {
FilterLoader.filterServerBaseUri = filterServerBaseUri;
}

@Override
public List<Filter> load(List<UUID> filtersUuids) {
if (filtersUuids == null || filtersUuids.isEmpty()) {
return List.of();
}
String path = UriComponentsBuilder.fromPath(DELIMITER
+ FILTER_SERVER_API_VERSION
+ "/standalone-filters")
.queryParam("ids", filtersUuids)
.buildAndExpand()
.toUriString();
return restTemplate.exchange(filterServerBaseUri + path, HttpMethod.GET, null, new ParameterizedTypeReference<List<Filter>>() { }).getBody();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package org.gridsuite.modification.server.modifications.byfilter;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.matching.MultiValuePattern;
import com.powsybl.iidm.network.IdentifiableType;
import lombok.SneakyThrows;
import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.filter.wip.Filter;
import org.gridsuite.filter.wip.IdentifierListFilter;
import org.gridsuite.modification.server.modifications.AbstractNetworkModificationTest;
import org.gridsuite.modification.server.utils.FilterStub;
import org.gridsuite.modification.server.utils.StubbedFilterRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author Joris Mancini <joris.mancini_externe at rte-france.com>
*/
public abstract class AbstractByFilterTest extends AbstractNetworkModificationTest {

protected static final String PATH = "/v1/standalone-filters";

protected abstract Map<UUID, Set<String>> getFilterMapping();

protected abstract IdentifiableType getIdentifiableType();

protected abstract EquipmentType getEquipmentType();

protected FilterStub createFilterStub(UUID filterID, Collection<String> equipmentIds) {
return new FilterStub(filterID, equipmentFilter(Set.copyOf(equipmentIds)));
}

protected Filter equipmentFilter(Set<String> equipmentIds) {
return IdentifierListFilter.builder()
.equipmentType(getEquipmentType())
.equipmentIds(equipmentIds)
.build();
}

protected List<StubbedFilterRequest> stubStandaloneFilterRequests(List<List<UUID>> filterIdsList) {
Map<Set<UUID>, Integer> requestCounts = new LinkedHashMap<>();
filterIdsList.forEach(filterIds -> requestCounts.merge(filterIds.stream().collect(Collectors.toSet()), 1, Integer::sum));

Map<UUID, Filter> filtersById = getFilterMapping().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> equipmentFilter(entry.getValue())));

List<StubbedFilterRequest> stubbedFilterRequests = new ArrayList<>();
for (Map.Entry<Set<UUID>, Integer> requestCount : requestCounts.entrySet()) {
List<FilterStub> filterStubs = requestCount.getKey().stream()
.map(filterId -> new FilterStub(filterId, Objects.requireNonNull(filtersById.get(filterId))))
.toList();
stubbedFilterRequests.add(new StubbedFilterRequest(stubStandaloneFilters(filterStubs), requestCount.getKey(), requestCount.getValue()));
}
return stubbedFilterRequests;
}

@SneakyThrows
protected UUID stubStandaloneFilters(List<FilterStub> filterStubs) {
List<UUID> filterIds = filterStubs.stream().map(FilterStub::id).toList();
return wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo(PATH))
.withQueryParam("ids", havingExactlyIdsIgnoringOrder(filterIds))
.willReturn(WireMock.ok()
.withBody(mapper.writeValueAsString(filterStubs.stream().map(FilterStub::filter).toList()))
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))).getId();
}

protected void verifyStandaloneFiltersRequest(UUID stubId, Set<UUID> filterIds) {
verifyStandaloneFiltersRequest(stubId, filterIds, 1);
}

protected void verifyStandaloneFiltersRequest(UUID stubId, Set<UUID> filterIds, int nbRequests) {
wireMockUtils.verifyGetRequest(stubId, PATH, "ids", havingExactlyIdsIgnoringOrder(filterIds), false, nbRequests);
}

protected void verifyStandaloneFiltersRequests(List<StubbedFilterRequest> stubs) {
stubs.forEach(stub -> wireMockUtils.verifyGetRequest(
stub.stubId(), PATH, "ids", havingExactlyIdsIgnoringOrder(stub.filterIds()), false, stub.requestCount()));
}

protected MultiValuePattern havingExactlyIdsIgnoringOrder(Collection<UUID> filterIds) {
String[] expectedIds = filterIds.stream()
.map(UUID::toString)
.distinct()
.toArray(String[]::new);
return WireMock.havingExactly(expectedIds);
}
}
Loading
Loading