diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index d5020780..5ee6e33a 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -36,6 +36,7 @@ import static org.gridsuite.computation.service.NotificationService.HEADER_USER_ID; import static org.gridsuite.computation.utils.FilterUtils.fromStringFiltersToDTO; +import static org.gridsuite.computation.utils.FilterUtils.fromStringGlobalFiltersToDTO; import static org.springframework.http.MediaType.*; /** @@ -122,12 +123,19 @@ public ResponseEntity runAndSave(@Parameter(description = "Network UUID") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity> getNResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, + @Parameter(description = "network Uuid") @RequestParam(name = "networkUuid", required = false) UUID networkUuid, + @Parameter(description = "variant Id") @RequestParam(name = "variantId", required = false) String variantId, @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, @Parameter(description = "Pageable parameters for pagination and sorting") Sort sort) { String decodedStringFilters = stringFilters != null ? URLDecoder.decode(stringFilters, StandardCharsets.UTF_8) : null; + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; List result = securityAnalysisResultService.findNResult( resultUuid, + networkUuid, + variantId, fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), + fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), sort); return result != null @@ -151,10 +159,14 @@ public ResponseEntity getNResultZippedCsv(@Parameter(description = "Resu @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity> getNmKContingenciesResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, - @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, - @Parameter(description = "Pagination parameters") Pageable pageable) { + @Parameter(description = "network Uuid") @RequestParam(name = "networkUuid", required = false) UUID networkUuid, + @Parameter(description = "variant Id") @RequestParam(name = "variantId", required = false) String variantId, + @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, + @Parameter(description = "Pagination parameters") Pageable pageable) { String decodedStringFilters = stringFilters != null ? URLDecoder.decode(stringFilters, StandardCharsets.UTF_8) : null; - Page result = securityAnalysisResultService.findNmKContingenciesPaged(resultUuid, decodedStringFilters, pageable); + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; + Page result = securityAnalysisResultService.findNmKContingenciesPaged(resultUuid, networkUuid, variantId, decodedStringFilters, decodedStringGlobalFilters, pageable); return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result) @@ -177,10 +189,14 @@ public ResponseEntity getNmKContingenciesResultZippedCsv(@Parameter(desc @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity> getNmKConstraintsResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, + @Parameter(description = "network Uuid") @RequestParam(name = "networkUuid", required = false) UUID networkUuid, + @Parameter(description = "variant Id") @RequestParam(name = "variantId", required = false) String variantId, @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, @Parameter(description = "Pagination parameters") Pageable pageable) { String decodedStringFilters = stringFilters != null ? URLDecoder.decode(stringFilters, StandardCharsets.UTF_8) : null; - Page result = securityAnalysisResultService.findNmKConstraintsResultPaged(resultUuid, decodedStringFilters, pageable); + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; + Page result = securityAnalysisResultService.findNmKConstraintsResultPaged(resultUuid, networkUuid, variantId, decodedStringFilters, decodedStringGlobalFilters, pageable); return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result) : ResponseEntity.notFound().build(); diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/FilterService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/FilterService.java new file mode 100644 index 00000000..7379187a --- /dev/null +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/FilterService.java @@ -0,0 +1,47 @@ +/** + * 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.securityanalysis.server.service; + +import com.powsybl.network.store.client.NetworkStoreService; +import lombok.NonNull; +import org.gridsuite.computation.dto.GlobalFilter; +import org.gridsuite.computation.dto.ResourceFilterDTO; +import org.gridsuite.computation.service.AbstractFilterService; +import org.gridsuite.filter.utils.EquipmentType; +import org.gridsuite.securityanalysis.server.entities.ContingencyEntity; +import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * @author Rehili Ghazwa + */ +@Service +public class FilterService extends AbstractFilterService { + + public FilterService( + NetworkStoreService networkStoreService, + @Value("${gridsuite.services.filter-server.base-uri:http://filter-server/}") String filterServerBaseUri) { + super(networkStoreService, filterServerBaseUri); + } + + public Optional getResourceFilterN(@NonNull UUID networkUuid, @NonNull String variantId, @NonNull GlobalFilter globalFilter) { + return super.getResourceFilter(networkUuid, variantId, globalFilter, List.of(EquipmentType.VOLTAGE_LEVEL), "subjectLimitViolation.subjectId"); + } + + public Optional getResourceFilterContingencies(@NonNull UUID networkUuid, @NonNull String variantId, @NonNull GlobalFilter globalFilter) { + return super.getResourceFilter(networkUuid, variantId, globalFilter, List.of(EquipmentType.LINE, EquipmentType.TWO_WINDINGS_TRANSFORMER, EquipmentType.VOLTAGE_LEVEL), ContingencyEntity.Fields.contingencyId); + } + + public Optional getResourceFilterSubjectLimitViolations(@NonNull UUID networkUuid, @NonNull String variantId, @NonNull GlobalFilter globalFilter) { + return super.getResourceFilter(networkUuid, variantId, globalFilter, List.of(EquipmentType.LINE, EquipmentType.TWO_WINDINGS_TRANSFORMER, EquipmentType.VOLTAGE_LEVEL), SubjectLimitViolationEntity.Fields.subjectId); + } +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java index 5b8bfc94..a9c4e4b4 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -11,11 +11,12 @@ import com.powsybl.iidm.network.ThreeSides; import com.powsybl.security.LimitViolationType; import com.powsybl.security.SecurityAnalysisResult; +import lombok.Getter; import org.gridsuite.computation.ComputationException; +import org.gridsuite.computation.dto.GlobalFilter; import org.gridsuite.computation.dto.ResourceFilterDTO; import org.gridsuite.computation.service.AbstractComputationResultService; import org.gridsuite.computation.utils.SpecificationUtils; -import lombok.Getter; import org.gridsuite.securityanalysis.server.dto.*; import org.gridsuite.securityanalysis.server.entities.*; import org.gridsuite.securityanalysis.server.repositories.*; @@ -36,6 +37,7 @@ import java.util.concurrent.atomic.AtomicReference; import static org.gridsuite.computation.utils.FilterUtils.fromStringFiltersToDTO; +import static org.gridsuite.computation.utils.FilterUtils.fromStringGlobalFiltersToDTO; /** * @author Geoffroy Jamgotchian @@ -53,6 +55,7 @@ public class SecurityAnalysisResultService extends AbstractComputationResultServ private final PreContingencyLimitViolationSpecificationBuilder preContingencyLimitViolationSpecificationBuilder; @Getter private final ObjectMapper objectMapper; + private final FilterService filterService; private final SecurityAnalysisResultService self; private static final Sort.Direction DEFAULT_SORT_DIRECTION = Sort.Direction.ASC; @@ -106,6 +109,7 @@ public SecurityAnalysisResultService(SecurityAnalysisResultRepository securityAn ContingencySpecificationBuilder contingencySpecificationBuilder, SubjectLimitViolationSpecificationBuilder subjectLimitViolationSpecificationBuilder, @Lazy SecurityAnalysisResultService self, + FilterService filterService, ObjectMapper objectMapper) { this.securityAnalysisResultRepository = securityAnalysisResultRepository; this.contingencyRepository = contingencyRepository; @@ -115,15 +119,24 @@ public SecurityAnalysisResultService(SecurityAnalysisResultRepository securityAn this.preContingencyLimitViolationSpecificationBuilder = preContingencyLimitViolationSpecificationBuilder; this.contingencySpecificationBuilder = contingencySpecificationBuilder; this.subjectLimitViolationSpecificationBuilder = subjectLimitViolationSpecificationBuilder; + this.filterService = filterService; this.objectMapper = objectMapper; this.self = self; } @Transactional(readOnly = true) - public List findNResult(UUID resultUuid, List resourceFilters, Sort sort) { + public List findNResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { assertResultExists(resultUuid); assertPreContingenciesSortAllowed(sort); - Specification specification = preContingencyLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); + List allResourceFilters = new ArrayList<>(); + if (resourceFilters != null) { + allResourceFilters.addAll(resourceFilters); + } + if (globalFilter != null) { + Optional resourceGlobalFilters = filterService.getResourceFilterN(networkUuid, variantId, globalFilter); + resourceGlobalFilters.ifPresent(allResourceFilters::add); + } + Specification specification = preContingencyLimitViolationSpecificationBuilder.buildSpecification(resultUuid, allResourceFilters); List preContingencyLimitViolation = preContingencyLimitViolationRepository.findAll(specification, sort); return preContingencyLimitViolation.stream() @@ -133,16 +146,16 @@ public List findNResult(UUID resultUuid, @Transactional(readOnly = true) public byte[] findNResultZippedCsv(UUID resultUuid, CsvTranslationDTO csvTranslations) { - List result = self.findNResult(resultUuid, List.of(), Sort.by(Sort.Direction.ASC, AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId)); + List result = self.findNResult(resultUuid, null, null, List.of(), null, Sort.by(Sort.Direction.ASC, AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId)); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), result.stream().map(r -> r.toCsvRow(csvTranslations.enumValueTranslations())).toList()); } @Transactional(readOnly = true) - public Page findNmKContingenciesPaged(UUID resultUuid, String stringFilters, Pageable pageable) { + public Page findNmKContingenciesPaged(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Pageable pageable) { assertResultExists(resultUuid); - Page contingencyPageBis = self.findContingenciesPage(resultUuid, fromStringFiltersToDTO(stringFilters, objectMapper), pageable); + Page contingencyPageBis = self.findContingenciesPage(resultUuid, networkUuid, variantId, fromStringFiltersToDTO(stringFilters, objectMapper), fromStringGlobalFiltersToDTO(stringGlobalFilters, objectMapper), pageable); return contingencyPageBis.map(ContingencyResultDTO::toDto); } @@ -168,10 +181,10 @@ public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, CsvTranslatio } @Transactional(readOnly = true) - public Page findNmKConstraintsResultPaged(UUID resultUuid, String stringFilters, Pageable pageable) { + public Page findNmKConstraintsResultPaged(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Pageable pageable) { assertResultExists(resultUuid); - Page subjectLimitViolationsPage = self.findSubjectLimitViolationsPage(resultUuid, fromStringFiltersToDTO(stringFilters, objectMapper), pageable); + Page subjectLimitViolationsPage = self.findSubjectLimitViolationsPage(resultUuid, networkUuid, variantId, fromStringFiltersToDTO(stringFilters, objectMapper), fromStringGlobalFiltersToDTO(stringGlobalFilters, objectMapper), pageable); return subjectLimitViolationsPage.map(SubjectLimitViolationResultDTO::toDto); } @@ -289,12 +302,19 @@ private static Page emptyPage(Pageable pageable) { } @Transactional(readOnly = true) - public Page findContingenciesPage(UUID resultUuid, List resourceFilters, Pageable pageable) { + public Page findContingenciesPage(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKContingenciesSortAllowed(pageable.getSort()); Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, ContingencyEntity.Fields.uuid); - - Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, resourceFilters); + List allResourceFilters = new ArrayList<>(); + if (resourceFilters != null) { + allResourceFilters.addAll(resourceFilters); + } + if (globalFilter != null) { + Optional resourceGlobalFilters = filterService.getResourceFilterContingencies(networkUuid, variantId, globalFilter); + resourceGlobalFilters.ifPresent(allResourceFilters::add); + } + Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, allResourceFilters); // WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl - // HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! // cf. https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/ @@ -319,18 +339,26 @@ public Page findContingenciesPage(UUID resultUuid, List contingenciesPage = new PageImpl<>(contingencies, pageable, uuidPage.getTotalElements()); // then we append the missing data, and filter some of the Lazy Loaded collections - appendLimitViolationsAndElementsToContingenciesResult(contingenciesPage, resourceFilters); + appendLimitViolationsAndElementsToContingenciesResult(contingenciesPage, allResourceFilters); return contingenciesPage; } } @Transactional(readOnly = true) - public Page findSubjectLimitViolationsPage(UUID resultUuid, List resourceFilters, Pageable pageable) { + public Page findSubjectLimitViolationsPage(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKSubjectLimitViolationsSortAllowed(pageable.getSort()); Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, SubjectLimitViolationEntity.Fields.id); - Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); + List allResourceFilters = new ArrayList<>(); + if (resourceFilters != null) { + allResourceFilters.addAll(resourceFilters); + } + if (globalFilter != null) { + Optional resourceGlobalFilters = filterService.getResourceFilterSubjectLimitViolations(networkUuid, variantId, globalFilter); + resourceGlobalFilters.ifPresent(allResourceFilters::add); + } + Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, allResourceFilters); // WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl - // HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! // cf. https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/ @@ -353,7 +381,7 @@ public Page findSubjectLimitViolationsPage(UUID res Page subjectLimitViolationPage = new PageImpl<>(subjectLimitViolations, pageable, uuidPage.getTotalElements()); // then we append the missing data, and filter some of the Lazy Loaded collections - appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(subjectLimitViolationPage, resourceFilters); + appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(subjectLimitViolationPage, allResourceFilters); return subjectLimitViolationPage; } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java b/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java index a99a006b..24dea87b 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java @@ -87,7 +87,7 @@ void tearDown() { }) void findFilteredContingencyResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) { reset(); - Page contingenciesPage = securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), filters, pageable); + Page contingenciesPage = securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), null, null, filters, null, pageable); // assert contingency ids to check parent filters assertThat(contingenciesPage.getContent()).extracting(ContingencyEntity.Fields.contingencyId).containsExactlyElementsOf(expectedResult.stream().map(c -> c.getContingency().getContingencyId()).toList()); @@ -110,7 +110,7 @@ void findFilteredContingencyResultsTest(List filters, Pageabl "provideForbiddenFilter" }) void testSortAndFilterErrors(List filters, Pageable pageable, Exception expectedException) { - Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), filters, pageable)); + Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), null, null, filters, null, pageable)); assertEquals(expectedException.getMessage(), exception.getMessage()); } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java b/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java index 5943feea..9d6b1ea6 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java @@ -72,7 +72,7 @@ void tearDown() { }) void findFilteredPrecontingencyLimitViolationResultsTest(List filters, Sort sort, List expectedResult, Integer expectedSelectCount) { reset(); - List preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), filters, sort); + List preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), null, null, filters, null, sort); // assert subject ids to check parent filters assertThat(preContingencyLimitViolation).extracting(SubjectLimitViolationEntity.Fields.subjectId) diff --git a/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java b/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java index 6c9e0797..7acb7db0 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java @@ -82,7 +82,7 @@ void tearDown() { }) void findFilteredSubjectLimitViolationResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) { reset(); - Page subjectLimitViolationPage = securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), filters, pageable); + Page subjectLimitViolationPage = securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), null, null, filters, null, pageable); // assert subject ids to check parent filters assertThat(subjectLimitViolationPage.getContent()).extracting("subjectId").containsExactlyElementsOf(expectedResult.stream().map(SubjectLimitViolationResultDTO::getSubjectId).toList()); @@ -108,7 +108,7 @@ void findFilteredSubjectLimitViolationResultsTest(List filter "provideForbiddenFilter" }) void testSortAndFilterErrors(List filters, Pageable pageable, Exception expectedException) { - Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), filters, pageable)); + Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), null, null, filters, null, pageable)); assertEquals(expectedException.getMessage(), exception.getMessage()); } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java index 62748e2f..29b5d2b4 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java @@ -14,9 +14,7 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.powsybl.commons.report.ReportNode; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.ThreeSides; -import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.loadflow.LoadFlowResult; @@ -24,12 +22,14 @@ import com.powsybl.network.store.client.PreloadingStrategy; import com.powsybl.network.store.iidm.impl.NetworkFactoryImpl; import com.powsybl.security.*; +import com.vladmihalcea.sql.SQLStatementCountValidator; +import org.assertj.core.api.Assertions; +import org.gridsuite.computation.dto.GlobalFilter; import org.gridsuite.computation.dto.ResourceFilterDTO; +import org.gridsuite.computation.service.AbstractFilterService; import org.gridsuite.computation.service.ReportService; import org.gridsuite.computation.service.UuidGeneratorService; import org.gridsuite.computation.utils.SpecificationUtils; -import com.vladmihalcea.sql.SQLStatementCountValidator; -import org.assertj.core.api.Assertions; import org.gridsuite.securityanalysis.server.dto.*; import org.gridsuite.securityanalysis.server.entities.AbstractLimitViolationEntity; import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; @@ -52,6 +52,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.messaging.Message; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.StreamUtils; @@ -75,12 +76,10 @@ import static org.gridsuite.securityanalysis.server.util.DatabaseQueryUtils.assertRequestsCount; import static org.gridsuite.securityanalysis.server.util.TestUtils.assertLogMessage; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -100,6 +99,7 @@ class SecurityAnalysisControllerTest { private static final UUID RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5d"); private static final UUID REPORT_UUID = UUID.fromString("0c4de370-3e6a-4d72-b292-d355a97e0d53"); private static final UUID OTHER_RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5a"); + private static final UUID LIST_FILTER_ID = UUID.fromString("762b72a8-8c0f-11ed-a1eb-0242ac120003"); private static final int TIMEOUT = 1000; @@ -126,6 +126,9 @@ class SecurityAnalysisControllerTest { @MockBean private UuidGeneratorService uuidGeneratorService; + @Autowired + private AbstractFilterService filterService; + @Autowired private SecurityAnalysisWorkerService workerService; @@ -161,7 +164,7 @@ void setUp() throws Exception { wireMockServer.start(); MockitoAnnotations.initMocks(this); - + ReflectionTestUtils.setField(filterService, "filterServerBaseUri", wireMockServer.baseUrl()); // network store service mocking Network network = EurostagTutorialExample1Factory.create(new NetworkFactoryImpl()); network.getVariantManager().cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, VARIANT_1_ID); @@ -198,6 +201,10 @@ void setUp() throws Exception { wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/parameters/.*/values\\?provider=.*")) .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(mapper.writeValueAsString(loadFlowParametersValues)))); + wireMockServer.stubFor(WireMock.get(WireMock.urlMatching("/v1/filters/metadata\\?ids=" + LIST_FILTER_ID)) + .willReturn(WireMock.ok() + .withBody(mapper.writeValueAsString(List.of())) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))).getId(); // purge messages while (output.receive(1000, "sa.result") != null) { } @@ -329,12 +336,12 @@ void runAndSaveTest() throws Exception { .andExpectAll( status().isOk(), content().contentType(MediaType.APPLICATION_JSON)); - + assertFiltredResultNmkContingencies(); mockMvc.perform(get("/" + VERSION + "/results/" + RESULT_UUID + "/nmk-constraints-result/paged")) .andExpectAll( status().isOk(), content().contentType(MediaType.APPLICATION_JSON)); - + assertFiltredResultNmkConstraints(); checkNmKResultEnumFilters(RESULT_UUID); // should throw not found if result does not exist @@ -383,7 +390,7 @@ void testDeterministicResults() throws Exception { status().isOk(), content().contentType(MediaType.APPLICATION_JSON)) .andReturn().getResponse().getContentAsString(); - + assertFiltredResultNmkConstraints(); JsonNode resultsPageNode0 = mapper.readTree(res); ObjectReader faultResultsReader = mapper.readerFor(new TypeReference>() { }); List subjectLimitViolationResultDTOS = faultResultsReader.readValue(resultsPageNode0.get("content")); @@ -400,7 +407,7 @@ void testDeterministicResults() throws Exception { status().isOk(), content().contentType(MediaType.APPLICATION_JSON)) .andReturn().getResponse().getContentAsString(); - + assertFiltredResultNmkContingencies(); resultsPageNode0 = mapper.readTree(res); faultResultsReader = mapper.readerFor(new TypeReference>() { }); subjectLimitViolationResultDTOS = faultResultsReader.readValue(resultsPageNode0.get("content")); @@ -409,28 +416,82 @@ void testDeterministicResults() throws Exception { assertEquals(expectedResultInOrder, result); } - private static String buildFilterUrl() throws JsonProcessingException { + private static String buildFilterNUrl() throws JsonProcessingException { List filters = List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.STARTS_WITH, "vl1", AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId), new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.EQUALS, new String[]{"HIGH_VOLTAGE"}, AbstractLimitViolationEntity.Fields.limitType), new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.GREATER_THAN_OR_EQUAL, "399", AbstractLimitViolationEntity.Fields.limit), new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.LESS_THAN_OR_EQUAL, "420", AbstractLimitViolationEntity.Fields.value), new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.NOT_EQUAL, "2", AbstractLimitViolationEntity.Fields.acceptableDuration) ); + GlobalFilter globalFilter = GlobalFilter.builder() + .genericFilter(List.of(LIST_FILTER_ID)) + .nominalV(List.of("400.0")) + .countryCode(List.of(Country.FR)) + .build(); String jsonFilters = new ObjectMapper().writeValueAsString(filters); - return "filters=" + URLEncoder.encode(jsonFilters, StandardCharsets.UTF_8); + String jsonGlobalFilters = new ObjectMapper().writeValueAsString(globalFilter); + return "filters=" + URLEncoder.encode(jsonFilters, StandardCharsets.UTF_8) + + "&globalFilters=" + URLEncoder.encode(jsonGlobalFilters, StandardCharsets.UTF_8) + "&networkUuid=" + NETWORK_UUID + "&variantId=" + "initialState"; + } + + private static String buildFilterNmkUrl() throws JsonProcessingException { + List filters = List.of(); + GlobalFilter globalFilter = GlobalFilter.builder() + .genericFilter(List.of(LIST_FILTER_ID)) + .nominalV(List.of("400.0")) + .countryCode(List.of(Country.FR)) + .build(); + String jsonFilters = new ObjectMapper().writeValueAsString(filters); + String jsonGlobalFilters = new ObjectMapper().writeValueAsString(globalFilter); + return "filters=" + URLEncoder.encode(jsonFilters, StandardCharsets.UTF_8) + + "&globalFilters=" + URLEncoder.encode(jsonGlobalFilters, StandardCharsets.UTF_8) + "&networkUuid=" + NETWORK_UUID + "&variantId=" + "initialState"; } private void assertFiltredResultN() throws Exception { - MvcResult mvcResult = mockMvc.perform(get("/" + VERSION + "/results/" + RESULT_UUID + "/n-result?" + buildFilterUrl())) - .andExpectAll( - status().isOk(), - content().contentType(MediaType.APPLICATION_JSON) - ).andReturn(); + Network network = mock(Network.class); + VariantManager variantManager = mock(VariantManager.class); + when(network.getVariantManager()).thenReturn(variantManager); + doNothing().when(variantManager).setWorkingVariant(anyString()); + + when(networkStoreService.getNetwork(NETWORK_UUID, PreloadingStrategy.COLLECTION)).thenReturn(network); + + // test - n-result endpoint + MvcResult mvcResult = mockMvc.perform(get("/" + VERSION + "/results/" + RESULT_UUID + "/n-result?" + buildFilterNUrl())) + .andExpectAll(status().isOk()).andReturn(); String resultAsString = mvcResult.getResponse().getContentAsString(); List preContingencyResult = mapper.readValue(resultAsString, new TypeReference<>() { }); assertEquals(1, preContingencyResult.size()); assertEquals("vl1 (VLGEN_0, VLLOAD_0)", preContingencyResult.get(0).getLimitViolation().getLocationId()); + } + + private void assertFiltredResultNmkConstraints() throws Exception { + Network network = mock(Network.class); + VariantManager variantManager = mock(VariantManager.class); + when(network.getVariantManager()).thenReturn(variantManager); + doNothing().when(variantManager).setWorkingVariant(anyString()); + when(networkStoreService.getNetwork(NETWORK_UUID, PreloadingStrategy.COLLECTION)).thenReturn(network); + + // test - nmk-constraints-result/paged endpoint + MvcResult mvcResult = mockMvc.perform(get("/" + VERSION + "/results/" + RESULT_UUID + "/nmk-constraints-result/paged?" + buildFilterNmkUrl())) + .andExpectAll(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + assertNotNull(resultAsString); + } + + private void assertFiltredResultNmkContingencies() throws Exception { + Network network = mock(Network.class); + VariantManager variantManager = mock(VariantManager.class); + when(network.getVariantManager()).thenReturn(variantManager); + doNothing().when(variantManager).setWorkingVariant(anyString()); + + when(networkStoreService.getNetwork(NETWORK_UUID, PreloadingStrategy.COLLECTION)).thenReturn(network); + + // test - nmk-contingencies-result/paged endpoint + MvcResult mvcResult = mockMvc.perform(get("/" + VERSION + "/results/" + RESULT_UUID + "/nmk-contingencies-result/paged?" + buildFilterNmkUrl())) + .andExpectAll(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + assertNotNull(resultAsString); } private void checkNResultEnumFilters(UUID resultUuid) throws Exception {