diff --git a/pom.xml b/pom.xml index b0adb2f4..4985bb7f 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,8 @@ org.gridsuite gridsuite-filter + + 1.9.0-SNAPSHOT com.fasterxml.jackson.core diff --git a/src/main/java/org/gridsuite/filter/server/FilterController.java b/src/main/java/org/gridsuite/filter/server/FilterController.java index c6ef88e5..53318c21 100644 --- a/src/main/java/org/gridsuite/filter/server/FilterController.java +++ b/src/main/java/org/gridsuite/filter/server/FilterController.java @@ -14,7 +14,9 @@ import org.gridsuite.filter.AbstractFilter; import org.gridsuite.filter.IFilterAttributes; import org.gridsuite.filter.identifierlistfilter.FilterEquipments; +import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables; import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes; +import org.gridsuite.filter.server.dto.FilterAttributes; import org.gridsuite.filter.server.dto.IdsByGroup; import org.springframework.context.annotation.ComponentScan; import org.springframework.http.MediaType; @@ -52,6 +54,13 @@ public ResponseEntity> getFilters() { return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(service.getFilters()); } + @GetMapping(value = "/filters/infos", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get filters infos") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "All filters")}) + public ResponseEntity> getFilters(@RequestParam List filterUuids, @RequestHeader String userId) { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(service.getFiltersAttributes(filterUuids, userId)); + } + @GetMapping(value = "/filters/{id}", produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Get filter by id") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The filter"), @@ -192,6 +201,18 @@ public ResponseEntity> exportFilters(@RequestParam("ids") .body(ret); } + @GetMapping(value = "/filters/evaluate/identifiables", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Export matched identifiables elements to JSON format") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "The list of matched elements") + }) + public ResponseEntity evaluateFilters(@RequestParam("ids") List ids, + @RequestParam(value = "networkUuid") UUID networkUuid, + @RequestParam(value = "variantUuid", required = false) String variantUuid) { + FilteredIdentifiables identifiableAttributes = service.evaluateFilters(ids, networkUuid, variantUuid); + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(identifiableAttributes); + } + @PostMapping(value = "/filters/evaluate", produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Export matched elements to JSON format") @ApiResponses(value = { diff --git a/src/main/java/org/gridsuite/filter/server/FilterService.java b/src/main/java/org/gridsuite/filter/server/FilterService.java index 4f0914a3..da396e4b 100644 --- a/src/main/java/org/gridsuite/filter/server/FilterService.java +++ b/src/main/java/org/gridsuite/filter/server/FilterService.java @@ -16,6 +16,8 @@ import org.gridsuite.filter.IFilterAttributes; import org.gridsuite.filter.identifierlistfilter.FilterEquipments; import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes; +import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables; +import org.gridsuite.filter.server.dto.FilterAttributes; import org.gridsuite.filter.server.dto.IdsByGroup; import org.gridsuite.filter.server.entities.AbstractFilterEntity; import org.gridsuite.filter.server.repositories.FilterRepository; @@ -24,6 +26,7 @@ import org.gridsuite.filter.server.repositories.proxies.AbstractFilterRepositoryProxy; import org.gridsuite.filter.server.repositories.proxies.expertfiler.ExpertFilterRepositoryProxy; import org.gridsuite.filter.server.repositories.proxies.identifierlistfilter.IdentifierListFilterRepositoryProxy; +import org.gridsuite.filter.server.service.DirectoryService; import org.gridsuite.filter.utils.FilterServiceUtils; import org.gridsuite.filter.utils.FilterType; import org.gridsuite.filter.utils.expertfilter.FilterCycleDetector; @@ -54,15 +57,19 @@ public class FilterService { private final NotificationService notificationService; + private final DirectoryService directoryService; + public FilterService(final IdentifierListFilterRepository identifierListFilterRepository, final ExpertFilterRepository expertFilterRepository, NetworkStoreService networkStoreService, - NotificationService notificationService) { + NotificationService notificationService, + DirectoryService directoryService) { filterRepositories.put(FilterType.IDENTIFIER_LIST.name(), new IdentifierListFilterRepositoryProxy(identifierListFilterRepository)); filterRepositories.put(FilterType.EXPERT.name(), new ExpertFilterRepositoryProxy(expertFilterRepository)); this.networkStoreService = networkStoreService; this.notificationService = notificationService; + this.directoryService = directoryService; } public List getFilters() { @@ -71,6 +78,16 @@ public List getFilters() { .collect(Collectors.toList()); } + public List getFiltersAttributes(List filterUuids, String userId) { + List filterAttributes = filterRepositories.entrySet().stream() + .flatMap(entry -> entry.getValue().getFiltersAttributes(filterUuids)) + .toList(); + // call directory server to add name information + Map elementsName = directoryService.getElementsName(filterAttributes.stream().map(FilterAttributes::getId).toList(), userId); + filterAttributes.forEach(attribute -> attribute.setName(elementsName.get(attribute.getId()))); + return filterAttributes; + } + @Transactional(readOnly = true) public Optional getFilter(UUID id) { return getFilterFromRepository(id); @@ -249,6 +266,34 @@ public List evaluateFilter(AbstractFilter filter, UUID n return getIdentifiableAttributes(filter, networkUuid, variantId, filterLoader); } + @Transactional(readOnly = true) + public FilteredIdentifiables evaluateFilters(List filters, UUID networkUuid, String variantId) { + FilteredIdentifiables filteredIdentifiables = new FilteredIdentifiables(); + Map result = new TreeMap<>(); + Map notFound = new TreeMap<>(); + Network network = getNetwork(networkUuid, variantId); + filters.forEach((UUID filterUuid) -> { + Optional optFilter = getFilterFromRepository(filterUuid); + if (optFilter.isEmpty()) { + return; + } + AbstractFilter filter = optFilter.get(); + Objects.requireNonNull(filter); + FilterLoader filterLoader = new FilterLoaderImpl(filterRepositories); + FilteredIdentifiables filterIdentiables = filter.toFilteredIdentifiables(FilterServiceUtils.getIdentifiableAttributes(filter, network, filterLoader)); + + // unduplicate equipments and merge in common lists + if (filterIdentiables.getNotFoundIds() != null) { + filterIdentiables.getNotFoundIds().forEach(element -> notFound.put(element.getId(), element)); + } + filterIdentiables.getEquipmentIds().forEach(element -> result.put(element.getId(), element)); + } + ); + filteredIdentifiables.setEquipmentIds(result.values().stream().toList()); + filteredIdentifiables.setNotFoundIds(notFound.values().stream().toList()); + return filteredIdentifiables; + } + @Transactional(readOnly = true) public Optional> exportFilter(UUID id, UUID networkUuid, String variantId) { Objects.requireNonNull(id); diff --git a/src/main/java/org/gridsuite/filter/server/configs/RestTemplateConfig.java b/src/main/java/org/gridsuite/filter/server/configs/RestTemplateConfig.java new file mode 100644 index 00000000..4b3c6fe8 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/configs/RestTemplateConfig.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025, 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.filter.server.configs; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} + diff --git a/src/main/java/org/gridsuite/filter/server/dto/FilterAttributes.java b/src/main/java/org/gridsuite/filter/server/dto/FilterAttributes.java index 5c93a764..a7fb46b2 100644 --- a/src/main/java/org/gridsuite/filter/server/dto/FilterAttributes.java +++ b/src/main/java/org/gridsuite/filter/server/dto/FilterAttributes.java @@ -30,6 +30,7 @@ public class FilterAttributes implements IFilterAttributes { Date modificationDate; FilterType type; EquipmentType equipmentType; + String name; public FilterAttributes(FilterMetadata filterMetadata, FilterType type, EquipmentType equipmentType) { id = filterMetadata.getId(); diff --git a/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java b/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java index caf7676d..36fafd74 100644 --- a/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java +++ b/src/main/java/org/gridsuite/filter/server/repositories/proxies/AbstractFilterRepositoryProxy.java @@ -72,6 +72,10 @@ public Stream getFiltersAttributes() { return getRepository().getFiltersMetadata().stream().map(this::metadataToAttribute); } + public Stream getFiltersAttributes(List ids) { + return getRepository().findFiltersMetaDataById(ids).stream().map(this::metadataToAttribute); + } + FilterAttributes metadataToAttribute(FilterMetadata f) { return new FilterAttributes(f, getFilterType(), getEquipmentType(f.getId())); } diff --git a/src/main/java/org/gridsuite/filter/server/service/DirectoryService.java b/src/main/java/org/gridsuite/filter/server/service/DirectoryService.java new file mode 100644 index 00000000..c65b75bc --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/service/DirectoryService.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, 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.filter.server.service; + +import lombok.Getter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import org.gridsuite.filter.utils.ElementAttributes; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Service +public class DirectoryService { + public static final String DELIMITER = "/"; + public static final String DIRECTORY_API_VERSION = "v1"; + public static final String ELEMENT_END_POINT_INFOS = "/elements"; + + @Getter + private final String baseUri; + private final RestTemplate restTemplate; + + @Autowired + public DirectoryService(@Value("${gridsuite.services.directory-server.base-uri:http://directory-server/}") String baseUri, + RestTemplate restTemplate) { + this.baseUri = baseUri; + this.restTemplate = restTemplate; + } + + public Map getElementsName(List ids, String userId) { + Map result = new HashMap<>(); + String endPointUrl = getBaseUri() + DELIMITER + DIRECTORY_API_VERSION + ELEMENT_END_POINT_INFOS; + UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(endPointUrl); + uriComponentsBuilder.queryParam("ids", ids); + var uriComponent = uriComponentsBuilder.buildAndExpand(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("userId", userId); + + HttpEntity entity = new HttpEntity<>(headers); + + List elementAttributes = restTemplate.exchange(uriComponent.toUriString(), + HttpMethod.GET, entity, new ParameterizedTypeReference>() { }).getBody(); + if (elementAttributes != null) { + for (ElementAttributes elementAttribute : elementAttributes) { + result.put(elementAttribute.getElementUuid(), elementAttribute.getElementName()); + } + } + + return result; + } +} diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 8d5a72ef..1ddc43bc 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -13,3 +13,8 @@ powsybl: services: network-store-server: base-uri: http://localhost:8080 + +gridsuite: + services: + directory-server: + base-uri: http://localhost:5026 diff --git a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java index 983082e7..922cca4d 100644 --- a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java +++ b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java @@ -25,6 +25,7 @@ import org.gridsuite.filter.expertfilter.ExpertFilter; import org.gridsuite.filter.expertfilter.expertrule.*; import org.gridsuite.filter.identifierlistfilter.FilterEquipments; +import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables; import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes; import org.gridsuite.filter.identifierlistfilter.IdentifierListFilter; import org.gridsuite.filter.identifierlistfilter.IdentifierListFilterEquipmentAttributes; @@ -421,6 +422,34 @@ private void checkIdentifiableAttributes(List identifiab } } + @Test + public void testEvaluateFilters() throws Exception { + UUID filterId = UUID.randomUUID(); + ArrayList rules = new ArrayList<>(); + EnumExpertRule country1Filter = EnumExpertRule.builder().field(FieldType.COUNTRY_1).operator(OperatorType.IN) + .values(new TreeSet<>(Set.of("FR"))).build(); + rules.add(country1Filter); + CombinatorExpertRule parentRule = CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(rules).build(); + ExpertFilter lineFilter = new ExpertFilter(filterId, new Date(), EquipmentType.LINE, parentRule); + insertFilter(filterId, lineFilter); + + // Apply filter by calling endPoint + List filterIds = List.of(filterId.toString()); + MultiValueMap params = new LinkedMultiValueMap<>(); + params.addAll("ids", filterIds); + params.add("networkUuid", NETWORK_UUID.toString()); + FilteredIdentifiables result = objectMapper.readValue(mvc.perform(get(URL_TEMPLATE + "/evaluate/identifiables") + .params(params).contentType(APPLICATION_JSON)).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), new TypeReference<>() { }); + + List expected = new ArrayList<>(); + expected.add(new IdentifiableAttributes("NHV1_NHV2_1", IdentifiableType.LINE, null)); + expected.add(new IdentifiableAttributes("NHV1_NHV2_2", IdentifiableType.LINE, null)); + assertTrue(expected.size() == result.getEquipmentIds().size() + && result.getEquipmentIds().containsAll(expected) + && expected.containsAll(result.getEquipmentIds())); + } + @Test public void testExportFilters() throws Exception { UUID filterId = UUID.randomUUID();