From 1cae47e9a06f1d9f8929940f9ac4ccd2dd3d59e3 Mon Sep 17 00:00:00 2001 From: Caroline Jeandat Date: Thu, 8 Jan 2026 16:20:35 +0100 Subject: [PATCH 1/5] add sort and filters to csv export --- .../server/SecurityAnalysisController.java | 59 ++++++++++++++++--- .../repositories/ContingencyRepository.java | 4 ++ .../SubjectLimitViolationRepository.java | 4 ++ .../SecurityAnalysisResultService.java | 58 ++++++++++-------- 4 files changed, 92 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index 5ee6e33a..d64e8855 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -148,10 +148,25 @@ public ResponseEntity> getNResult(@P @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result csv export"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity getNResultZippedCsv(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, - @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations) { + @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 filters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, + @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, + @Parameter(description = "Sort parameters") Sort sort) { + String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; return ResponseEntity.ok() .contentType(APPLICATION_OCTET_STREAM) - .body(securityAnalysisResultService.findNResultZippedCsv(resultUuid, csvTranslations)); + .body(securityAnalysisResultService.findNResultZippedCsv( + resultUuid, + networkUuid, + variantId, + fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), + fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + sort, + csvTranslations + )); } @GetMapping(value = "/results/{resultUuid}/nmk-contingencies-result/paged", produces = APPLICATION_JSON_VALUE) @@ -178,10 +193,25 @@ public ResponseEntity> getNmKContingenciesResult(@Par @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result csv export"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity getNmKContingenciesResultZippedCsv(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, - @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations) { + @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 filters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, + @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, + @Parameter(description = "Sort parameters") Sort sort) { + String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(securityAnalysisResultService.findNmKContingenciesResultZippedCsv(resultUuid, csvTranslations)); + .body(securityAnalysisResultService.findNmKContingenciesResultZippedCsv( + resultUuid, + networkUuid, + variantId, + fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), + fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + sort, + csvTranslations + )); } @GetMapping(value = "/results/{resultUuid}/nmk-constraints-result/paged", produces = APPLICATION_JSON_VALUE) @@ -207,10 +237,25 @@ public ResponseEntity> getNmKConstraintsRes @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result csv export"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) public ResponseEntity getNmKContraintsResultZippedCsv(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, - @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations) { + @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 filters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, + @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, + @Parameter(description = "Sort parameters") Sort sort) { + String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; + String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(securityAnalysisResultService.findNmKConstraintsResultZippedCsv(resultUuid, csvTranslations)); + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(securityAnalysisResultService.findNmKConstraintsResultZippedCsv( + resultUuid, + networkUuid, + variantId, + fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), + fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + sort, + csvTranslations + )); } @DeleteMapping(value = "/results", produces = APPLICATION_JSON_VALUE) diff --git a/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java b/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java index bf8c2323..1b6a352a 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java @@ -9,6 +9,7 @@ import com.powsybl.loadflow.LoadFlowResult; import org.gridsuite.securityanalysis.server.entities.ContingencyEntity; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.*; import org.springframework.stereotype.Repository; @@ -26,6 +27,9 @@ public interface ContingencyRepository extends JpaRepository findAll(Specification spec); + @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.subjectLimitViolation"}, type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec, Sort sort); + @EntityGraph(attributePaths = {"contingencyElements"}, type = EntityGraph.EntityGraphType.LOAD) List findAllWithContingencyElementsByUuidIn(List uuids); diff --git a/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java b/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java index 19e267ce..376348a3 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java @@ -7,6 +7,7 @@ package org.gridsuite.securityanalysis.server.repositories; import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.*; import org.springframework.stereotype.Repository; @@ -23,6 +24,9 @@ public interface SubjectLimitViolationRepository extends JpaRepository findAll(Specification spec); + @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.contingency"}, type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec, Sort sort); + @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.subjectLimitViolation"}, type = EntityGraph.EntityGraphType.LOAD) List findAllWithContingencyContingencyLimitViolationsByIdIn(List uuids); 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 cfdbdd60..24e5cc8e 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -147,8 +147,8 @@ public List findNResult(UUID resultUuid, } @Transactional(readOnly = true) - public byte[] findNResultZippedCsv(UUID resultUuid, CsvTranslationDTO csvTranslations) { - List result = self.findNResult(resultUuid, null, null, List.of(), null, Sort.by(Sort.Direction.ASC, AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId)); + public byte[] findNResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, Sort.by(Sort.Direction.ASC, AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId)); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRow(csvTranslations.enumValueTranslations(), csvTranslations.language())).toList()); } @@ -162,22 +162,26 @@ public Page findNmKContingenciesPaged(UUID resultUuid, UUI } @Transactional(readOnly = true) - public List findNmKContingenciesResult(UUID resultUuid) { + public List findNmKContingenciesResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { assertResultExists(resultUuid); + assertNmKContingenciesSortAllowed(sort); + 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); - List contingencies = contingencyRepository.findAllByResultId(resultUuid); - List uuids = contingencies.stream().map(ContingencyEntity::getUuid).toList(); - // fetching contingency elements to prevent n+1 requests - contingencyRepository.findAllWithContingencyElementsByUuidIn(uuids); - // fetching contingency limitViolations to prevent n+1 requests - contingencyRepository.findAllWithContingencyLimitViolationsByUuidIn(uuids); - - return contingencies.stream().map(ContingencyResultDTO::toDto).toList(); + List contingencyEntities = contingencyRepository.findAll(specification, sort); + return contingencyEntities.stream().map(ContingencyResultDTO::toDto).toList(); } @Transactional(readOnly = true) - public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, CsvTranslationDTO csvTranslations) { - List result = self.findNmKContingenciesResult(resultUuid); + public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNmKContingenciesResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, sort); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } @@ -191,24 +195,26 @@ public Page findNmKConstraintsResultPaged(UUID r } @Transactional(readOnly = true) - public List findNmKConstraintsResult(UUID resultUuid) { + public List findNmKConstraintsResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { assertResultExists(resultUuid); + assertNmKSubjectLimitViolationsSortAllowed(sort); + 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); - List subjectLimitViolations = subjectLimitViolationRepository.findAllByResultId(resultUuid); - List uuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getId).toList(); - subjectLimitViolationRepository.findAllWithContingencyContingencyLimitViolationsByIdIn(uuids); - List contingencyUuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getContingencyLimitViolations).flatMap(List::stream) - .map(lm -> lm.getContingency().getUuid()) - .toList(); - // we fetch contingencyElements for each contingency here to prevent N+1 query - contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); - - return subjectLimitViolations.stream().map(SubjectLimitViolationResultDTO::toDto).toList(); + List subjectLimitViolationEntities = subjectLimitViolationRepository.findAll(specification, sort); + return subjectLimitViolationEntities.stream().map(SubjectLimitViolationResultDTO::toDto).toList(); } @Transactional(readOnly = true) - public byte[] findNmKConstraintsResultZippedCsv(UUID resultUuid, CsvTranslationDTO csvTranslations) { - List result = self.findNmKConstraintsResult(resultUuid); + public byte[] findNmKConstraintsResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNmKConstraintsResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, sort); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } From ee0ce7b5792f28d5579bb5722f9b706e4f84f9f9 Mon Sep 17 00:00:00 2001 From: Caroline Jeandat Date: Wed, 14 Jan 2026 17:03:03 +0100 Subject: [PATCH 2/5] add sort and filters to csv export --- .../server/SecurityAnalysisController.java | 18 +- .../SecurityAnalysisResultService.java | 299 +++++++++++++----- .../server/FindContingenciesTest.java | 4 +- .../FindPreContingencyLimitViolationTest.java | 2 +- .../FindSubjectLimitViolationsTest.java | 4 +- 5 files changed, 225 insertions(+), 102 deletions(-) diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index d64e8855..0b0e5475 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -35,8 +35,6 @@ import java.util.UUID; 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.*; /** @@ -134,8 +132,8 @@ public ResponseEntity> getNResult(@P resultUuid, networkUuid, variantId, - fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), - fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + decodedStringFilters, + decodedStringGlobalFilters, sort); return result != null @@ -162,8 +160,8 @@ public ResponseEntity getNResultZippedCsv(@Parameter(description = "Resu resultUuid, networkUuid, variantId, - fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), - fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + decodedStringFilters, + decodedStringGlobalFilters, sort, csvTranslations )); @@ -207,8 +205,8 @@ public ResponseEntity getNmKContingenciesResultZippedCsv(@Parameter(desc resultUuid, networkUuid, variantId, - fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), - fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + decodedStringFilters, + decodedStringGlobalFilters, sort, csvTranslations )); @@ -251,8 +249,8 @@ public ResponseEntity getNmKContraintsResultZippedCsv(@Parameter(descrip resultUuid, networkUuid, variantId, - fromStringFiltersToDTO(decodedStringFilters, securityAnalysisResultService.getObjectMapper()), - fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, securityAnalysisResultService.getObjectMapper()), + decodedStringFilters, + decodedStringGlobalFilters, sort, csvTranslations )); 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 24e5cc8e..8763cf62 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -35,6 +35,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import static org.gridsuite.computation.error.ComputationBusinessErrorCode.INVALID_SORT_FORMAT; import static org.gridsuite.computation.error.ComputationBusinessErrorCode.RESULT_NOT_FOUND; @@ -127,17 +128,22 @@ public SecurityAnalysisResultService(SecurityAnalysisResultRepository securityAn } @Transactional(readOnly = true) - public List findNResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { + public List findNResult(UUID resultUuid, List resourceFilters, Sort sort) { assertResultExists(resultUuid); assertPreContingenciesSortAllowed(sort); - 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, resourceFilters); + List preContingencyLimitViolation = preContingencyLimitViolationRepository.findAll(specification, sort); + return preContingencyLimitViolation.stream() + .map(PreContingencyLimitViolationResultDTO::toDto) + .toList(); + } + + public List findNResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { + assertResultExists(resultUuid); + assertPreContingenciesSortAllowed(sort); + + List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterN(networkUuid, variantId, globalFilter)); Specification specification = preContingencyLimitViolationSpecificationBuilder.buildSpecification(resultUuid, allResourceFilters); List preContingencyLimitViolation = preContingencyLimitViolationRepository.findAll(specification, sort); @@ -147,9 +153,8 @@ public List findNResult(UUID resultUuid, } @Transactional(readOnly = true) - public byte[] findNResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { - List result = self.findNResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, Sort.by(Sort.Direction.ASC, AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId)); - + public byte[] findNResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNResult(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, sort); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRow(csvTranslations.enumValueTranslations(), csvTranslations.language())).toList()); } @@ -157,32 +162,23 @@ public byte[] findNResultZippedCsv(UUID resultUuid, UUID networkUuid, String var public Page findNmKContingenciesPaged(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Pageable pageable) { assertResultExists(resultUuid); - Page contingencyPageBis = self.findContingenciesPage(resultUuid, networkUuid, variantId, fromStringFiltersToDTO(stringFilters, objectMapper), fromStringGlobalFiltersToDTO(stringGlobalFilters, objectMapper), pageable); + List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterContingencies(networkUuid, variantId, globalFilter)); + Page contingencyPageBis = self.findContingenciesPage(resultUuid, allResourceFilters, pageable); return contingencyPageBis.map(ContingencyResultDTO::toDto); } @Transactional(readOnly = true) - public List findNmKContingenciesResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { + public List findNmKContingenciesResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { assertResultExists(resultUuid); - assertNmKContingenciesSortAllowed(sort); - 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); - List contingencyEntities = contingencyRepository.findAll(specification, sort); + List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterContingencies(networkUuid, variantId, globalFilter)); + List contingencyEntities = self.findContingencies(resultUuid, allResourceFilters, sort); return contingencyEntities.stream().map(ContingencyResultDTO::toDto).toList(); } @Transactional(readOnly = true) - public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { - List result = self.findNmKContingenciesResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, sort); - + public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNmKContingenciesResult(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, sort); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } @@ -190,32 +186,24 @@ public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, UUID networkU public Page findNmKConstraintsResultPaged(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Pageable pageable) { assertResultExists(resultUuid); - Page subjectLimitViolationsPage = self.findSubjectLimitViolationsPage(resultUuid, networkUuid, variantId, fromStringFiltersToDTO(stringFilters, objectMapper), fromStringGlobalFiltersToDTO(stringGlobalFilters, objectMapper), pageable); + List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterSubjectLimitViolations(networkUuid, variantId, globalFilter)); + Page subjectLimitViolationsPage = self.findSubjectLimitViolationsPage(resultUuid, allResourceFilters, pageable); return subjectLimitViolationsPage.map(SubjectLimitViolationResultDTO::toDto); } @Transactional(readOnly = true) - public List findNmKConstraintsResult(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort) { + public List findNmKConstraintsResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { assertResultExists(resultUuid); assertNmKSubjectLimitViolationsSortAllowed(sort); - 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); - List subjectLimitViolationEntities = subjectLimitViolationRepository.findAll(specification, sort); + List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterSubjectLimitViolations(networkUuid, variantId, globalFilter)); + List subjectLimitViolationEntities = self.findSubjectLimitViolations(resultUuid, allResourceFilters, sort); return subjectLimitViolationEntities.stream().map(SubjectLimitViolationResultDTO::toDto).toList(); } @Transactional(readOnly = true) - public byte[] findNmKConstraintsResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Sort sort, CsvTranslationDTO csvTranslations) { - List result = self.findNmKConstraintsResult(resultUuid, networkUuid, variantId, resourceFilters, globalFilter, sort); - + public byte[] findNmKConstraintsResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort, CsvTranslationDTO csvTranslations) { + List result = self.findNmKConstraintsResult(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, sort); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } @@ -310,19 +298,12 @@ private static Page emptyPage(Pageable pageable) { } @Transactional(readOnly = true) - public Page findContingenciesPage(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Pageable pageable) { + public Page findContingenciesPage(UUID resultUuid, List resourceFilters, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKContingenciesSortAllowed(pageable.getSort()); - Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, ContingencyEntity.Fields.uuid); - 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); + + Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSortingPage(pageable, ContingencyEntity.Fields.uuid); + Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, resourceFilters); // 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/ @@ -347,26 +328,49 @@ public Page findContingenciesPage(UUID resultUuid, UUID netwo Page contingenciesPage = new PageImpl<>(contingencies, pageable, uuidPage.getTotalElements()); // then we append the missing data, and filter some of the Lazy Loaded collections - appendLimitViolationsAndElementsToContingenciesResult(contingenciesPage, allResourceFilters); + appendLimitViolationsAndElementsToContingenciesResultPage(contingenciesPage, resourceFilters); return contingenciesPage; } } @Transactional(readOnly = true) - public Page findSubjectLimitViolationsPage(UUID resultUuid, UUID networkUuid, String variantId, List resourceFilters, GlobalFilter globalFilter, Pageable pageable) { + public List findContingencies(UUID resultUuid, List resourceFilters, Sort sort) { + Objects.requireNonNull(resultUuid); + assertNmKContingenciesSortAllowed(sort); + + Sort finalSort = addDefaultSortAndRemoveChildrenSorting(sort, ContingencyEntity.Fields.uuid); + Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, resourceFilters); + + // 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/ + // We must separate in two requests, one with pagination the other one with Join Fetch + + // ---- First query: UUIDs only (sorted) + List uuidList = contingencyRepository.findBy(specification, q -> + q.project("uuid") + .as(ContingencyRepository.EntityUuid.class) + .sortBy(finalSort) + .all() + ); + List uuids = uuidList.stream().map(ContingencyRepository.EntityUuid::getUuid).toList(); + // Then we fetch the main entities data for each UUID + List contingencies = contingencyRepository.findAllByUuidIn(uuids); + contingencies.sort(Comparator.comparing(c -> uuids.indexOf(c.getUuid()))); + + appendLimitViolationsAndElementsToContingenciesResult(contingencies, resourceFilters, sort); + + return contingencies; + } + + @Transactional(readOnly = true) + public Page findSubjectLimitViolationsPage(UUID resultUuid, List resourceFilters, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKSubjectLimitViolationsSortAllowed(pageable.getSort()); - Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, SubjectLimitViolationEntity.Fields.id); - 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); + + Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSortingPage(pageable, SubjectLimitViolationEntity.Fields.id); + Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); // 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/ @@ -389,12 +393,41 @@ 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, allResourceFilters); + appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResultPage(subjectLimitViolationPage, resourceFilters); return subjectLimitViolationPage; } } + @Transactional(readOnly = true) + public List findSubjectLimitViolations(UUID resultUuid, List resourceFilters, Sort sort) { + Objects.requireNonNull(resultUuid); // + assertNmKSubjectLimitViolationsSortAllowed(sort); + + Sort finalSort = addDefaultSortAndRemoveChildrenSorting(sort, SubjectLimitViolationEntity.Fields.id); + Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); + // 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/ + // We must separate in two requests, one with pagination the other one with Join Fetch + List uuidList = subjectLimitViolationRepository.findBy(specification, q -> + q.project("id") + .as(SubjectLimitViolationRepository.EntityId.class) + .sortBy(finalSort) + .all() + ); + + List uuids = uuidList.stream().map(u -> u.getId()).toList(); + // Then we fetch the main entities data for each UUID + List subjectLimitViolations = subjectLimitViolationRepository.findAllByIdIn(uuids); + subjectLimitViolations.sort(Comparator.comparing(lm -> uuids.indexOf(lm.getId()))); + + // then we append the missing data, and filter some of the Lazy Loaded collections + appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(subjectLimitViolations, resourceFilters, finalSort); + + return subjectLimitViolations; + } + @Transactional(readOnly = true) public List findNResultLimitTypes(UUID resultUuid) { Objects.requireNonNull(resultUuid); @@ -425,7 +458,21 @@ public List findNmKC return contingencyRepository.findComputingStatus(resultUuid); } - private void appendLimitViolationsAndElementsToContingenciesResult(Page contingencies, List resourceFilters) { + private List getAllResourceFilters(String stringFilters, String stringGlobalFilter, Function> getResourceGlobalFilter) { + List resourceFilters = fromStringFiltersToDTO(stringFilters, objectMapper); + GlobalFilter globalFilter = fromStringGlobalFiltersToDTO(stringGlobalFilter, objectMapper); + List allResourceFilters = new ArrayList<>(); + if (resourceFilters != null) { + allResourceFilters.addAll(resourceFilters); + } + if (globalFilter != null) { + Optional resourceGlobalFilters = getResourceGlobalFilter.apply(globalFilter); + resourceGlobalFilters.ifPresent(allResourceFilters::add); + } + return allResourceFilters; + } + + private void appendLimitViolationsAndElementsToContingenciesResultPage(Page contingencies, List resourceFilters) { // using the the Hibernate First-Level Cache or Persistence Context // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ @@ -438,11 +485,28 @@ private void appendLimitViolationsAndElementsToContingenciesResult(Page contingencies, List resourceFilters, Sort sort) { + + // using the the Hibernate First-Level Cache or Persistence Context + // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ + if (!contingencies.isEmpty()) { + List contingencyUuids = contingencies.stream() + .map(c -> c.getUuid()) + .toList(); + Specification specification = contingencySpecificationBuilder.buildLimitViolationsSpecification(contingencyUuids, resourceFilters); + contingencyRepository.findAll(specification); + // we fetch contingencyElements here to prevent N+1 query + contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); + + sortLimitViolationsInContingencies(contingencies, sort); } } - private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(Page subjectLimitViolations, List resourceFilters) { + private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResultPage(Page subjectLimitViolations, List resourceFilters) { // using the the Hibernate First-Level Cache or Persistence Context // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ @@ -459,11 +523,32 @@ private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolations // we fetch contingencyElements for each contingency here to prevent N+1 query contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); - sortLimitViolationsInSubjectLimitViolations(subjectLimitViolations); + sortLimitViolationsInSubjectLimitViolationsPage(subjectLimitViolations); } } - private void sortLimitViolationsInContingencies(Page contingencies) { + private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(List subjectLimitViolations, List resourceFilters, Sort sort) { + + // using the the Hibernate First-Level Cache or Persistence Context + // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ + if (!subjectLimitViolations.isEmpty()) { + List subjectLimitViolationsUuids = subjectLimitViolations.stream() + .map(c -> c.getId()) + .toList(); + Specification specification = subjectLimitViolationSpecificationBuilder.buildLimitViolationsSpecification(subjectLimitViolationsUuids, resourceFilters); + subjectLimitViolationRepository.findAll(specification); + + List contingencyUuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getContingencyLimitViolations).flatMap(List::stream) + .map(lm -> lm.getContingency().getUuid()) + .toList(); + // we fetch contingencyElements for each contingency here to prevent N+1 query + contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); + + sortLimitViolationsInSubjectLimitViolations(subjectLimitViolations, sort); + } + } + + private void sortLimitViolationsInContingenciesPage(Page contingencies) { Optional lvSortOrder = contingencies.getSort().get() // we filter sort on nested limit violations .filter(sortOrder -> @@ -482,14 +567,27 @@ private void sortLimitViolationsInContingencies(Page continge comparator.reversed())); } - private void sortLimitViolationsInSubjectLimitViolations(Page subjectLimitViolations) { - Optional lvSortOrder = subjectLimitViolations.getSort().get() - // we filter sort on nested limit violations - .filter(sortOrder -> - sortOrder.getProperty().startsWith(SubjectLimitViolationEntity.Fields.contingencyLimitViolations)) - // for now, only one children sort possible - .findFirst(); + private void sortLimitViolationsInContingencies(List contingencies, Sort sort) { + Optional lvSortOrder = sort.get() + // we filter sort on nested limit violations + .filter(sortOrder -> + sortOrder.getProperty().startsWith(ContingencyEntity.Fields.contingencyLimitViolations)) + // for now, only one children sort possible + .findFirst(); + + Comparator comparator = getLimitViolationComparatorForContingencies(lvSortOrder); + boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); + + contingencies.forEach(contingency -> contingency + .getContingencyLimitViolations() + .sort(isSortOrderAscending ? + comparator : + comparator.reversed())); + } + + private void sortLimitViolationsInSubjectLimitViolationsPage(Page subjectLimitViolations) { + Optional lvSortOrder = getLimitViolationLvSortOrderForSubjectLimitViolations(subjectLimitViolations.getSort()); Comparator comparator = getLimitViolationComparatorForSubjectLimitViolations(lvSortOrder); boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); @@ -501,6 +599,28 @@ private void sortLimitViolationsInSubjectLimitViolations(Page subjectLimitViolations, Sort sort) { + Optional lvSortOrder = getLimitViolationLvSortOrderForSubjectLimitViolations(sort); + Comparator comparator = getLimitViolationComparatorForSubjectLimitViolations(lvSortOrder); + + boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); + + subjectLimitViolations.forEach(subjectLimitViolation -> subjectLimitViolation + .getContingencyLimitViolations() + .sort(isSortOrderAscending ? + comparator : + comparator.reversed())); + } + + private Optional getLimitViolationLvSortOrderForSubjectLimitViolations(Sort sort) { + return sort.get() + // we filter sort on nested limit violations + .filter(sortOrder -> + sortOrder.getProperty().startsWith(SubjectLimitViolationEntity.Fields.contingencyLimitViolations)) + // for now, only one children sort possible + .findFirst(); + } + private static Comparator getLimitViolationComparatorForContingencies(Optional lvSortOrder) { if (lvSortOrder.isPresent()) { String field = lvSortOrder.get().getProperty() @@ -559,18 +679,23 @@ private static Comparator getCommonComparator(S }; } - private Pageable addDefaultSortAndRemoveChildrenSorting(Pageable pageable, String defaultSortColumn) { + private Pageable addDefaultSortAndRemoveChildrenSortingPage(Pageable pageable, String defaultSortColumn) { if (pageable.isPaged()) { - // Can't use both distinct and sort on nested field here, so we have to remove "children" sorting. Maybe there is a way to do it ? - // https://github.com/querydsl/querydsl/issues/2443 - Sort finalSort = Sort.by(pageable.getSort().filter(sortOrder -> !sortOrder.getProperty().startsWith("contingencyLimitViolations")).toList()); - //if it's already sorted by our defaultColumn we don't add another sort by the same column - if (finalSort.getOrderFor(defaultSortColumn) == null) { - finalSort = finalSort.and(Sort.by(DEFAULT_SORT_DIRECTION, defaultSortColumn)); - } + Sort finalSort = addDefaultSortAndRemoveChildrenSorting(pageable.getSort(), defaultSortColumn); return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), finalSort); } //nothing to do if the request is not paged return pageable; } + + private Sort addDefaultSortAndRemoveChildrenSorting(Sort sort, String defaultSortColumn) { + // Can't use both distinct and sort on nested field here, so we have to remove "children" sorting. Maybe there is a way to do it ? + // https://github.com/querydsl/querydsl/issues/2443 + Sort finalSort = Sort.by(sort.filter(sortOrder -> !sortOrder.getProperty().startsWith("contingencyLimitViolations")).toList()); + //if it's already sorted by our defaultColumn we don't add another sort by the same column + if (finalSort.getOrderFor(defaultSortColumn) == null) { + finalSort = finalSort.and(Sort.by(DEFAULT_SORT_DIRECTION, defaultSortColumn)); + } + return finalSort; + } } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java b/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java index 4335755a..280008aa 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindContingenciesTest.java @@ -88,7 +88,7 @@ void tearDown() { }) void findFilteredContingencyResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) { reset(); - Page contingenciesPage = securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), null, null, filters, null, pageable); + Page contingenciesPage = securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), filters, pageable); // assert contingency ids to check parent filters assertThat(contingenciesPage.getContent()).extracting(ContingencyEntity.Fields.contingencyId).containsExactlyElementsOf(expectedResult.stream().map(c -> c.getContingency().getContingencyId()).toList()); @@ -111,7 +111,7 @@ void findFilteredContingencyResultsTest(List filters, Pageabl "provideForbiddenFilter" }) void testSortAndFilterErrors(List filters, Pageable pageable, Exception expectedException) { - Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), null, null, filters, null, pageable)); + Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findContingenciesPage(resultEntity.getId(), filters, 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 9d6b1ea6..5943feea 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(), null, null, filters, null, sort); + List preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), filters, 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 2e60b3a4..d7484884 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java @@ -83,7 +83,7 @@ void tearDown() { }) void findFilteredSubjectLimitViolationResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) { reset(); - Page subjectLimitViolationPage = securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), null, null, filters, null, pageable); + Page subjectLimitViolationPage = securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), filters, pageable); // assert subject ids to check parent filters assertThat(subjectLimitViolationPage.getContent()).extracting("subjectId").containsExactlyElementsOf(expectedResult.stream().map(SubjectLimitViolationResultDTO::getSubjectId).toList()); @@ -109,7 +109,7 @@ void findFilteredSubjectLimitViolationResultsTest(List filter "provideForbiddenFilter" }) void testSortAndFilterErrors(List filters, Pageable pageable, Exception expectedException) { - Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), null, null, filters, null, pageable)); + Exception exception = assertThrows(expectedException.getClass(), () -> securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), filters, pageable)); assertEquals(expectedException.getMessage(), exception.getMessage()); } From 0e62247da643387883e7898a5b448634493ed153 Mon Sep 17 00:00:00 2001 From: Caroline Jeandat Date: Tue, 27 Jan 2026 08:27:00 +0100 Subject: [PATCH 3/5] add sort and filters to csv export --- .../server/SecurityAnalysisController.java | 41 +-- .../repositories/ContingencyRepository.java | 4 - .../SubjectLimitViolationRepository.java | 4 - .../SecurityAnalysisResultService.java | 245 +++++------------- .../SecurityAnalysisControllerTest.java | 38 ++- .../results/n-result-with-filter-en.csv | 3 + .../results/nmk-constraints-result-en.csv | 6 +- .../results/nmk-constraints-result-fr.csv | 6 +- .../nmk-constraints-result-with-filter-en.csv | 14 + .../results/nmk-contingencies-result-en.csv | 12 +- .../results/nmk-contingencies-result-fr.csv | 12 +- ...mk-contingencies-result-with-filter-en.csv | 14 + 12 files changed, 148 insertions(+), 251 deletions(-) create mode 100644 src/test/resources/results/n-result-with-filter-en.csv create mode 100644 src/test/resources/results/nmk-constraints-result-with-filter-en.csv create mode 100644 src/test/resources/results/nmk-contingencies-result-with-filter-en.csv diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index 11b33b81..4184e694 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -30,8 +30,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.UUID; @@ -124,17 +122,15 @@ public ResponseEntity runAndSave(@Parameter(description = "Network UUID") 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 = "Filters") @RequestParam(name = "filters", required = false) String filters, @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, - decodedStringFilters, - decodedStringGlobalFilters, + filters, + globalFilters, sort); return result != null @@ -153,16 +149,14 @@ public ResponseEntity getNResultZippedCsv(@Parameter(description = "Resu @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, @Parameter(description = "Sort parameters") Sort sort) { - String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; - String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; return ResponseEntity.ok() .contentType(APPLICATION_OCTET_STREAM) .body(securityAnalysisResultService.findNResultZippedCsv( resultUuid, networkUuid, variantId, - decodedStringFilters, - decodedStringGlobalFilters, + filters, + globalFilters, sort, csvTranslations )); @@ -175,12 +169,10 @@ public ResponseEntity getNResultZippedCsv(@Parameter(description = "Resu public ResponseEntity> getPagedNmKContingenciesResult(@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 = "Filters") @RequestParam(name = "filters", required = false) String filters, @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; - String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; - Page result = securityAnalysisResultService.findNmKContingenciesPaged(resultUuid, networkUuid, variantId, decodedStringFilters, decodedStringGlobalFilters, pageable); + Page result = securityAnalysisResultService.findNmKContingenciesPaged(resultUuid, networkUuid, variantId, filters, globalFilters, pageable); return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result) @@ -210,16 +202,14 @@ public ResponseEntity getNmKContingenciesResultZippedCsv(@Parameter(desc @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, @Parameter(description = "Sort parameters") Sort sort) { - String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; - String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(securityAnalysisResultService.findNmKContingenciesResultZippedCsv( resultUuid, networkUuid, variantId, - decodedStringFilters, - decodedStringGlobalFilters, + filters, + globalFilters, sort, csvTranslations )); @@ -232,12 +222,10 @@ public ResponseEntity getNmKContingenciesResultZippedCsv(@Parameter(desc 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 = "Filters") @RequestParam(name = "filters", required = false) String filters, @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; - String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; - Page result = securityAnalysisResultService.findNmKConstraintsResultPaged(resultUuid, networkUuid, variantId, decodedStringFilters, decodedStringGlobalFilters, pageable); + Page result = securityAnalysisResultService.findNmKConstraintsResultPaged(resultUuid, networkUuid, variantId, filters, globalFilters, pageable); return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result) : ResponseEntity.notFound().build(); @@ -254,16 +242,15 @@ public ResponseEntity getNmKContraintsResultZippedCsv(@Parameter(descrip @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, @Parameter(description = "Translation properties") @RequestBody CsvTranslationDTO csvTranslations, @Parameter(description = "Sort parameters") Sort sort) { - String decodedStringFilters = filters != null ? URLDecoder.decode(filters, StandardCharsets.UTF_8) : null; - String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; + return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(securityAnalysisResultService.findNmKConstraintsResultZippedCsv( resultUuid, networkUuid, variantId, - decodedStringFilters, - decodedStringGlobalFilters, + filters, + globalFilters, sort, csvTranslations )); diff --git a/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java b/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java index 1b6a352a..bf8c2323 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/repositories/ContingencyRepository.java @@ -9,7 +9,6 @@ import com.powsybl.loadflow.LoadFlowResult; import org.gridsuite.securityanalysis.server.entities.ContingencyEntity; -import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.*; import org.springframework.stereotype.Repository; @@ -27,9 +26,6 @@ public interface ContingencyRepository extends JpaRepository findAll(Specification spec); - @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.subjectLimitViolation"}, type = EntityGraph.EntityGraphType.LOAD) - List findAll(Specification spec, Sort sort); - @EntityGraph(attributePaths = {"contingencyElements"}, type = EntityGraph.EntityGraphType.LOAD) List findAllWithContingencyElementsByUuidIn(List uuids); diff --git a/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java b/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java index 376348a3..19e267ce 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/repositories/SubjectLimitViolationRepository.java @@ -7,7 +7,6 @@ package org.gridsuite.securityanalysis.server.repositories; import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; -import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.*; import org.springframework.stereotype.Repository; @@ -24,9 +23,6 @@ public interface SubjectLimitViolationRepository extends JpaRepository findAll(Specification spec); - @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.contingency"}, type = EntityGraph.EntityGraphType.LOAD) - List findAll(Specification spec, Sort sort); - @EntityGraph(attributePaths = {"contingencyLimitViolations", "contingencyLimitViolations.subjectLimitViolation"}, type = EntityGraph.EntityGraphType.LOAD) List findAllWithContingencyContingencyLimitViolationsByIdIn(List uuids); 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 d2735eb7..647cf937 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -33,6 +33,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -140,17 +142,10 @@ public List findNResult(UUID resultUuid, .toList(); } + @Transactional(readOnly = true) public List findNResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { - assertResultExists(resultUuid); - assertPreContingenciesSortAllowed(sort); - - List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterN(networkUuid, variantId, globalFilter)); - Specification specification = preContingencyLimitViolationSpecificationBuilder.buildSpecification(resultUuid, allResourceFilters); - - List preContingencyLimitViolation = preContingencyLimitViolationRepository.findAll(specification, sort); - return preContingencyLimitViolation.stream() - .map(PreContingencyLimitViolationResultDTO::toDto) - .toList(); + List resourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterN(networkUuid, variantId, globalFilter)); + return self.findNResult(resultUuid, resourceFilters, sort); } @Transactional(readOnly = true) @@ -169,17 +164,22 @@ public Page findNmKContingenciesPaged(UUID resultUuid, UUI } @Transactional(readOnly = true) - public List findNmKContingenciesResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { + public List findNmKContingenciesResult(UUID resultUuid) { assertResultExists(resultUuid); - List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterContingencies(networkUuid, variantId, globalFilter)); - List contingencyEntities = self.findContingencies(resultUuid, allResourceFilters, sort); - return contingencyEntities.stream().map(ContingencyResultDTO::toDto).toList(); + List contingencies = contingencyRepository.findAllByResultId(resultUuid); + List uuids = contingencies.stream().map(ContingencyEntity::getUuid).toList(); + // fetching contingency elements to prevent n+1 requests + contingencyRepository.findAllWithContingencyElementsByUuidIn(uuids); + // fetching contingency limitViolations to prevent n+1 requests + contingencyRepository.findAllWithContingencyLimitViolationsByUuidIn(uuids); + + return contingencies.stream().map(ContingencyResultDTO::toDto).toList(); } @Transactional(readOnly = true) public byte[] findNmKContingenciesResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort, CsvTranslationDTO csvTranslations) { - List result = self.findNmKContingenciesResult(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, sort); + List result = self.findNmKContingenciesPaged(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, Pageable.unpaged(sort)).getContent(); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } @@ -193,18 +193,24 @@ public Page findNmKConstraintsResultPaged(UUID r } @Transactional(readOnly = true) - public List findNmKConstraintsResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { + public List findNmKConstraintsResult(UUID resultUuid) { assertResultExists(resultUuid); - assertNmKSubjectLimitViolationsSortAllowed(sort); - List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterSubjectLimitViolations(networkUuid, variantId, globalFilter)); - List subjectLimitViolationEntities = self.findSubjectLimitViolations(resultUuid, allResourceFilters, sort); - return subjectLimitViolationEntities.stream().map(SubjectLimitViolationResultDTO::toDto).toList(); + List subjectLimitViolations = subjectLimitViolationRepository.findAllByResultId(resultUuid); + List uuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getId).toList(); + subjectLimitViolationRepository.findAllWithContingencyContingencyLimitViolationsByIdIn(uuids); + List contingencyUuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getContingencyLimitViolations).flatMap(List::stream) + .map(lm -> lm.getContingency().getUuid()) + .toList(); + // we fetch contingencyElements for each contingency here to prevent N+1 query + contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); + + return subjectLimitViolations.stream().map(SubjectLimitViolationResultDTO::toDto).toList(); } @Transactional(readOnly = true) public byte[] findNmKConstraintsResultZippedCsv(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort, CsvTranslationDTO csvTranslations) { - List result = self.findNmKConstraintsResult(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, sort); + List result = self.findNmKConstraintsResultPaged(resultUuid, networkUuid, variantId, stringFilters, stringGlobalFilters, Pageable.unpaged(sort)).getContent(); return CsvExportUtils.csvRowsToZippedCsv(csvTranslations.headers(), csvTranslations.language(), result.stream().map(r -> r.toCsvRows(csvTranslations.enumValueTranslations(), csvTranslations.language())).flatMap(List::stream).toList()); } @@ -302,8 +308,7 @@ private static Page emptyPage(Pageable pageable) { public Page findContingenciesPage(UUID resultUuid, List resourceFilters, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKContingenciesSortAllowed(pageable.getSort()); - - Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSortingPage(pageable, ContingencyEntity.Fields.uuid); + Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, ContingencyEntity.Fields.uuid); Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, resourceFilters); // WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl - // HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! @@ -329,48 +334,17 @@ 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 - appendLimitViolationsAndElementsToContingenciesResultPage(contingenciesPage, resourceFilters); + appendLimitViolationsAndElementsToContingenciesResult(contingenciesPage, resourceFilters); return contingenciesPage; } } - @Transactional(readOnly = true) - public List findContingencies(UUID resultUuid, List resourceFilters, Sort sort) { - Objects.requireNonNull(resultUuid); - assertNmKContingenciesSortAllowed(sort); - - Sort finalSort = addDefaultSortAndRemoveChildrenSorting(sort, ContingencyEntity.Fields.uuid); - Specification specification = contingencySpecificationBuilder.buildSpecification(resultUuid, resourceFilters); - - // 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/ - // We must separate in two requests, one with pagination the other one with Join Fetch - - // ---- First query: UUIDs only (sorted) - List uuidList = contingencyRepository.findBy(specification, q -> - q.project("uuid") - .as(ContingencyRepository.EntityUuid.class) - .sortBy(finalSort) - .all() - ); - List uuids = uuidList.stream().map(ContingencyRepository.EntityUuid::getUuid).toList(); - // Then we fetch the main entities data for each UUID - List contingencies = contingencyRepository.findAllByUuidIn(uuids); - contingencies.sort(Comparator.comparing(c -> uuids.indexOf(c.getUuid()))); - - appendLimitViolationsAndElementsToContingenciesResult(contingencies, resourceFilters, sort); - - return contingencies; - } - @Transactional(readOnly = true) public Page findSubjectLimitViolationsPage(UUID resultUuid, List resourceFilters, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKSubjectLimitViolationsSortAllowed(pageable.getSort()); - - Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSortingPage(pageable, SubjectLimitViolationEntity.Fields.id); + Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, SubjectLimitViolationEntity.Fields.id); Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); // WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl - // HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! @@ -387,48 +361,19 @@ public Page findSubjectLimitViolationsPage(UUID res // Since springboot 3.2, the return value of Page.empty() is not serializable. See https://github.com/spring-projects/spring-data-commons/issues/2987 return (Page) emptyPage(pageable); } else { - List uuids = uuidPage.map(u -> u.getId()).toList(); + List uuids = uuidPage.map(SubjectLimitViolationRepository.EntityId::getId).toList(); // Then we fetch the main entities data for each UUID List subjectLimitViolations = subjectLimitViolationRepository.findAllByIdIn(uuids); subjectLimitViolations.sort(Comparator.comparing(lm -> uuids.indexOf(lm.getId()))); Page subjectLimitViolationPage = new PageImpl<>(subjectLimitViolations, pageable, uuidPage.getTotalElements()); // then we append the missing data, and filter some of the Lazy Loaded collections - appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResultPage(subjectLimitViolationPage, resourceFilters); + appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(subjectLimitViolationPage, resourceFilters); return subjectLimitViolationPage; } } - @Transactional(readOnly = true) - public List findSubjectLimitViolations(UUID resultUuid, List resourceFilters, Sort sort) { - Objects.requireNonNull(resultUuid); // - assertNmKSubjectLimitViolationsSortAllowed(sort); - - Sort finalSort = addDefaultSortAndRemoveChildrenSorting(sort, SubjectLimitViolationEntity.Fields.id); - Specification specification = subjectLimitViolationSpecificationBuilder.buildSpecification(resultUuid, resourceFilters); - // 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/ - // We must separate in two requests, one with pagination the other one with Join Fetch - List uuidList = subjectLimitViolationRepository.findBy(specification, q -> - q.project("id") - .as(SubjectLimitViolationRepository.EntityId.class) - .sortBy(finalSort) - .all() - ); - - List uuids = uuidList.stream().map(u -> u.getId()).toList(); - // Then we fetch the main entities data for each UUID - List subjectLimitViolations = subjectLimitViolationRepository.findAllByIdIn(uuids); - subjectLimitViolations.sort(Comparator.comparing(lm -> uuids.indexOf(lm.getId()))); - - // then we append the missing data, and filter some of the Lazy Loaded collections - appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(subjectLimitViolations, resourceFilters, finalSort); - - return subjectLimitViolations; - } - @Transactional(readOnly = true) public List findNResultLimitTypes(UUID resultUuid) { Objects.requireNonNull(resultUuid); @@ -460,9 +405,13 @@ public List findNmKC } private List getAllResourceFilters(String stringFilters, String stringGlobalFilter, Function> getResourceGlobalFilter) { - List resourceFilters = fromStringFiltersToDTO(stringFilters, objectMapper); - GlobalFilter globalFilter = fromStringGlobalFiltersToDTO(stringGlobalFilter, objectMapper); + String decodedStringFilters = stringFilters != null ? URLDecoder.decode(stringFilters, StandardCharsets.UTF_8) : null; + String decodedStringGlobalFilters = stringGlobalFilter != null ? URLDecoder.decode(stringGlobalFilter, StandardCharsets.UTF_8) : null; + + List resourceFilters = fromStringFiltersToDTO(decodedStringFilters, objectMapper); + GlobalFilter globalFilter = fromStringGlobalFiltersToDTO(decodedStringGlobalFilters, objectMapper); List allResourceFilters = new ArrayList<>(); + if (resourceFilters != null) { allResourceFilters.addAll(resourceFilters); } @@ -473,7 +422,7 @@ private List getAllResourceFilters(String stringFilters, Stri return allResourceFilters; } - private void appendLimitViolationsAndElementsToContingenciesResultPage(Page contingencies, List resourceFilters) { + private void appendLimitViolationsAndElementsToContingenciesResult(Page contingencies, List resourceFilters) { // using the the Hibernate First-Level Cache or Persistence Context // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ @@ -486,34 +435,17 @@ private void appendLimitViolationsAndElementsToContingenciesResultPage(Page contingencies, List resourceFilters, Sort sort) { - - // using the the Hibernate First-Level Cache or Persistence Context - // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ - if (!contingencies.isEmpty()) { - List contingencyUuids = contingencies.stream() - .map(c -> c.getUuid()) - .toList(); - Specification specification = contingencySpecificationBuilder.buildLimitViolationsSpecification(contingencyUuids, resourceFilters); - contingencyRepository.findAll(specification); - // we fetch contingencyElements here to prevent N+1 query - contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); - - sortLimitViolationsInContingencies(contingencies, sort); - } - } - - private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResultPage(Page subjectLimitViolations, List resourceFilters) { + private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(Page subjectLimitViolations, List resourceFilters) { // using the the Hibernate First-Level Cache or Persistence Context // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ if (!subjectLimitViolations.isEmpty()) { List subjectLimitViolationsUuids = subjectLimitViolations.stream() - .map(c -> c.getId()) + .map(SubjectLimitViolationEntity::getId) .toList(); Specification specification = subjectLimitViolationSpecificationBuilder.buildLimitViolationsSpecification(subjectLimitViolationsUuids, resourceFilters); subjectLimitViolationRepository.findAll(specification); @@ -524,32 +456,11 @@ private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolations // we fetch contingencyElements for each contingency here to prevent N+1 query contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); - sortLimitViolationsInSubjectLimitViolationsPage(subjectLimitViolations); - } - } - - private void appendLimitViolationsAndContingencyElementsToSubjectLimitViolationsResult(List subjectLimitViolations, List resourceFilters, Sort sort) { - - // using the the Hibernate First-Level Cache or Persistence Context - // cf.https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/ - if (!subjectLimitViolations.isEmpty()) { - List subjectLimitViolationsUuids = subjectLimitViolations.stream() - .map(c -> c.getId()) - .toList(); - Specification specification = subjectLimitViolationSpecificationBuilder.buildLimitViolationsSpecification(subjectLimitViolationsUuids, resourceFilters); - subjectLimitViolationRepository.findAll(specification); - - List contingencyUuids = subjectLimitViolations.stream().map(SubjectLimitViolationEntity::getContingencyLimitViolations).flatMap(List::stream) - .map(lm -> lm.getContingency().getUuid()) - .toList(); - // we fetch contingencyElements for each contingency here to prevent N+1 query - contingencyRepository.findAllWithContingencyElementsByUuidIn(contingencyUuids); - - sortLimitViolationsInSubjectLimitViolations(subjectLimitViolations, sort); + sortLimitViolationsInSubjectLimitViolations(subjectLimitViolations); } } - private void sortLimitViolationsInContingenciesPage(Page contingencies) { + private void sortLimitViolationsInContingencies(Page contingencies) { Optional lvSortOrder = contingencies.getSort().get() // we filter sort on nested limit violations .filter(sortOrder -> @@ -568,27 +479,14 @@ private void sortLimitViolationsInContingenciesPage(Page cont comparator.reversed())); } - private void sortLimitViolationsInContingencies(List contingencies, Sort sort) { - Optional lvSortOrder = sort.get() - // we filter sort on nested limit violations - .filter(sortOrder -> - sortOrder.getProperty().startsWith(ContingencyEntity.Fields.contingencyLimitViolations)) - // for now, only one children sort possible - .findFirst(); - - Comparator comparator = getLimitViolationComparatorForContingencies(lvSortOrder); - - boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); - - contingencies.forEach(contingency -> contingency - .getContingencyLimitViolations() - .sort(isSortOrderAscending ? - comparator : - comparator.reversed())); - } + private void sortLimitViolationsInSubjectLimitViolations(Page subjectLimitViolations) { + Optional lvSortOrder = subjectLimitViolations.getSort().get() + // we filter sort on nested limit violations + .filter(sortOrder -> + sortOrder.getProperty().startsWith(SubjectLimitViolationEntity.Fields.contingencyLimitViolations)) + // for now, only one children sort possible + .findFirst(); - private void sortLimitViolationsInSubjectLimitViolationsPage(Page subjectLimitViolations) { - Optional lvSortOrder = getLimitViolationLvSortOrderForSubjectLimitViolations(subjectLimitViolations.getSort()); Comparator comparator = getLimitViolationComparatorForSubjectLimitViolations(lvSortOrder); boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); @@ -600,28 +498,6 @@ private void sortLimitViolationsInSubjectLimitViolationsPage(Page subjectLimitViolations, Sort sort) { - Optional lvSortOrder = getLimitViolationLvSortOrderForSubjectLimitViolations(sort); - Comparator comparator = getLimitViolationComparatorForSubjectLimitViolations(lvSortOrder); - - boolean isSortOrderAscending = lvSortOrder.map(Sort.Order::isAscending).orElse(true); - - subjectLimitViolations.forEach(subjectLimitViolation -> subjectLimitViolation - .getContingencyLimitViolations() - .sort(isSortOrderAscending ? - comparator : - comparator.reversed())); - } - - private Optional getLimitViolationLvSortOrderForSubjectLimitViolations(Sort sort) { - return sort.get() - // we filter sort on nested limit violations - .filter(sortOrder -> - sortOrder.getProperty().startsWith(SubjectLimitViolationEntity.Fields.contingencyLimitViolations)) - // for now, only one children sort possible - .findFirst(); - } - private static Comparator getLimitViolationComparatorForContingencies(Optional lvSortOrder) { if (lvSortOrder.isPresent()) { String field = lvSortOrder.get().getProperty() @@ -680,23 +556,18 @@ private static Comparator getCommonComparator(S }; } - private Pageable addDefaultSortAndRemoveChildrenSortingPage(Pageable pageable, String defaultSortColumn) { - if (pageable.isPaged()) { - Sort finalSort = addDefaultSortAndRemoveChildrenSorting(pageable.getSort(), defaultSortColumn); - return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), finalSort); - } - //nothing to do if the request is not paged - return pageable; - } - - private Sort addDefaultSortAndRemoveChildrenSorting(Sort sort, String defaultSortColumn) { + private Pageable addDefaultSortAndRemoveChildrenSorting(Pageable pageable, String defaultSortColumn) { // Can't use both distinct and sort on nested field here, so we have to remove "children" sorting. Maybe there is a way to do it ? // https://github.com/querydsl/querydsl/issues/2443 - Sort finalSort = Sort.by(sort.filter(sortOrder -> !sortOrder.getProperty().startsWith("contingencyLimitViolations")).toList()); + Sort finalSort = Sort.by(pageable.getSort().filter(sortOrder -> !sortOrder.getProperty().startsWith("contingencyLimitViolations")).toList()); //if it's already sorted by our defaultColumn we don't add another sort by the same column if (finalSort.getOrderFor(defaultSortColumn) == null) { finalSort = finalSort.and(Sort.by(DEFAULT_SORT_DIRECTION, defaultSortColumn)); } - return finalSort; + if (pageable.isPaged()) { + return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), finalSort); + } else { + return Pageable.unpaged(finalSort); + } } } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java index eb9404ab..efeb8f36 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java @@ -32,6 +32,7 @@ import org.gridsuite.computation.utils.SpecificationUtils; import org.gridsuite.securityanalysis.server.dto.*; import org.gridsuite.securityanalysis.server.entities.AbstractLimitViolationEntity; +import org.gridsuite.securityanalysis.server.entities.ContingencyEntity; import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; import org.gridsuite.securityanalysis.server.repositories.SubjectLimitViolationRepository; import org.gridsuite.securityanalysis.server.service.ActionsService; @@ -917,6 +918,7 @@ void getZippedCsvResults() throws Exception { assertEquals("me", resultMessage.getHeaders().get("receiver")); checkAllZippedCsvResults(); + checkFiltersOnZippedCsvResults(); } @Test @@ -962,7 +964,7 @@ private List getCsvHeaderFromResource(String resourcePath, String lang) private void checkAllZippedCsvResults() throws Exception { SQLStatementCountValidator.reset(); - checkZippedCsvResult("n-result", "/results/n-result-en.csv", "en"); + checkZippedCsvResult("n-result", "/results/n-result-en.csv", "en", AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId, null); /* * SELECT * assert result exists @@ -971,10 +973,10 @@ private void checkAllZippedCsvResults() throws Exception { assertRequestsCount(2, 0, 0, 0); SQLStatementCountValidator.reset(); - checkZippedCsvResult("n-result", "/results/n-result-fr.csv", "fr"); + checkZippedCsvResult("n-result", "/results/n-result-fr.csv", "fr", AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId, null); SQLStatementCountValidator.reset(); - checkZippedCsvResult("nmk-contingencies-result", "/results/nmk-contingencies-result-en.csv", "en"); + checkZippedCsvResult("nmk-contingencies-result", "/results/nmk-contingencies-result-en.csv", "en", ContingencyEntity.Fields.contingencyId, null); /* * SELECT @@ -983,14 +985,14 @@ private void checkAllZippedCsvResults() throws Exception { * join contingency_entity_contingency_elements * join contingency_limit_violation and subject_limit_violation */ - assertRequestsCount(4, 0, 0, 0); + assertRequestsCount(5, 0, 0, 0); SQLStatementCountValidator.reset(); - checkZippedCsvResult("nmk-contingencies-result", "/results/nmk-contingencies-result-fr.csv", "fr"); - assertRequestsCount(4, 0, 0, 0); + checkZippedCsvResult("nmk-contingencies-result", "/results/nmk-contingencies-result-fr.csv", "fr", ContingencyEntity.Fields.contingencyId, null); + assertRequestsCount(5, 0, 0, 0); SQLStatementCountValidator.reset(); - checkZippedCsvResult("nmk-constraints-result", "/results/nmk-constraints-result-en.csv", "en"); + checkZippedCsvResult("nmk-constraints-result", "/results/nmk-constraints-result-en.csv", "en", SubjectLimitViolationEntity.Fields.subjectId, null); /* * SELECT * assert result exists @@ -998,11 +1000,20 @@ private void checkAllZippedCsvResults() throws Exception { * join contingency_limit_violation * join contingency_entity_contingency_elements */ - assertRequestsCount(4, 0, 0, 0); + assertRequestsCount(5, 0, 0, 0); SQLStatementCountValidator.reset(); - checkZippedCsvResult("nmk-constraints-result", "/results/nmk-constraints-result-fr.csv", "fr"); - assertRequestsCount(4, 0, 0, 0); + checkZippedCsvResult("nmk-constraints-result", "/results/nmk-constraints-result-fr.csv", "fr", SubjectLimitViolationEntity.Fields.subjectId, null); + assertRequestsCount(5, 0, 0, 0); + } + + private void checkFiltersOnZippedCsvResults() throws Exception { + List filterN = List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.EQUALS, new String[]{"CURRENT"}, AbstractLimitViolationEntity.Fields.limitType)); + List filterNmK = List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.EQUALS, new String[]{"CURRENT"}, SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + AbstractLimitViolationEntity.Fields.limitType)); + + checkZippedCsvResult("n-result", "/results/n-result-with-filter-en.csv", "en", AbstractLimitViolationEntity.Fields.subjectLimitViolation + SpecificationUtils.FIELD_SEPARATOR + SubjectLimitViolationEntity.Fields.subjectId, filterN); + checkZippedCsvResult("nmk-contingencies-result", "/results/nmk-contingencies-result-with-filter-en.csv", "en", ContingencyEntity.Fields.contingencyId, filterNmK); + checkZippedCsvResult("nmk-constraints-result", "/results/nmk-constraints-result-with-filter-en.csv", "en", SubjectLimitViolationEntity.Fields.subjectId, filterNmK); } private void checkCsvResultFromBytes(String expectedCsvResource, byte[] resultAsByteArray) throws Exception { @@ -1031,15 +1042,20 @@ private void checkCsvResultFromBytes(String expectedCsvResource, byte[] resultAs } } - private void checkZippedCsvResult(String resultType, String expectedCsvResource, String lang) throws Exception { + private void checkZippedCsvResult(String resultType, String expectedCsvResource, String lang, String sortField, List filter) throws Exception { CsvTranslationDTO csvTranslationDTO = CsvTranslationDTO.builder() .headers(getCsvHeaderFromResource(expectedCsvResource, lang)) .enumValueTranslations("en".equalsIgnoreCase(lang) ? ENUM_TRANSLATIONS_EN : ENUM_TRANSLATIONS_FR) .language(lang) .build(); + String jsonFilter = new ObjectMapper().writeValueAsString(filter); + String encodedFilter = URLEncoder.encode(jsonFilter, StandardCharsets.UTF_8); + // get csv file as binary (zip) byte[] resultAsByteArray = mockMvc.perform(post("/" + VERSION + "/results/" + RESULT_UUID + "/" + resultType + "/csv") + .param("sort", sortField) + .param("filters", encodedFilter) .contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(csvTranslationDTO))) .andExpectAll( diff --git a/src/test/resources/results/n-result-with-filter-en.csv b/src/test/resources/results/n-result-with-filter-en.csv new file mode 100644 index 00000000..3046c404 --- /dev/null +++ b/src/test/resources/results/n-result-with-filter-en.csv @@ -0,0 +1,3 @@ +Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 diff --git a/src/test/resources/results/nmk-constraints-result-en.csv b/src/test/resources/results/nmk-constraints-result-en.csv index 812f5864..5937d594 100644 --- a/src/test/resources/results/nmk-constraints-result-en.csv +++ b/src/test/resources/results/nmk-constraints-result-en.csv @@ -9,6 +9,9 @@ l3,l8,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 l3,l9,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 l3,la,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 l3,lb,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l6,f1,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 +l6,f2,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 +l6,f3,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 vl1,l1,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, vl1,l2,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, vl1,l3,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, @@ -19,9 +22,6 @@ vl1,l8,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, vl1,l9,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, vl1,la,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, vl1,lb,Converged,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, -l6,f1,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 -l6,f2,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 -l6,f3,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 vl7,f1,Failed,High voltage,vl7,vl7_name,,,0,,,400,,410, vl7,f2,Failed,High voltage,vl7,vl7_name,,,0,,,400,,410, vl7,f3,Failed,High voltage,vl7,vl7_name,,,0,,,400,,410, diff --git a/src/test/resources/results/nmk-constraints-result-fr.csv b/src/test/resources/results/nmk-constraints-result-fr.csv index 4ba1c9fb..cbd34dc6 100644 --- a/src/test/resources/results/nmk-constraints-result-fr.csv +++ b/src/test/resources/results/nmk-constraints-result-fr.csv @@ -9,6 +9,9 @@ l3;l8;Convergence;Intensité;;l3_name;110;;;;;10;;11;Côté 1 l3;l9;Convergence;Intensité;;l3_name;110;;;;;10;;11;Côté 1 l3;la;Convergence;Intensité;;l3_name;110;;;;;10;;11;Côté 1 l3;lb;Convergence;Intensité;;l3_name;110;;;;;10;;11;Côté 1 +l6;f1;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 +l6;f2;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 +l6;f3;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 vl1;l1;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; vl1;l2;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; vl1;l3;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; @@ -19,9 +22,6 @@ vl1;l8;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; vl1;l9;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; vl1;la;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; vl1;lb;Convergence;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; -l6;f1;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 -l6;f2;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 -l6;f3;Echec;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 vl7;f1;Echec;Tension haute;vl7;vl7_name;;;0;;;400;;410; vl7;f2;Echec;Tension haute;vl7;vl7_name;;;0;;;400;;410; vl7;f3;Echec;Tension haute;vl7;vl7_name;;;0;;;400;;410; diff --git a/src/test/resources/results/nmk-constraints-result-with-filter-en.csv b/src/test/resources/results/nmk-constraints-result-with-filter-en.csv new file mode 100644 index 00000000..1495d5ab --- /dev/null +++ b/src/test/resources/results/nmk-constraints-result-with-filter-en.csv @@ -0,0 +1,14 @@ +Equipment,Contingency,Status,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +l3,l1,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l2,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l3,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l4,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l6,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l7,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l8,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,l9,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,la,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,lb,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 +l6,f1,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 +l6,f2,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 +l6,f3,Failed,Current,,l6_name,110,,1200,,,10,,11,Side 1 diff --git a/src/test/resources/results/nmk-contingencies-result-en.csv b/src/test/resources/results/nmk-contingencies-result-en.csv index f68b5f24..890d6bc1 100644 --- a/src/test/resources/results/nmk-contingencies-result-en.csv +++ b/src/test/resources/results/nmk-contingencies-result-en.csv @@ -1,4 +1,10 @@ Contingency,Status,Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +f1,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +f1,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, +f2,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +f2,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, +f3,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +f3,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, l1,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 l1,Converged,vl1,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, l2,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 @@ -19,9 +25,3 @@ la,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 la,Converged,vl1,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, lb,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 lb,Converged,vl1,High voltage,"vl1 (VLGEN_0, VLLOAD_0)",IST,,,0,,,400,,410, -f1,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 -f1,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, -f2,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 -f2,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, -f3,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 -f3,Failed,vl7,High voltage,vl7,vl7_name,,,0,,,400,,410, diff --git a/src/test/resources/results/nmk-contingencies-result-fr.csv b/src/test/resources/results/nmk-contingencies-result-fr.csv index d75cda30..5bb792e7 100644 --- a/src/test/resources/results/nmk-contingencies-result-fr.csv +++ b/src/test/resources/results/nmk-contingencies-result-fr.csv @@ -1,4 +1,10 @@ Aléa;Statut;Ouvrage;Type de contrainte;Noeud électrique;Nom de la limite;Charge (% limite);Charge (% IST);Tempo effective;Tempo imminente;Nom de la limite suivante;Limite (A ou kV);IST (A);Valeur calculée (A ou kV);Côté +f1;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 +f1;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; +f2;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 +f2;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; +f3;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 +f3;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; l1;Convergence;l3;Intensité;;l3_name;110;;;;;10;;11;Côté 1 l1;Convergence;vl1;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; l2;Convergence;l3;Intensité;;l3_name;110;;;;;10;;11;Côté 1 @@ -19,9 +25,3 @@ la;Convergence;l3;Intensité;;l3_name;110;;;;;10;;11;Côté 1 la;Convergence;vl1;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; lb;Convergence;l3;Intensité;;l3_name;110;;;;;10;;11;Côté 1 lb;Convergence;vl1;Tension haute;vl1 (VLGEN_0, VLLOAD_0);IST;;;0;;;400;;410; -f1;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 -f1;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; -f2;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 -f2;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; -f3;Echec;l6;Intensité;;l6_name;110;;1200;;;10;;11;Côté 1 -f3;Echec;vl7;Tension haute;vl7;vl7_name;;;0;;;400;;410; diff --git a/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv b/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv new file mode 100644 index 00000000..174559f8 --- /dev/null +++ b/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv @@ -0,0 +1,14 @@ +Contingency,Status,Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +f1,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +f2,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +f3,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 +l1,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l2,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l3,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l4,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l6,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l7,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l8,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +l9,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +la,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 +lb,Converged,l3,Current,,l3_name,110,,,,,10,,11,Side 1 From cfc80152296011883f2f4d13897e401eed05a772 Mon Sep 17 00:00:00 2001 From: Caroline Jeandat Date: Tue, 27 Jan 2026 09:52:09 +0100 Subject: [PATCH 4/5] add BOM to test csv files --- src/test/resources/results/n-result-with-filter-en.csv | 2 +- .../resources/results/nmk-constraints-result-with-filter-en.csv | 2 +- .../results/nmk-contingencies-result-with-filter-en.csv | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/resources/results/n-result-with-filter-en.csv b/src/test/resources/results/n-result-with-filter-en.csv index 3046c404..a2dd6086 100644 --- a/src/test/resources/results/n-result-with-filter-en.csv +++ b/src/test/resources/results/n-result-with-filter-en.csv @@ -1,3 +1,3 @@ -Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side l3,Current,,l3_name,110,,,,,10,,11,Side 1 l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 diff --git a/src/test/resources/results/nmk-constraints-result-with-filter-en.csv b/src/test/resources/results/nmk-constraints-result-with-filter-en.csv index 1495d5ab..79af21b7 100644 --- a/src/test/resources/results/nmk-constraints-result-with-filter-en.csv +++ b/src/test/resources/results/nmk-constraints-result-with-filter-en.csv @@ -1,4 +1,4 @@ -Equipment,Contingency,Status,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +Equipment,Contingency,Status,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side l3,l1,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 l3,l2,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 l3,l3,Converged,Current,,l3_name,110,,,,,10,,11,Side 1 diff --git a/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv b/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv index 174559f8..2b963e1c 100644 --- a/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv +++ b/src/test/resources/results/nmk-contingencies-result-with-filter-en.csv @@ -1,4 +1,4 @@ -Contingency,Status,Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side +Contingency,Status,Equipment,Violation type,Bus,Limit name,Load (% limit),Load (% PATL),Actual overload,Upcoming overload,Next limit name,Limit (A or kV),PATL (A),Calculated value (A or kV),Side f1,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 f2,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 f3,Failed,l6,Current,,l6_name,110,,1200,,,10,,11,Side 1 From 28b16a71fedec2f26e9913457bda1239deaabad8 Mon Sep 17 00:00:00 2001 From: Caroline Jeandat Date: Tue, 27 Jan 2026 14:36:32 +0100 Subject: [PATCH 5/5] make methods private --- .../SecurityAnalysisResultService.java | 10 ++-- .../FindPreContingencyLimitViolationTest.java | 9 +++- .../FindSubjectLimitViolationsTest.java | 46 ++++++++++--------- 3 files changed, 36 insertions(+), 29 deletions(-) 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 647cf937..49d0b356 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -130,8 +130,7 @@ public SecurityAnalysisResultService(SecurityAnalysisResultRepository securityAn this.self = self; } - @Transactional(readOnly = true) - public List findNResult(UUID resultUuid, List resourceFilters, Sort sort) { + private List findNResult(UUID resultUuid, List resourceFilters, Sort sort) { assertResultExists(resultUuid); assertPreContingenciesSortAllowed(sort); @@ -145,7 +144,7 @@ public List findNResult(UUID resultUuid, @Transactional(readOnly = true) public List findNResult(UUID resultUuid, UUID networkUuid, String variantId, String stringFilters, String stringGlobalFilters, Sort sort) { List resourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterN(networkUuid, variantId, globalFilter)); - return self.findNResult(resultUuid, resourceFilters, sort); + return findNResult(resultUuid, resourceFilters, sort); } @Transactional(readOnly = true) @@ -188,7 +187,7 @@ public Page findNmKConstraintsResultPaged(UUID r assertResultExists(resultUuid); List allResourceFilters = getAllResourceFilters(stringFilters, stringGlobalFilters, globalFilter -> filterService.getResourceFilterSubjectLimitViolations(networkUuid, variantId, globalFilter)); - Page subjectLimitViolationsPage = self.findSubjectLimitViolationsPage(resultUuid, allResourceFilters, pageable); + Page subjectLimitViolationsPage = findSubjectLimitViolationsPage(resultUuid, allResourceFilters, pageable); return subjectLimitViolationsPage.map(SubjectLimitViolationResultDTO::toDto); } @@ -340,8 +339,7 @@ public Page findContingenciesPage(UUID resultUuid, List findSubjectLimitViolationsPage(UUID resultUuid, List resourceFilters, Pageable pageable) { + private Page findSubjectLimitViolationsPage(UUID resultUuid, List resourceFilters, Pageable pageable) { Objects.requireNonNull(resultUuid); assertNmKSubjectLimitViolationsSortAllowed(pageable.getSort()); Pageable modifiedPageable = addDefaultSortAndRemoveChildrenSorting(pageable, SubjectLimitViolationEntity.Fields.id); diff --git a/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java b/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java index 5943feea..2bd51ce5 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindPreContingencyLimitViolationTest.java @@ -6,6 +6,8 @@ */ package org.gridsuite.securityanalysis.server; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.ThreeSides; import org.gridsuite.computation.dto.ResourceFilterDTO; @@ -49,6 +51,9 @@ class FindPreContingencyLimitViolationTest { @Autowired private SecurityAnalysisResultService securityAnalysisResultService; + @Autowired + private ObjectMapper mapper; + @BeforeAll void setUp() { // network store service mocking @@ -70,9 +75,9 @@ void tearDown() { "provideEachColumnFilter", "provideChildFilterWithTolerance" }) - void findFilteredPrecontingencyLimitViolationResultsTest(List filters, Sort sort, List expectedResult, Integer expectedSelectCount) { + void findFilteredPrecontingencyLimitViolationResultsTest(List filters, Sort sort, List expectedResult, Integer expectedSelectCount) throws JsonProcessingException { reset(); - List preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), filters, sort); + List preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), null, null, mapper.writeValueAsString(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 d7484884..a2d7e3f8 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/FindSubjectLimitViolationsTest.java @@ -6,6 +6,8 @@ */ package org.gridsuite.securityanalysis.server; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.ThreeSides; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; @@ -60,6 +62,9 @@ class FindSubjectLimitViolationsTest { @Autowired private SecurityAnalysisResultService securityAnalysisResultService; + @Autowired + private ObjectMapper mapper; + @BeforeAll void setUp() { // network store service mocking @@ -81,25 +86,24 @@ void tearDown() { "provideChildSorting", "provideEachColumnFilter" }) - void findFilteredSubjectLimitViolationResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) { + void findFilteredSubjectLimitViolationResultsTest(List filters, Pageable pageable, List expectedResult, Integer expectedSelectCount) throws JsonProcessingException { reset(); - Page subjectLimitViolationPage = securityAnalysisResultService.findSubjectLimitViolationsPage(resultEntity.getId(), filters, pageable); + Page subjectLimitViolationPage = securityAnalysisResultService.findNmKConstraintsResultPaged(resultEntity.getId(), null, null, mapper.writeValueAsString(filters), null, pageable); // assert subject ids to check parent filters assertThat(subjectLimitViolationPage.getContent()).extracting("subjectId").containsExactlyElementsOf(expectedResult.stream().map(SubjectLimitViolationResultDTO::getSubjectId).toList()); assertThat(subjectLimitViolationPage.getContent().stream() - .map(SubjectLimitViolationResultDTO::toDto).map(lm -> lm.getContingencies().stream().map(c -> c.getLimitViolation().getLocationId()).toList())) + .map(lm -> lm.getContingencies().stream().map(c -> c.getLimitViolation().getLocationId()).toList())) .containsExactlyElementsOf(expectedResult.stream().map(slm -> slm.getContingencies().stream().map(FindSubjectLimitViolationsTest::getContingencyLimitViolationDTOLocationId).toList()).toList()); // assert limit violation contingency ids to check nested filters assertThat(subjectLimitViolationPage.getContent().stream() - .map(SubjectLimitViolationResultDTO::toDto) .map(lm -> lm.getContingencies().stream().map(c -> c.getContingency().getContingencyId()).toList())) .containsExactlyElementsOf(expectedResult.stream().map(c -> c.getContingencies().stream().map(FindSubjectLimitViolationsTest::getContingencyLimitViolationDTOContingencyId).toList()).toList()); // select count check to prevent potential n+1 problems - // 1 -> parent UUIDs ; empty parents, no count or children request - // 4 -> parent UUIDs + parents + children + contingencyElements ; no count (number of element < page size) - // 5 -> parent UUIDs + count + parents + children + contingencyElements + // 2 -> assertResultExists ; parent UUIDs ; empty parents, no count or children request + // 5 -> assertResultExists ; parent UUIDs + parents + children + contingencyElements ; no count (number of element < page size) + // 6 -> assertResultExists ; parent UUIDs + count + parents + children + contingencyElements assertSelectCount(expectedSelectCount); } @@ -109,27 +113,27 @@ 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.findNmKConstraintsResultPaged(resultEntity.getId(), null, null, mapper.writeValueAsString(filters), null, pageable)); assertEquals(expectedException.getMessage(), exception.getMessage()); } private static Stream providePageableAndSortOnly() { return Stream.of( - Arguments.of(List.of(), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 4), - Arguments.of(List.of(), PageRequest.of(0, 30, Sort.by(Sort.Direction.DESC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId).reversed()).toList(), 4), - Arguments.of(List.of(), PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList().subList(0, 2), 5), - Arguments.of(List.of(), PageRequest.of(1, 2, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList().subList(2, Math.min(RESULT_CONSTRAINTS.size(), 4)), 5) + Arguments.of(List.of(), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 5), + Arguments.of(List.of(), PageRequest.of(0, 30, Sort.by(Sort.Direction.DESC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId).reversed()).toList(), 5), + Arguments.of(List.of(), PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList().subList(0, 2), 6), + Arguments.of(List.of(), PageRequest.of(1, 2, Sort.by(Sort.Direction.ASC, "subjectId")), RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList().subList(2, Math.min(RESULT_CONSTRAINTS.size(), 4)), 6) ); } private static Stream provideParentFilter() { return Stream.of( Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.CONTAINS, "3", SubjectLimitViolationEntity.Fields.subjectId)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().contains("3")).toList(), 4), + RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().contains("3")).toList(), 5), Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.STARTS_WITH, "l", SubjectLimitViolationEntity.Fields.subjectId)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().startsWith("l")).toList(), 4), + RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().startsWith("l")).toList(), 5), Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.STARTS_WITH, "3", SubjectLimitViolationEntity.Fields.subjectId)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().startsWith("3")).toList(), 1) + RESULT_CONSTRAINTS.stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).filter(c -> c.getSubjectId().startsWith("3")).toList(), 2) ); } @@ -138,11 +142,11 @@ private static Stream provideChildFilter() { Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.CONTAINS, "2", SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + ContingencyLimitViolationEntity.Fields.contingency + SpecificationUtils.FIELD_SEPARATOR + ContingencyEntity.Fields.contingencyId)), PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "subjectId")), getResultConstraintsWithNestedFilter(c -> c.getContingency().getContingencyId().contains("2")) .stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList() - .subList(0, 2), 5), // find 1st page of size 2 of contingencies, filtered by SubjectId + .subList(0, 2), 6), // find 1st page of size 2 of contingencies, filtered by SubjectId Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.STARTS_WITH, "CURRENT", SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + AbstractLimitViolationEntity.Fields.limitType)), PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "subjectId")), getResultConstraintsWithNestedFilter(c -> c.getLimitViolation().getLimitType().equals(LimitViolationType.CURRENT)) .stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList() - .subList(0, 2), 5) + .subList(0, 2), 6) ); } @@ -201,17 +205,17 @@ private static Arguments buildArgumentsForChildrenSorting(Sort childrenSort, Com getResultConstraintsSorted( childrenComparator, Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)) - .subList(0, 4), 5); + .subList(0, 4), 6); } private static Stream provideEachColumnFilter() { return Stream.of( Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.CONTAINS, "CO", SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + ContingencyLimitViolationEntity.Fields.contingency + SpecificationUtils.FIELD_SEPARATOR + ContingencyEntity.Fields.status)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - getResultConstraintsWithNestedFilter(c -> c.getContingency().getStatus().contains("CO")).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 4), + getResultConstraintsWithNestedFilter(c -> c.getContingency().getStatus().contains("CO")).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 5), Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.EQUALS, "ONE", SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + AbstractLimitViolationEntity.Fields.side)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - getResultConstraintsWithNestedFilter(c -> c.getLimitViolation().getSide() != null && c.getLimitViolation().getSide().equals(ThreeSides.ONE)).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 4), + getResultConstraintsWithNestedFilter(c -> c.getLimitViolation().getSide() != null && c.getLimitViolation().getSide().equals(ThreeSides.ONE)).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 5), Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.TEXT, ResourceFilterDTO.Type.EQUALS, "l6_name", SubjectLimitViolationEntity.Fields.contingencyLimitViolations + SpecificationUtils.FIELD_SEPARATOR + AbstractLimitViolationEntity.Fields.limitName)), PageRequest.of(0, 30, Sort.by(Sort.Direction.ASC, "subjectId")), - getResultConstraintsWithNestedFilter(c -> c.getLimitViolation().getLimitName().equals("l6_name")).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 4) + getResultConstraintsWithNestedFilter(c -> c.getLimitViolation().getLimitName().equals("l6_name")).stream().sorted(Comparator.comparing(SubjectLimitViolationResultDTO::getSubjectId)).toList(), 5) ); }