diff --git a/pom.xml b/pom.xml
index b851df1c00..0e2a545105 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,9 +44,10 @@
43.0.0
+ 1.12.0
org.gridsuite.study.server
5.0.0-alpha.14
- 1.0.5
+ 1.0.5
gridsuite
org.gridsuite:study-server
@@ -144,6 +145,11 @@
com.powsybl
powsybl-ws-commons
+
+ org.gridsuite
+ gridsuite-filter
+ ${gridsuite-filter.version}
+
io.projectreactor
reactor-core
diff --git a/src/main/java/org/gridsuite/study/server/StudyConstants.java b/src/main/java/org/gridsuite/study/server/StudyConstants.java
index 82046a6397..983c242584 100644
--- a/src/main/java/org/gridsuite/study/server/StudyConstants.java
+++ b/src/main/java/org/gridsuite/study/server/StudyConstants.java
@@ -46,6 +46,7 @@ private StudyConstants() {
public static final String QUERY_PARAM_VARIANT_ID = "variantId";
public static final String QUERY_PARAM_NETWORK_UUID = "networkUuid";
public static final String QUERY_PARAM_EQUIPMENT_TYPE = "equipmentType";
+ public static final String QUERY_PARAM_EQUIPMENT_TYPES = "equipmentTypes";
public static final String QUERY_PARAM_ELEMENT_TYPE = "elementType";
public static final String QUERY_PARAM_SIDE = "side";
public static final String QUERY_PARAM_NOMINAL_VOLTAGES = "nominalVoltages";
diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java
index b56f32837c..2af0a6f12a 100644
--- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java
+++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java
@@ -16,6 +16,8 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
+import org.gridsuite.filter.globalfilter.GlobalFilter;
+import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.study.server.StudyApi;
import org.gridsuite.study.server.StudyConstants.ModificationsActionType;
import org.gridsuite.study.server.StudyConstants.SldDisplayMode;
@@ -54,6 +56,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
@@ -2256,6 +2259,20 @@ public ResponseEntity evaluateFilter(
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(studyService.evaluateFilter(nodeUuid, rootNetworkUuid, inUpstreamBuiltParentNode, filter));
}
+ @PostMapping(value = "/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/global-filter/evaluate",
+ produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
+ @Operation(summary = "Evaluate a global filter to get matched elements")
+ @ApiResponse(responseCode = "200", description = "The list of matched elements")
+ public ResponseEntity> evaluateGlobalFilter(
+ @Parameter(description = "Study uuid") @PathVariable("studyUuid") UUID studyUuid,
+ @Parameter(description = "Root network uuid") @PathVariable("rootNetworkUuid") UUID rootNetworkUuid,
+ @Parameter(description = "Node uuid") @PathVariable("nodeUuid") UUID nodeUuid,
+ @Parameter(description = "The equipments types to filter and return") @RequestParam(name = "equipmentTypes") @NonNull final List equipmentTypes,
+ @RequestBody @NonNull GlobalFilter filter) {
+ this.studyService.assertIsRootNetworkAndNodeInStudy(studyUuid, rootNetworkUuid, nodeUuid);
+ return ResponseEntity.ok(studyService.evaluateGlobalFilter(nodeUuid, rootNetworkUuid, equipmentTypes, filter));
+ }
+
@GetMapping(value = "/studies/{studyUuid}/root-networks/{rootNetworkUuid}/filters/{filterUuid}/elements")
@Operation(summary = "Evaluate a filter on root node to get matched elements")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The list of matched elements")})
diff --git a/src/main/java/org/gridsuite/study/server/service/FilterService.java b/src/main/java/org/gridsuite/study/server/service/FilterService.java
index f93a61a927..bb35a77d31 100644
--- a/src/main/java/org/gridsuite/study/server/service/FilterService.java
+++ b/src/main/java/org/gridsuite/study/server/service/FilterService.java
@@ -7,16 +7,20 @@
package org.gridsuite.study.server.service;
+import lombok.Getter;
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+import org.gridsuite.filter.globalfilter.GlobalFilter;
+import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.study.server.RemoteServicesProperties;
import org.gridsuite.study.server.StudyException;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
@@ -25,6 +29,10 @@
import static org.gridsuite.study.server.StudyConstants.DELIMITER;
import static org.gridsuite.study.server.StudyConstants.FILTER_API_VERSION;
+import static org.gridsuite.study.server.StudyConstants.IDS;
+import static org.gridsuite.study.server.StudyConstants.NETWORK_UUID;
+import static org.gridsuite.study.server.StudyConstants.QUERY_PARAM_EQUIPMENT_TYPES;
+import static org.gridsuite.study.server.StudyConstants.QUERY_PARAM_VARIANT_ID;
import static org.gridsuite.study.server.StudyException.Type.EVALUATE_FILTER_FAILED;
import static org.gridsuite.study.server.StudyException.Type.NETWORK_NOT_FOUND;
import static org.gridsuite.study.server.utils.StudyUtils.handleHttpError;
@@ -42,13 +50,9 @@ public class FilterService {
private final RestTemplate restTemplate;
+ @Getter // getter to facilitate to mock
private final String baseUri;
- // getter to facilitate to mock
- public String getBaseUri() {
- return baseUri;
- }
-
@Autowired
public FilterService(RemoteServicesProperties remoteServicesProperties, RestTemplate restTemplate) {
this.baseUri = remoteServicesProperties.getServiceUri("filter-server");
@@ -60,12 +64,11 @@ public String evaluateFilter(UUID networkUuid, String variantId, String filter)
String endPointUrl = getBaseUri() + DELIMITER + FILTER_API_VERSION + FILTER_END_POINT_EVALUATE;
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl);
- uriComponentsBuilder.queryParam("networkUuid", networkUuid);
- if (variantId != null && !variantId.isBlank()) {
- uriComponentsBuilder.queryParam("variantId", variantId);
+ uriComponentsBuilder.queryParam(NETWORK_UUID, networkUuid);
+ if (!StringUtils.isBlank(variantId)) {
+ uriComponentsBuilder.queryParam(QUERY_PARAM_VARIANT_ID, variantId);
}
- var uriComponent = uriComponentsBuilder
- .build();
+ var uriComponent = uriComponentsBuilder.build();
var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
@@ -83,13 +86,35 @@ public String evaluateFilter(UUID networkUuid, String variantId, String filter)
}
}
+ public List evaluateGlobalFilter(@NonNull final UUID networkUuid, @NonNull final String variantId,
+ @NonNull final List equipmentTypes, @NonNull final GlobalFilter filter) {
+ final UriComponents uriComponent = UriComponentsBuilder.fromHttpUrl(getBaseUri())
+ .pathSegment(FILTER_API_VERSION, "global-filter")
+ .queryParam(NETWORK_UUID, networkUuid)
+ .queryParam(QUERY_PARAM_VARIANT_ID, variantId)
+ .queryParam(QUERY_PARAM_EQUIPMENT_TYPES, equipmentTypes)
+ .build();
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ try {
+ return restTemplate.exchange(uriComponent.toUri(), HttpMethod.POST, new HttpEntity<>(filter, headers), new ParameterizedTypeReference>() { })
+ .getBody();
+ } catch (final HttpStatusCodeException ex) {
+ if (HttpStatus.NOT_FOUND.equals(ex.getStatusCode())) {
+ throw new StudyException(NETWORK_NOT_FOUND);
+ } else {
+ throw handleHttpError(ex, EVALUATE_FILTER_FAILED);
+ }
+ }
+ }
+
public String exportFilter(UUID networkUuid, UUID filterUuid) {
Objects.requireNonNull(networkUuid);
Objects.requireNonNull(filterUuid);
String endPointUrl = getBaseUri() + DELIMITER + FILTER_API_VERSION + FILTER_END_POINT_EXPORT;
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl);
- uriComponentsBuilder.queryParam("networkUuid", networkUuid);
+ uriComponentsBuilder.queryParam(NETWORK_UUID, networkUuid);
var uriComponent = uriComponentsBuilder.buildAndExpand(filterUuid);
return restTemplate.getForObject(uriComponent.toUriString(), String.class);
@@ -101,11 +126,11 @@ public String exportFilters(UUID networkUuid, List filtersUuid, String var
String endPointUrl = getBaseUri() + DELIMITER + FILTER_API_VERSION + FILTERS_END_POINT_EXPORT;
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl);
- uriComponentsBuilder.queryParam("networkUuid", networkUuid);
- if (variantId != null && !variantId.isBlank()) {
- uriComponentsBuilder.queryParam("variantId", variantId);
+ uriComponentsBuilder.queryParam(NETWORK_UUID, networkUuid);
+ if (!StringUtils.isBlank(variantId)) {
+ uriComponentsBuilder.queryParam(QUERY_PARAM_VARIANT_ID, variantId);
}
- uriComponentsBuilder.queryParam("ids", filtersUuid);
+ uriComponentsBuilder.queryParam(IDS, filtersUuid);
var uriComponent = uriComponentsBuilder.buildAndExpand();
return restTemplate.getForObject(uriComponent.toUriString(), String.class);
@@ -117,7 +142,7 @@ public String evaluateFilters(UUID networkUuid, String filters) {
String endPointUrl = getBaseUri() + DELIMITER + FILTER_API_VERSION + FILTER_END_POINT_EVALUATE_IDS;
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl);
- uriComponentsBuilder.queryParam("networkUuid", networkUuid);
+ uriComponentsBuilder.queryParam(NETWORK_UUID, networkUuid);
var uriComponent = uriComponentsBuilder.buildAndExpand();
var headers = new HttpHeaders();
diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java
index ec75331e2f..78fc03dd0b 100644
--- a/src/main/java/org/gridsuite/study/server/service/StudyService.java
+++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java
@@ -14,6 +14,8 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import org.apache.commons.collections4.CollectionUtils;
+import org.gridsuite.filter.globalfilter.GlobalFilter;
+import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.study.server.StudyConstants;
import org.gridsuite.study.server.StudyException;
import org.gridsuite.study.server.dto.*;
@@ -296,6 +298,14 @@ public BasicStudyInfos createStudy(UUID caseUuid, String userId, UUID studyUuid,
return basicStudyInfos;
}
+ @Transactional(readOnly = true)
+ public void assertIsRootNetworkAndNodeInStudy(@NonNull final UUID studyUuid, @NonNull final UUID rootNetworkId, @NonNull final UUID nodeUuid) {
+ this.rootNetworkService.assertIsRootNetworkInStudy(studyUuid, rootNetworkId);
+ if (!studyUuid.equals(this.networkModificationTreeService.getStudyUuidForNodeId(nodeUuid))) {
+ throw new StudyException(NODE_NOT_FOUND);
+ }
+ }
+
@Transactional
public void deleteRootNetworks(UUID studyUuid, List rootNetworksUuids) {
assertIsStudyExist(studyUuid);
@@ -3202,6 +3212,17 @@ public String evaluateFilter(UUID nodeUuid, UUID rootNetworkUuid, boolean inUpst
return filterService.evaluateFilter(rootNetworkService.getNetworkUuid(rootNetworkUuid), networkModificationTreeService.getVariantId(nodeUuidToSearchIn, rootNetworkUuid), filter);
}
+ @Transactional(readOnly = true)
+ public List evaluateGlobalFilter(@NonNull final UUID nodeUuid, @NonNull final UUID rootNetworkUuid,
+ @NonNull final List equipmentTypes, @NonNull final GlobalFilter filter) {
+ return filterService.evaluateGlobalFilter(
+ rootNetworkService.getNetworkUuid(rootNetworkUuid),
+ networkModificationTreeService.getVariantId(getNodeUuidToSearchIn(nodeUuid, rootNetworkUuid, true), rootNetworkUuid),
+ equipmentTypes,
+ filter
+ );
+ }
+
@Transactional(readOnly = true)
public String exportFilter(UUID rootNetworkUuid, UUID filterUuid) {
return filterService.exportFilter(rootNetworkService.getNetworkUuid(rootNetworkUuid), filterUuid);
diff --git a/src/test/java/org/gridsuite/study/server/FilterServiceTest.java b/src/test/java/org/gridsuite/study/server/FilterServiceTest.java
index 0eceb20b81..3b95d56885 100644
--- a/src/test/java/org/gridsuite/study/server/FilterServiceTest.java
+++ b/src/test/java/org/gridsuite/study/server/FilterServiceTest.java
@@ -10,6 +10,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.powsybl.commons.exceptions.UncheckedInterruptedException;
+import org.gridsuite.filter.utils.EquipmentType;
+import org.gridsuite.study.server.networkmodificationtree.dto.InsertMode;
+import org.gridsuite.study.server.networkmodificationtree.dto.NetworkModificationNode;
import org.gridsuite.study.server.networkmodificationtree.dto.RootNode;
import org.gridsuite.study.server.repository.StudyEntity;
import org.gridsuite.study.server.repository.StudyRepository;
@@ -38,6 +41,7 @@
import java.util.stream.Collectors;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.gridsuite.study.server.utils.TestUtils.createModificationNodeInfo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -58,6 +62,7 @@ class FilterServiceTest {
private static final String FILTER_UUID_STRING = "c6c15d08-81e9-47a1-9cdb-7be22f017ad5";
private static final List FILTERS_UUID_STRING = List.of("fc3aa057-5fa4-4173-b1a8-16028f5eefd1", "f8773f32-f77c-4126-8c6f-a4af8bf6f788");
private static final UUID CASE_UUID = UUID.fromString(CASE_UUID_STRING);
+ private static final String NODE_1_NAME = "node1";
@Autowired
private MockMvc mockMvc;
@@ -126,6 +131,7 @@ void testEvaluateFilter() throws Exception {
UUID studyNameUserIdUuid = studyEntity.getId();
UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid);
UUID rootNodeUuid = getRootNode(studyNameUserIdUuid).getId();
+ NetworkModificationNode firstNode = networkModificationTreeService.createNode(studyEntity, rootNodeUuid, createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null);
// whatever string is allowed but given here a json string for more expressive
final String sendBody = """
@@ -165,6 +171,49 @@ void testEvaluateFilter() throws Exception {
assertEquals(responseBody, resultAsString);
wireMockUtils.verifyFilterEvaluate(stubUuid, NETWORK_UUID_STRING);
+
+ // evaluate on first node
+ mvcResult = mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/filters/evaluate",
+ studyNameUserIdUuid, firstRootNetworkUuid, firstNode.getId())
+ .content(sendBody).contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+ resultAsString = mvcResult.getResponse().getContentAsString();
+ assertEquals(responseBody, resultAsString);
+
+ wireMockUtils.verifyFilterEvaluate(stubUuid, NETWORK_UUID_STRING);
+ }
+
+ @Test
+ void testEvaluateGlobalFilter() throws Exception {
+ StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID);
+ UUID studyNameUserIdUuid = studyEntity.getId();
+ UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid);
+ UUID rootNodeUuid = getRootNode(studyNameUserIdUuid).getId();
+
+ // whatever string is allowed but given here a json string for more expressive
+ final String sendBody = """
+ {
+ "nominalV" : ["380", "225"],
+ "countryCode": ["FR", "BE"],
+ "genericFilter": ["c6c15d08-81e9-47a1-9cdb-7be22f017ad5"],
+ "substationProperty": {}
+ }
+ """;
+
+ String responseBody = "[\"GEN\"]";
+
+ UUID stubUuid = wireMockUtils.stubGlobalFilterEvaluate(NETWORK_UUID_STRING, List.of(EquipmentType.GENERATOR), responseBody);
+
+ MvcResult mvcResult = mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/global-filter/evaluate?equipmentTypes=GENERATOR",
+ studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid)
+ .content(sendBody).contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+ String resultAsString = mvcResult.getResponse().getContentAsString();
+ assertEquals(responseBody, resultAsString);
+
+ wireMockUtils.verifyGlobalFilterEvaluate(stubUuid, NETWORK_UUID_STRING, List.of(EquipmentType.GENERATOR));
}
@Test
@@ -330,6 +379,8 @@ void testExportFilters() throws Exception {
UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyEntity.getId());
UUID studyUuid = studyEntity.getId();
UUID rootNodeUuid = getRootNode(studyUuid).getId();
+ NetworkModificationNode firstNode = networkModificationTreeService.createNode(studyEntity, rootNodeUuid, createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null);
+
String responseBody = """
[
{
@@ -371,5 +422,15 @@ void testExportFilters() throws Exception {
assertEquals(responseBody, resultAsString);
wireMockUtils.verifyFiltersExport(stubUuid, FILTERS_UUID_STRING, NETWORK_UUID_STRING);
+
+ // export on first node
+ mvcResult = mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/filters/elements?filtersUuid=" + FILTERS_UUID_STRING.stream().collect(Collectors.joining(",")),
+ studyUuid, firstRootNetworkUuid, firstNode.getId()))
+ .andExpect(status().isOk())
+ .andReturn();
+ resultAsString = mvcResult.getResponse().getContentAsString();
+ assertEquals(responseBody, resultAsString);
+
+ wireMockUtils.verifyFiltersExport(stubUuid, FILTERS_UUID_STRING, NETWORK_UUID_STRING);
}
}
diff --git a/src/test/java/org/gridsuite/study/server/utils/WireMockUtils.java b/src/test/java/org/gridsuite/study/server/utils/WireMockUtils.java
index 81cc3aa773..1d162b8dce 100644
--- a/src/test/java/org/gridsuite/study/server/utils/WireMockUtils.java
+++ b/src/test/java/org/gridsuite/study/server/utils/WireMockUtils.java
@@ -15,6 +15,7 @@
import com.github.tomakehurst.wiremock.matching.StringValuePattern;
import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
import com.powsybl.iidm.network.TwoSides;
+import org.gridsuite.filter.utils.EquipmentType;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -524,6 +525,15 @@ public UUID stubFilterEvaluate(String networkUuid, String responseBody) {
).getId();
}
+ public UUID stubGlobalFilterEvaluate(String networkUuid, List equipmentTypes, String responseBody) {
+ return wireMock.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/global-filter"))
+ .withQueryParam(NETWORK_UUID, WireMock.equalTo(networkUuid))
+ .withQueryParam(QUERY_PARAM_VARIANT_ID, WireMock.equalTo(""))
+ .withQueryParam(QUERY_PARAM_EQUIPMENT_TYPES, WireMock.equalTo(String.join(",", equipmentTypes.stream().map(EquipmentType::name).toList())))
+ .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(responseBody))
+ ).getId();
+ }
+
public UUID stubFilterEvaluateNotFoundError(String networkUuid) {
return wireMock.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/filters/evaluate"))
.withQueryParam(NETWORK_UUID, WireMock.equalTo(networkUuid))
@@ -543,6 +553,13 @@ public void verifyFilterEvaluate(UUID stubUuid, String networkUuid) {
Map.of(NETWORK_UUID, WireMock.equalTo(networkUuid)));
}
+ public void verifyGlobalFilterEvaluate(UUID stubUuid, String networkUuid, List equipmentTypes) {
+ verifyPostRequest(stubUuid, "/v1/global-filter",
+ Map.of(NETWORK_UUID, WireMock.equalTo(networkUuid),
+ QUERY_PARAM_VARIANT_ID, WireMock.equalTo(""),
+ QUERY_PARAM_EQUIPMENT_TYPES, WireMock.equalTo(String.join(",", equipmentTypes.stream().map(EquipmentType::name).toList()))));
+ }
+
public UUID stubFilterExport(String networkUuid, String filterUuid, String responseBody) {
return wireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/filters/" + filterUuid + "/export"))
.withQueryParam(NETWORK_UUID, WireMock.equalTo(networkUuid))