diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index 4184e694..7b415500 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -67,7 +67,6 @@ public SecurityAnalysisController(SecurityAnalysisService securityAnalysisServic schema = @Schema(implementation = SecurityAnalysisResult.class))})}) public ResponseEntity run(@Parameter(description = "Network UUID") @PathVariable("networkUuid") UUID networkUuid, @Parameter(description = "Variant Id") @RequestParam(name = "variantId", required = false) String variantId, - @Parameter(description = "Contingency list name") @RequestParam(name = "contingencyListName", required = false) List contigencyListNames, @Parameter(description = "Provider") @RequestParam(name = "provider", required = false) String provider, @Parameter(description = "reportUuid") @RequestParam(name = "reportUuid", required = false) UUID reportUuid, @Parameter(description = "reporterId") @RequestParam(name = "reporterId", required = false) String reporterId, @@ -79,14 +78,14 @@ public ResponseEntity run(@Parameter(description = "Netw securityAnalysisParametersService.createRunContext( networkUuid, variantId, - new RunContextParametersInfos(contigencyListNames, parametersUuid, loadFlowParametersUuid), + new RunContextParametersInfos(parametersUuid, loadFlowParametersUuid), null, new ReportInfos(reportUuid, reporterId, reportType), userId)); return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result); } - @PostMapping(value = "/networks/{networkUuid}/run-and-save", produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @PostMapping(value = "/networks/{networkUuid}/run-and-save", produces = APPLICATION_JSON_VALUE) @Operation(summary = "Run a security analysis on a network and save results in the database") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis has been performed and results have been saved to database", @@ -94,7 +93,6 @@ public ResponseEntity run(@Parameter(description = "Netw schema = @Schema(implementation = SecurityAnalysisResult.class))})}) public ResponseEntity runAndSave(@Parameter(description = "Network UUID") @PathVariable("networkUuid") UUID networkUuid, @Parameter(description = "Variant Id") @RequestParam(name = "variantId", required = false) String variantId, - @Parameter(description = "Contingency list name") @RequestParam(name = "contingencyListName", required = false) List contigencyListNames, @Parameter(description = "Result receiver") @RequestParam(name = "receiver", required = false) String receiver, @Parameter(description = "reportUuid") @RequestParam(name = "reportUuid", required = false) UUID reportUuid, @Parameter(description = "reporterId") @RequestParam(name = "reporterId", required = false) String reporterId, @@ -106,7 +104,7 @@ public ResponseEntity runAndSave(@Parameter(description = "Network UUID") securityAnalysisParametersService.createRunContext( networkUuid, variantId, - new RunContextParametersInfos(contigencyListNames, parametersUuid, loadFlowParametersUuid), + new RunContextParametersInfos(parametersUuid, loadFlowParametersUuid), receiver, new ReportInfos(reportUuid, reporterId, reportType), userId diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersController.java index 57a5a924..da7c91df 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersController.java @@ -11,8 +11,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.gridsuite.securityanalysis.server.dto.LimitReductionsByVoltageLevel; -import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersValues; +import org.gridsuite.securityanalysis.server.dto.parameters.LimitReductionsByVoltageLevel; +import org.gridsuite.securityanalysis.server.dto.parameters.SecurityAnalysisParametersValues; import org.gridsuite.securityanalysis.server.service.SecurityAnalysisParametersService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -21,6 +21,8 @@ import java.util.List; import java.util.UUID; +import static org.gridsuite.computation.service.NotificationService.HEADER_USER_ID; + /** * @author Abdelsalem Hedhili */ @@ -57,8 +59,9 @@ public ResponseEntity createDefaultParameters() { @ApiResponse(responseCode = "200", description = "parameters were duplicated"), @ApiResponse(responseCode = "404", description = "source parameters were not found")}) public ResponseEntity duplicateParameters( - @Parameter(description = "source parameters UUID") @RequestParam(name = "duplicateFrom") UUID sourceParametersUuid) { - return parametersService.duplicateParameters(sourceParametersUuid).map(duplicatedParametersUuid -> ResponseEntity.ok() + @Parameter(description = "source parameters UUID") @RequestParam(name = "duplicateFrom") UUID sourceParametersUuid, + @RequestHeader(HEADER_USER_ID) String userId) { + return parametersService.duplicateParameters(sourceParametersUuid, userId).map(duplicatedParametersUuid -> ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(duplicatedParametersUuid)) .orElse(ResponseEntity.notFound().build()); @@ -70,8 +73,9 @@ public ResponseEntity duplicateParameters( @ApiResponse(responseCode = "200", description = "parameters were returned"), @ApiResponse(responseCode = "404", description = "parameters were not found")}) public ResponseEntity getParameters( - @Parameter(description = "parameters UUID") @PathVariable(value = "uuid") UUID parametersUuid) { - return parametersService.getParameters(parametersUuid) + @Parameter(description = "parameters UUID") @PathVariable(value = "uuid") UUID parametersUuid, + @RequestHeader(HEADER_USER_ID) String userId) { + return parametersService.getParameters(parametersUuid, userId) .map(parametersValues -> ResponseEntity.ok().body(parametersValues)) .orElse(ResponseEntity.notFound().build()); } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/ElementAttributes.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/ElementAttributes.java new file mode 100644 index 00000000..580a0107 --- /dev/null +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/ElementAttributes.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; + +/** + * partial type from ElementAttributes (Directory-server) + * @author Caroline Jeandat {@literal } + */ +@AllArgsConstructor +@NoArgsConstructor +public class ElementAttributes { + @Getter + private UUID elementUuid; + @Setter + @Getter + private String elementName; +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/RunContextParametersInfos.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/RunContextParametersInfos.java index 75c70925..5061ac6b 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/dto/RunContextParametersInfos.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/RunContextParametersInfos.java @@ -10,7 +10,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import java.util.List; import java.util.UUID; /** @@ -20,8 +19,6 @@ @Getter @Schema(description = "to help create a securityAnalysisRunContext") public class RunContextParametersInfos { - private List contingencyListNames; - private UUID securityAnalysisParametersUuid; private UUID loadFlowParametersUuid; diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersDTO.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersDTO.java index 1e864d7d..24b68301 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersDTO.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersDTO.java @@ -10,9 +10,11 @@ import lombok.Builder; import java.util.List; +import java.util.UUID; @Builder public record SecurityAnalysisParametersDTO( SecurityAnalysisParameters securityAnalysisParameters, + List contingencyListUuids, List> limitReductions ) { } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/ContingencyListsInfos.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/ContingencyListsInfos.java new file mode 100644 index 00000000..ac6188c8 --- /dev/null +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/ContingencyListsInfos.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.dto.parameters; +import lombok.*; + +import java.util.List; + +/** + * @author Caroline Jeandat {@literal } + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class ContingencyListsInfos { + private List contingencyLists; + private String description; + private boolean activated; +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/IdNameInfos.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/IdNameInfos.java new file mode 100644 index 00000000..77fa5180 --- /dev/null +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/IdNameInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.dto.parameters; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; + +/** + * @author Caroline Jeandat {@literal } + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class IdNameInfos { + private UUID id; + private String name; +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/LimitReductionsByVoltageLevel.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/LimitReductionsByVoltageLevel.java similarity index 94% rename from src/main/java/org/gridsuite/securityanalysis/server/dto/LimitReductionsByVoltageLevel.java rename to src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/LimitReductionsByVoltageLevel.java index b80ff880..abcca7ae 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/dto/LimitReductionsByVoltageLevel.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/LimitReductionsByVoltageLevel.java @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.gridsuite.securityanalysis.server.dto; +package org.gridsuite.securityanalysis.server.dto.parameters; import lombok.*; diff --git a/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersValues.java b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/SecurityAnalysisParametersValues.java similarity index 93% rename from src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersValues.java rename to src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/SecurityAnalysisParametersValues.java index 99c2892b..3105c361 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersValues.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/dto/parameters/SecurityAnalysisParametersValues.java @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.gridsuite.securityanalysis.server.dto; +package org.gridsuite.securityanalysis.server.dto.parameters; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; @@ -36,6 +36,8 @@ public class SecurityAnalysisParametersValues { private double flowProportionalThreshold; + private List contingencyListsInfos; + private List limitReductions; public SecurityAnalysisParametersEntity toEntity() { diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java index 2c8f5656..d9bddad7 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java @@ -43,6 +43,7 @@ public ContingencyEntity(String contingencyId, String status, List} + */ +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "parameters_contingency_lists", indexes = {@Index(name = "idx_parameters_contingency_lists_security_analysis_parameters_id", columnList = "security_analysis_parameters_id")}) +public class ParametersContingencyListEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(name = "id") + private UUID id; + + @ElementCollection + @CollectionTable( + name = "parameters_contingency_lists_contingency_list", + joinColumns = @JoinColumn(name = "parameters_contingency_lists_id"), + foreignKey = @ForeignKey(name = "parameters_contingency_lists_id_fk") + ) + @Column(name = "contingency_list_id") + private List contingencyListIds; + + @Column(name = "description") + private String description; + + @Column(name = "activated") + private boolean activated; + + @ManyToOne + @JoinColumn(name = "security_analysis_parameters_id", foreignKey = @ForeignKey(name = "security_analysis_parameters_id_fk")) + private SecurityAnalysisParametersEntity securityAnalysisParameters; + + public ParametersContingencyListEntity(List contingencyListIds, String description, boolean activated) { + this.contingencyListIds = contingencyListIds; + this.description = description; + this.activated = activated; + } +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisParametersEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisParametersEntity.java index 13aed58d..57e0c224 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisParametersEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisParametersEntity.java @@ -8,7 +8,9 @@ import jakarta.persistence.*; import lombok.*; -import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersValues; +import org.gridsuite.securityanalysis.server.dto.parameters.IdNameInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.ContingencyListsInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.SecurityAnalysisParametersValues; import org.springframework.lang.Nullable; import java.util.ArrayList; @@ -55,11 +57,25 @@ public SecurityAnalysisParametersEntity(SecurityAnalysisParametersValues securit @Column(name = "flowProportionalThreshold") private double flowProportionalThreshold; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "securityAnalysisParameters") + @OrderColumn(name = "index") + private List contingencyLists; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "security_analysis_parameters_id", foreignKey = @ForeignKey(name = "securityAnalysisParametersEntity_limitReductions_fk")) @OrderColumn(name = "index") private List limitReductions; + public List getActivatedContingencyListUuids() { + if (contingencyLists == null) { + return List.of(); + } + return this.contingencyLists.stream() + .filter(ParametersContingencyListEntity::isActivated) + .flatMap(contingencyList -> contingencyList.getContingencyListIds().stream()) + .toList(); + } + public List> toLimitReductionsValues() { return this.limitReductions.stream().map(LimitReductionEntity::getReductions).map(ArrayList::new).collect(Collectors.toList()); } @@ -75,9 +91,37 @@ private void assignAttributes(SecurityAnalysisParametersValues securityAnalysisP this.highVoltageProportionalThreshold = securityAnalysisParametersValues.getHighVoltageProportionalThreshold(); this.lowVoltageAbsoluteThreshold = securityAnalysisParametersValues.getLowVoltageAbsoluteThreshold(); this.lowVoltageProportionalThreshold = securityAnalysisParametersValues.getLowVoltageProportionalThreshold(); + assignContingencyLists(securityAnalysisParametersValues.getContingencyListsInfos()); assignLimitReductions(securityAnalysisParametersValues.getLimitReductionsValues()); } + private void assignContingencyLists(List contingencyListsInfos) { + if (contingencyListsInfos == null) { + return; + } + + List entities = contingencyListsInfos.stream() + .map(listsInfos -> { + List contingencyListIds = listsInfos.getContingencyLists().stream() + .map(IdNameInfos::getId) + .toList(); + ParametersContingencyListEntity entity = new ParametersContingencyListEntity( + contingencyListIds, + listsInfos.getDescription(), + listsInfos.isActivated() + ); + entity.setSecurityAnalysisParameters(this); + return entity; + }) + .toList(); + if (contingencyLists == null) { + contingencyLists = entities; + } else { + contingencyLists.clear(); + contingencyLists.addAll(entities); + } + } + private void assignLimitReductions(@Nullable List> values) { if (values == null) { return; @@ -95,4 +139,3 @@ public void updateProvider(String provider) { this.provider = provider; } } - diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/ActionsService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/ActionsService.java index 4e2a8a64..8e686f39 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/ActionsService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/ActionsService.java @@ -45,7 +45,7 @@ public ActionsService( this.restTemplate = restTemplate; } - public List getContingencyList(List ids, UUID networkUuid, String variantId) { + public List getContingencyList(List ids, UUID networkUuid, String variantId) { Objects.requireNonNull(ids); Objects.requireNonNull(networkUuid); if (ids.isEmpty()) { diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/DirectoryService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/DirectoryService.java new file mode 100644 index 00000000..ab3a438d --- /dev/null +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/DirectoryService.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.service; + +import org.gridsuite.securityanalysis.server.dto.ElementAttributes; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.util.*; +import java.util.stream.Collectors; + +import static org.gridsuite.computation.service.NotificationService.HEADER_USER_ID; + +/** + * @author Caroline Jeandat {@literal } + */ +@Service +public class DirectoryService { + static final String DIRECTORY_API_VERSION = "v1"; + private static final String DELIMITER = "/"; + private final String baseUri; + private final RestTemplate restTemplate; + + public DirectoryService( + @Value("${gridsuite.services.directory-server.base-uri:http://directory-server}") String baseUri, + RestTemplate restTemplate) { + this.baseUri = baseUri; + this.restTemplate = restTemplate; + } + + public Map getElementNames(List elementUuids, String userId) { + Objects.requireNonNull(elementUuids); + + if (elementUuids.isEmpty()) { + return Map.of(); + } + + URI path = UriComponentsBuilder + .fromPath(DELIMITER + DIRECTORY_API_VERSION + "/elements") + .queryParam("ids", elementUuids) + .queryParam("strictMode", "false") // to ignore non existing elements error + .build() + .toUri(); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HEADER_USER_ID, userId); + headers.setContentType(MediaType.APPLICATION_JSON); + + try { + List elementAttributes = + restTemplate.exchange( + baseUri + path, + HttpMethod.GET, + new HttpEntity<>(headers), + new ParameterizedTypeReference>() { } + ).getBody(); + + return elementAttributes == null ? Map.of() + : elementAttributes.stream().collect(Collectors.toMap(ElementAttributes::getElementUuid, ElementAttributes::getElementName)); + + } catch (HttpClientErrorException e) { + return Map.of(); + } + } +} diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/LimitReductionService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/LimitReductionService.java index 2d8a2f5d..44eca241 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/LimitReductionService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/LimitReductionService.java @@ -10,7 +10,7 @@ import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.Range; -import org.gridsuite.securityanalysis.server.dto.LimitReductionsByVoltageLevel; +import org.gridsuite.securityanalysis.server.dto.parameters.LimitReductionsByVoltageLevel; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersService.java index d2f16aef..fca6c761 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersService.java @@ -11,6 +11,10 @@ import org.gridsuite.computation.dto.ReportInfos; import lombok.NonNull; import org.gridsuite.securityanalysis.server.dto.*; +import org.gridsuite.securityanalysis.server.dto.parameters.ContingencyListsInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.IdNameInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.LimitReductionsByVoltageLevel; +import org.gridsuite.securityanalysis.server.dto.parameters.SecurityAnalysisParametersValues; import org.gridsuite.securityanalysis.server.entities.SecurityAnalysisParametersEntity; import org.gridsuite.securityanalysis.server.repositories.SecurityAnalysisParametersRepository; import org.springframework.beans.factory.annotation.Value; @@ -36,18 +40,22 @@ public class SecurityAnalysisParametersService { private final LimitReductionService limitReductionService; + private final DirectoryService directoryService; + private static final double DEFAULT_FLOW_PROPORTIONAL_THRESHOLD = 0.1; // meaning 10.0 % private static final double DEFAULT_LOW_VOLTAGE_PROPORTIONAL_THRESHOLD = 0.01; // meaning 1.0 % private static final double DEFAULT_HIGH_VOLTAGE_PROPORTIONAL_THRESHOLD = 0.01; // meaning 1.0 % private static final double DEFAULT_LOW_VOLTAGE_ABSOLUTE_THRESHOLD = 1.0; // 1.0 kV private static final double DEFAULT_HIGH_VOLTAGE_ABSOLUTE_THRESHOLD = 1.0; // 1.0 kV + private static final List DEFAULT_CONTINGENCY_LISTS_INFOS = new ArrayList<>(); public SecurityAnalysisParametersService(@NonNull SecurityAnalysisParametersRepository securityAnalysisParametersRepository, @NonNull LoadFlowService loadFlowService, - @Value("${security-analysis.default-provider}") String defaultProvider, @NonNull LimitReductionService limitReductionService) { + @Value("${security-analysis.default-provider}") String defaultProvider, @NonNull LimitReductionService limitReductionService, @NonNull DirectoryService directoryService) { this.securityAnalysisParametersRepository = Objects.requireNonNull(securityAnalysisParametersRepository); this.loadFlowService = loadFlowService; this.defaultProvider = defaultProvider; this.limitReductionService = limitReductionService; + this.directoryService = directoryService; } @Transactional(readOnly = true) @@ -69,7 +77,6 @@ public SecurityAnalysisRunContext createRunContext(UUID networkUuid, String vari return new SecurityAnalysisRunContext( networkUuid, variantId, - runContextParametersInfos.getContingencyListNames(), receiver, providerToUse, parameters, @@ -81,21 +88,27 @@ public SecurityAnalysisRunContext createRunContext(UUID networkUuid, String vari public SecurityAnalysisParametersDTO toSecurityAnalysisParameters(SecurityAnalysisParametersEntity entity) { SecurityAnalysisParameters securityAnalysisParameters = SecurityAnalysisParameters.load(); List> limitReductions = new ArrayList<>(); + List activatedContingencyListUuids = new ArrayList<>(); if (entity == null) { // the default values are overloaded securityAnalysisParameters.setIncreasedViolationsParameters(getIncreasedViolationsParameters(DEFAULT_FLOW_PROPORTIONAL_THRESHOLD, DEFAULT_LOW_VOLTAGE_PROPORTIONAL_THRESHOLD, DEFAULT_LOW_VOLTAGE_ABSOLUTE_THRESHOLD, DEFAULT_HIGH_VOLTAGE_PROPORTIONAL_THRESHOLD, DEFAULT_HIGH_VOLTAGE_ABSOLUTE_THRESHOLD)); } else { securityAnalysisParameters.setIncreasedViolationsParameters(getIncreasedViolationsParameters(entity.getFlowProportionalThreshold(), entity.getLowVoltageProportionalThreshold(), entity.getLowVoltageAbsoluteThreshold(), entity.getHighVoltageProportionalThreshold(), entity.getHighVoltageAbsoluteThreshold())); limitReductions = entity.toLimitReductionsValues(); + activatedContingencyListUuids = entity.getActivatedContingencyListUuids(); } if (limitReductions.isEmpty()) { limitReductions = limitReductionService.getDefaultValues(); } - return SecurityAnalysisParametersDTO.builder().securityAnalysisParameters(securityAnalysisParameters).limitReductions(limitReductions).build(); + return SecurityAnalysisParametersDTO.builder() + .securityAnalysisParameters(securityAnalysisParameters) + .contingencyListUuids(activatedContingencyListUuids) + .limitReductions(limitReductions) + .build(); } - public SecurityAnalysisParametersValues toSecurityAnalysisParametersValues(SecurityAnalysisParametersEntity entity) { + public SecurityAnalysisParametersValues toSecurityAnalysisParametersValues(SecurityAnalysisParametersEntity entity, String userId) { return SecurityAnalysisParametersValues.builder() .provider(entity.getProvider()) .flowProportionalThreshold(entity.getFlowProportionalThreshold()) @@ -103,10 +116,29 @@ public SecurityAnalysisParametersValues toSecurityAnalysisParametersValues(Secur .highVoltageProportionalThreshold(entity.getHighVoltageProportionalThreshold()) .lowVoltageAbsoluteThreshold(entity.getLowVoltageAbsoluteThreshold()) .lowVoltageProportionalThreshold(entity.getLowVoltageProportionalThreshold()) + .contingencyListsInfos(Optional.of( + entity.getContingencyLists().stream() + .map(c -> new ContingencyListsInfos( + contingenciesIdsToDTOs(c.getContingencyListIds(), userId), + c.getDescription(), + c.isActivated())) + .toList()) + .filter(list -> !list.isEmpty()) // if empty list return null + .orElse(null)) .limitReductions(getLimitReductionsForProvider(entity).orElse(null)) .build(); } + private List contingenciesIdsToDTOs(List contingenciesIds, String userId) { + if (contingenciesIds == null) { + return null; + } + Map contingenciesInfos = directoryService.getElementNames(contingenciesIds, userId); + return contingenciesIds.stream() + .map(id -> new IdNameInfos(id, contingenciesInfos.get(id))) + .toList(); + } + private Optional> getLimitReductionsForProvider(SecurityAnalysisParametersEntity entity) { // Only for some providers if (!limitReductionService.getProviders().contains(entity.getProvider())) { @@ -134,14 +166,15 @@ public SecurityAnalysisParametersValues getDefaultSecurityAnalysisParametersValu .highVoltageAbsoluteThreshold(DEFAULT_HIGH_VOLTAGE_ABSOLUTE_THRESHOLD) .highVoltageProportionalThreshold(DEFAULT_HIGH_VOLTAGE_PROPORTIONAL_THRESHOLD) .flowProportionalThreshold(DEFAULT_FLOW_PROPORTIONAL_THRESHOLD) + .contingencyListsInfos(DEFAULT_CONTINGENCY_LISTS_INFOS) .limitReductions(limitReductionService.createDefaultLimitReductions()) .build(); } @Transactional(readOnly = true) - public Optional getParameters(UUID parametersUuid) { + public Optional getParameters(UUID parametersUuid, String userId) { return securityAnalysisParametersRepository.findById(parametersUuid) - .map(this::toSecurityAnalysisParametersValues); + .map(entity -> toSecurityAnalysisParametersValues(entity, userId)); } public UUID createParameters(SecurityAnalysisParametersValues securityAnalysisParametersValues) { @@ -153,8 +186,8 @@ public UUID createDefaultParameters() { } @Transactional - public Optional duplicateParameters(UUID sourceParametersUuid) { - Optional securityAnalysisParametersValuesOptional = securityAnalysisParametersRepository.findById(sourceParametersUuid).map(this::toSecurityAnalysisParametersValues); + public Optional duplicateParameters(UUID sourceParametersUuid, String userId) { + Optional securityAnalysisParametersValuesOptional = securityAnalysisParametersRepository.findById(sourceParametersUuid).map(entity -> toSecurityAnalysisParametersValues(entity, userId)); return securityAnalysisParametersValuesOptional.map(parametersValues -> securityAnalysisParametersRepository.save(new SecurityAnalysisParametersEntity(parametersValues)).getId()); } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultContext.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultContext.java index 8843b37f..a7cc2289 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultContext.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultContext.java @@ -24,27 +24,16 @@ * @author Geoffroy Jamgotchian */ public class SecurityAnalysisResultContext extends AbstractResultContext { - public static final String CONTINGENCY_LIST_NAMES_HEADER = "contingencyListNames"; - public SecurityAnalysisResultContext(UUID resultUuid, SecurityAnalysisRunContext runContext) { super(resultUuid, runContext); } - private static List getHeaderList(MessageHeaders headers, String name) { - String header = (String) headers.get(name); - if (header == null || header.isEmpty()) { - return Collections.emptyList(); - } - return Arrays.asList(header.split(",")); - } - public static SecurityAnalysisResultContext fromMessage(Message message, ObjectMapper objectMapper) { Objects.requireNonNull(message); MessageHeaders headers = message.getHeaders(); UUID resultUuid = UUID.fromString(getNonNullHeader(headers, HEADER_RESULT_UUID)); UUID networkUuid = UUID.fromString(getNonNullHeader(headers, NETWORK_UUID_HEADER)); String variantId = (String) headers.get(VARIANT_ID_HEADER); - List contingencyListNames = getHeaderList(headers, CONTINGENCY_LIST_NAMES_HEADER); String receiver = (String) headers.get(HEADER_RECEIVER); String provider = (String) headers.get(HEADER_PROVIDER); String userId = (String) headers.get(HEADER_USER_ID); @@ -60,7 +49,6 @@ public static SecurityAnalysisResultContext fromMessage(Message message, SecurityAnalysisRunContext runContext = new SecurityAnalysisRunContext( networkUuid, variantId, - contingencyListNames, receiver, provider, parameters, @@ -69,10 +57,4 @@ public static SecurityAnalysisResultContext fromMessage(Message message, ); return new SecurityAnalysisResultContext(resultUuid, runContext); } - - @Override - protected Map getSpecificMsgHeaders(ObjectMapper ignoredObjectMapper) { - return Map.of( - CONTINGENCY_LIST_NAMES_HEADER, String.join(",", getRunContext().getContingencyListNames())); - } } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisRunContext.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisRunContext.java index d70bbf19..25fda4d3 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisRunContext.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisRunContext.java @@ -27,18 +27,15 @@ */ @Getter public class SecurityAnalysisRunContext extends AbstractComputationRunContext { - - private final List contingencyListNames; @Setter private List contingencies; - public SecurityAnalysisRunContext(UUID networkUuid, String variantId, List contingencyListNames, + public SecurityAnalysisRunContext(UUID networkUuid, String variantId, String receiver, String provider, SecurityAnalysisParametersDTO parameters, LoadFlowParametersValues loadFlowParametersValues, ReportInfos reportContext, String userId) { this( networkUuid, variantId, - contingencyListNames, receiver, provider, buildParameters(parameters, loadFlowParametersValues, provider), @@ -47,11 +44,10 @@ public SecurityAnalysisRunContext(UUID networkUuid, String variantId, List contingencyListNames, + public SecurityAnalysisRunContext(UUID networkUuid, String variantId, String receiver, String provider, SecurityAnalysisParametersDTO parameters, ReportInfos reportContext, String userId) { super(networkUuid, variantId, receiver, reportContext, userId, provider, parameters); - this.contingencyListNames = Objects.requireNonNull(contingencyListNames); } private static SecurityAnalysisParametersDTO buildParameters(SecurityAnalysisParametersDTO parameters, diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java index 04eff4bd..d5a91182 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java @@ -26,11 +26,10 @@ import com.powsybl.network.store.client.PreloadingStrategy; import com.powsybl.security.*; import com.powsybl.security.limitreduction.LimitReduction; -import com.powsybl.ws.commons.LogUtils; import org.gridsuite.computation.service.*; import org.gridsuite.securityanalysis.server.PropertyServerNameProvider; import org.gridsuite.securityanalysis.server.dto.ContingencyInfos; -import org.gridsuite.securityanalysis.server.dto.LimitReductionsByVoltageLevel; +import org.gridsuite.securityanalysis.server.dto.parameters.LimitReductionsByVoltageLevel; import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersDTO; import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisStatus; import org.gridsuite.securityanalysis.server.util.SecurityAnalysisRunnerSupplier; @@ -41,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -178,14 +178,19 @@ private LimitReduction createLimitReduction(IdentifiableCriterion voltageLevelCr @Override protected void preRun(SecurityAnalysisRunContext runContext) { - LOGGER.info("Run security analysis on contingency lists: {}", runContext.getContingencyListNames().stream().map(LogUtils::sanitizeParam).toList()); - - List contingencies = observer.observe("contingencies.fetch", runContext, - () -> - actionsService.getContingencyList(runContext.getContingencyListNames(), runContext.getNetworkUuid(), runContext.getVariantId()) - ); + if (runContext.getParameters().contingencyListUuids() != null) { + LOGGER.info("Run security analysis on contingency lists: {}", runContext.getParameters().contingencyListUuids()); + } - runContext.setContingencies(contingencies); + try { + List contingencies = observer.observe("contingencies.fetch", runContext, + () -> + actionsService.getContingencyList(runContext.getParameters().contingencyListUuids(), runContext.getNetworkUuid(), runContext.getVariantId()) + ); + runContext.setContingencies(contingencies); + } catch (IllegalArgumentException e) { + throw new InvalidParameterException("No contingency list found in parameters to run the analysis"); + } } @Override diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index a249a4ef..dda0a5f9 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -22,3 +22,5 @@ gridsuite: base-uri: http://localhost:5028 loadflow-server: base-uri: http://localhost:5008 + directory-server: + base-uri: http://localhost:5026 diff --git a/src/main/resources/db/changelog/changesets/changelog_20260218T115944Z.xml b/src/main/resources/db/changelog/changesets/changelog_20260218T115944Z.xml new file mode 100644 index 00000000..28a28cd9 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20260218T115944Z.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index eb9ac9bf..6d91d0ba 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -57,3 +57,6 @@ databaseChangeLog: - include: file: changesets/changelog_20260212T120615Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20260218T115944Z.xml + relativeToChangelogFile: true \ No newline at end of file diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java index efeb8f36..3bead74f 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java @@ -81,7 +81,6 @@ import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisService.COMPUTATION_TYPE; import static org.gridsuite.securityanalysis.server.util.CsvExportUtils.csvRowsToZippedCsv; import static org.gridsuite.securityanalysis.server.util.DatabaseQueryUtils.assertRequestsCount; -import static org.gridsuite.securityanalysis.server.util.TestUtils.assertLogMessage; import static org.gridsuite.securityanalysis.server.util.TestUtils.readLinesFromFilePath; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; @@ -109,11 +108,10 @@ class SecurityAnalysisControllerTest { private static final UUID REPORT_UUID = UUID.fromString("0c4de370-3e6a-4d72-b292-d355a97e0d53"); private static final UUID OTHER_RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5a"); private static final UUID LIST_FILTER_ID = UUID.fromString("762b72a8-8c0f-11ed-a1eb-0242ac120003"); + private static final String USER_ID = "testUserId"; private static final int TIMEOUT = 1000; - private static final String ERROR_MESSAGE = "Error message test"; - @Autowired private OutputDestination output; @@ -239,9 +237,9 @@ void tearDown() throws Exception { } private void simpleRunRequest(SecurityAnalysisParametersInfos lfParams) throws Exception { - MvcResult mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME_VARIANT + "&variantId=" + VARIANT_3_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) + MvcResult mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&variantId=" + VARIANT_3_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) .contentType(MediaType.APPLICATION_JSON) - .header(HEADER_USER_ID, "testUserId") + .header(HEADER_USER_ID, USER_ID) .content(mapper.writeValueAsString(lfParams))) .andExpectAll( status().isOk(), @@ -273,8 +271,8 @@ void runTest() throws Exception { String resultAsString; // run with specific variant - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME_VARIANT + "&variantId=" + VARIANT_3_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&variantId=" + VARIANT_3_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -286,8 +284,8 @@ void runTest() throws Exception { assertThat(RESULT_VARIANT, new MatcherJson<>(mapper, securityAnalysisResult)); // run with implicit initial variant - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -305,9 +303,8 @@ void runAndSaveTest() throws Exception { String resultAsString; SQLStatementCountValidator.reset(); - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME - + "&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -371,9 +368,8 @@ void testDeterministicResults() throws Exception { MvcResult mvcResult; String resultAsString; SQLStatementCountValidator.reset(); - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME - + "&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( content().contentType(MediaType.APPLICATION_JSON), @@ -587,30 +583,13 @@ private void verifyNmkContingrnciesResultLocationIds(List Assertions.assertThat(locationIds).hasSameElementsAs(Arrays.asList(null, "vl1 (VLGEN_0, VLLOAD_0)", "vl7")); } - @Test - void runWithTwoLists() throws Exception { - MvcResult mvcResult; - String resultAsString; - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + - "&contingencyListName=" + CONTINGENCY_LIST2_NAME + "&variantId=" + VARIANT_1_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .contentType(MediaType.APPLICATION_JSON) - .header(HEADER_USER_ID, "testUserId")) - .andExpectAll( - status().isOk(), - content().contentType(MediaType.APPLICATION_JSON) - ).andReturn(); - resultAsString = mvcResult.getResponse().getContentAsString(); - SecurityAnalysisResult securityAnalysisResult = mapper.readValue(resultAsString, SecurityAnalysisResult.class); - assertThat(RESULT, new MatcherJson<>(mapper, securityAnalysisResult)); - } - @Test void deleteResultsTest() throws Exception { MvcResult mvcResult; String resultAsString; - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -654,9 +633,8 @@ void testStatus() throws Exception { assertEquals(SecurityAnalysisStatus.NOT_DONE, securityAnalysisStatus); // running computation to create result - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME - + "&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -701,9 +679,8 @@ void stopTest() throws Exception { MvcResult mvcResult; String resultAsString; - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_STOP_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME - + "&receiver=me&variantId=" + VARIANT_TO_STOP_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_STOP_UUID + "/run-and-save?reportType=SecurityAnalysis&receiver=me&variantId=" + VARIANT_TO_STOP_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -722,7 +699,7 @@ void stopTest() throws Exception { countDownLatch.await(); mockMvc.perform(put("/" + VERSION + "/results/" + RESULT_UUID + "/stop" + "?receiver=me") - .header(HEADER_USER_ID, "testUserId")) + .header(HEADER_USER_ID, USER_ID)) .andExpect(status().isOk()); Message message = output.receive(TIMEOUT * 3, "sa.stopped"); @@ -735,7 +712,7 @@ void stopTest() throws Exception { void testStopAndFail() throws Exception { UUID randomUuid = UUID.randomUUID(); mockMvc.perform(put("/" + VERSION + "/results/" + randomUuid + "/stop" + "?receiver=me") - .header(HEADER_USER_ID, "testUserId")) + .header(HEADER_USER_ID, USER_ID)) .andExpect(status().isOk()); Message message = output.receive(TIMEOUT * 3, "sa.cancelfailed"); @@ -744,41 +721,13 @@ void testStopAndFail() throws Exception { assertEquals(getCancelFailedMessage(COMPUTATION_TYPE), message.getHeaders().get("message")); } - @Test - void runTestWithError() throws Exception { - MvcResult mvcResult; - String resultAsString; - - given(actionsService.getContingencyList(List.of(CONTINGENCY_LIST_ERROR_NAME), NETWORK_UUID, VARIANT_1_ID)) - .willThrow(new RuntimeException(ERROR_MESSAGE)); - - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_ERROR_NAME - + "&receiver=me&variantId=" + VARIANT_1_ID + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") - .contentType(MediaType.APPLICATION_JSON)) - .andExpectAll( - status().isOk(), - content().contentType(MediaType.APPLICATION_JSON) - ).andReturn(); - - resultAsString = mvcResult.getResponse().getContentAsString(); - UUID resultUuid = mapper.readValue(resultAsString, UUID.class); - assertEquals(RESULT_UUID, resultUuid); - - // No result message - assertNull(output.receive(TIMEOUT, "sa.result")); - - // No result - assertResultNotFound(RESULT_UUID); - } - @Test void runWithReportTest() throws Exception { MvcResult mvcResult; String resultAsString; - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + "&provider=testProvider" + "&reportUuid=" + REPORT_UUID + "&reporterId=" + UUID.randomUUID() + "&loadFlowParametersUuid=" + UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON) - .header(HEADER_USER_ID, "testUserId") + mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&provider=testProvider" + "&reportUuid=" + REPORT_UUID + "&reporterId=" + UUID.randomUUID() + "&loadFlowParametersUuid=" + UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), @@ -790,35 +739,6 @@ void runWithReportTest() throws Exception { assertThat(RESULT, new MatcherJson<>(mapper, securityAnalysisResult)); } - @Test - void runWithReportTestElementsNotFoundAndNotConnected() throws Exception { - MvcResult mvcResult; - String resultAsString; - - Network network = EurostagTutorialExample1Factory.create(new NetworkFactoryImpl()); - given(networkStoreService.getNetwork(NETWORK_UUID, PreloadingStrategy.COLLECTION)).willReturn(network); - given(actionsService.getContingencyList(List.of(CONTINGENCY_LIST_NAME), NETWORK_UUID, null)) - .willReturn(SecurityAnalysisProviderMock.CONTINGENCIES); - - mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + "&provider=testProvider" + "&reportUuid=" + REPORT_UUID + "&reporterId=" + UUID.randomUUID() + "&loadFlowParametersUuid=" + UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON) - .header(HEADER_USER_ID, "testUserId") - .contentType(MediaType.APPLICATION_JSON)) - .andExpectAll( - status().isOk(), - content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - - resultAsString = mvcResult.getResponse().getContentAsString(); - SecurityAnalysisResult securityAnalysisResult = mapper.readValue(resultAsString, SecurityAnalysisResult.class); - assertThat(RESULT, new MatcherJson<>(mapper, securityAnalysisResult)); - - assertLogMessage("Equipments not found", "security.analysis.server.notFoundEquipments", reportService); - assertLogMessage("Cannot find the following equipments wrongId1, wrongId2 in contingency l1", "security.analysis.server.contingencyEquipmentNotFound", reportService); - - assertLogMessage("Equipments not connected", "security.analysis.server.notConnectedEquipments", reportService); - assertLogMessage("The following equipments notConnectedId1 in contingency l4 are not connected", "security.analysis.server.contingencyEquipmentNotConnected", reportService); - } - @Test void getProvidersTest() throws Exception { MvcResult mvcResult; @@ -900,9 +820,8 @@ void getNmKContingenciesResultNotFound() throws Exception { @Test void getZippedCsvResults() throws Exception { // running computation to create some results - MvcResult mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME - + "&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) - .header(HEADER_USER_ID, "testUserId") + MvcResult mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( status().isOk(), diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersControllerTest.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersControllerTest.java index e6a429d0..909444c1 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersControllerTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisParametersControllerTest.java @@ -8,31 +8,42 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.vladmihalcea.sql.SQLStatementCountValidator; import org.gridsuite.computation.error.ComputationException; -import org.gridsuite.securityanalysis.server.dto.LimitReductionsByVoltageLevel; -import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersValues; +import org.gridsuite.securityanalysis.server.dto.parameters.ContingencyListsInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.IdNameInfos; +import org.gridsuite.securityanalysis.server.dto.parameters.LimitReductionsByVoltageLevel; +import org.gridsuite.securityanalysis.server.dto.parameters.SecurityAnalysisParametersValues; import org.gridsuite.securityanalysis.server.entities.SecurityAnalysisParametersEntity; import org.gridsuite.securityanalysis.server.repositories.SecurityAnalysisParametersRepository; +import org.gridsuite.securityanalysis.server.service.DirectoryService; import org.gridsuite.securityanalysis.server.service.LimitReductionService; import org.gridsuite.securityanalysis.server.service.SecurityAnalysisParametersService; import org.gridsuite.securityanalysis.server.util.ContextConfigurationWithTestChannel; import org.gridsuite.securityanalysis.server.util.MatcherJson; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import java.util.List; +import java.util.Map; import java.util.UUID; import static com.powsybl.network.store.model.NetworkStoreApi.VERSION; import static org.gridsuite.computation.error.ComputationBusinessErrorCode.PARAMETERS_NOT_FOUND; +import static org.gridsuite.computation.service.NotificationService.HEADER_USER_ID; +import static org.gridsuite.securityanalysis.server.util.DatabaseQueryUtils.assertRequestsCount; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -40,6 +51,7 @@ /** * @author Abdelsalem Hedhili */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) @AutoConfigureMockMvc @SpringBootTest @ContextConfigurationWithTestChannel @@ -63,6 +75,35 @@ class SecurityAnalysisParametersControllerTest { @Autowired private LimitReductionService limitReductionService; + @MockitoBean + private DirectoryService directoryService; + + private static final String USER_ID = "userId"; + private static final UUID CONTINGENCY_LIST_ID = UUID.fromString("3f7c9e2a-8b41-4d6a-a1f3-9c5b72e8d4af"); + private static final String CONTINGENCY_LIST_NAME = "contingencyList"; + private static final UUID DEACTIVATED_CONTINGENCY_LIST_ID = UUID.fromString("b8a4f2c1-6d3e-4a9b-92f7-1e5c8d7a3b60"); + private static final String DEACTIVATED_CONTINGENCY_LIST_NAME = "deactivatedContingencyList"; + + private List deactivatedContingencyListsInfos; + private List contingencyListsInfos; + + @BeforeEach + void setUp() { + contingencyListsInfos = List.of(new ContingencyListsInfos( + List.of(new IdNameInfos(CONTINGENCY_LIST_ID, CONTINGENCY_LIST_NAME)), + "activated contingency lists", + true)); + when(directoryService.getElementNames(List.of(CONTINGENCY_LIST_ID), USER_ID)) + .thenReturn(Map.of(CONTINGENCY_LIST_ID, CONTINGENCY_LIST_NAME)); + + deactivatedContingencyListsInfos = List.of(new ContingencyListsInfos( + List.of(new IdNameInfos(DEACTIVATED_CONTINGENCY_LIST_ID, DEACTIVATED_CONTINGENCY_LIST_NAME)), + "deactivated contingency lists", + false)); + when(directoryService.getElementNames(List.of(DEACTIVATED_CONTINGENCY_LIST_ID), USER_ID)) + .thenReturn(Map.of(DEACTIVATED_CONTINGENCY_LIST_ID, DEACTIVATED_CONTINGENCY_LIST_NAME)); + } + @Test void limitReductionConfigTest() { List limitReductions = limitReductionService.createDefaultLimitReductions(); @@ -138,8 +179,41 @@ void securityAnalysisParametersCreateAndGetTest() throws Exception { .limitReductions(limitReductionService.createLimitReductions(limitReductions)) .build()); + // Get with deactivated contingency lists + SQLStatementCountValidator.reset(); + testParametersCreateAndGetTest(builder.contingencyListsInfos(deactivatedContingencyListsInfos).build()); + /* + insert + security_analysis_parameters + parameters_contingency_lists + limit_reduction_entity + limit_reduction_entity_reductions + parameters_contingency_lists_contingency_list + update + parameters_contingency_lists + limit_reduction_entity + */ + assertRequestsCount(5, 5, 2, 0); + + // Get with contingency lists + SQLStatementCountValidator.reset(); + testParametersCreateAndGetTest(builder.contingencyListsInfos(contingencyListsInfos).build()); + /* + insert + security_analysis_parameters + parameters_contingency_lists + limit_reduction_entity + limit_reduction_entity_reductions + parameters_contingency_lists_contingency_list + update + parameters_contingency_lists + limit_reduction_entity + */ + assertRequestsCount(5, 5, 2, 0); + // Get not existing parameters and expect 404 - mockMvc.perform(get("/" + VERSION + "/parameters/" + UUID.randomUUID())) + mockMvc.perform(get("/" + VERSION + "/parameters/" + UUID.randomUUID()) + .header(HEADER_USER_ID, USER_ID)) .andExpect(status().isNotFound()); } @@ -149,6 +223,7 @@ private void testParametersCreateAndGetTest(SecurityAnalysisParametersValues par private void testParametersCreateAndGetTest(SecurityAnalysisParametersValues parametersToCreate, SecurityAnalysisParametersValues parametersExpected) throws Exception { MvcResult mvcResult = mockMvc.perform(post("/" + VERSION + "/parameters") + .header(HEADER_USER_ID, USER_ID) .content(objectMapper.writeValueAsString(parametersToCreate)) .contentType(MediaType.APPLICATION_JSON)) .andExpectAll( @@ -162,7 +237,8 @@ private void testParametersCreateAndGetTest(SecurityAnalysisParametersValues par assertSecurityAnalysisParametersEntityAreEquals(createdParametersUuid, 10, 11, 12, 13, 14); // Get the created parameters - mvcResult = mockMvc.perform(get("/" + VERSION + "/parameters/" + createdParametersUuid)) + mvcResult = mockMvc.perform(get("/" + VERSION + "/parameters/" + createdParametersUuid) + .header(HEADER_USER_ID, USER_ID)) .andExpectAll( status().isOk(), content().contentType(MediaType.APPLICATION_JSON) @@ -211,6 +287,7 @@ void securityAnalysisParametersUpdateTest() throws Exception { .highVoltageAbsoluteThreshold(12) .highVoltageProportionalThreshold(13) .flowProportionalThreshold(14) + .contingencyListsInfos(contingencyListsInfos) .limitReductions(limitReductionService.createLimitReductions(limitReductions)) .build(); @@ -270,6 +347,7 @@ void testDuplicateParameters() throws Exception { .highVoltageAbsoluteThreshold(12) .highVoltageProportionalThreshold(13) .flowProportionalThreshold(14) + .contingencyListsInfos(contingencyListsInfos) .limitReductions(limitReductionService.createLimitReductions(limitReductions)) .build(); @@ -288,7 +366,8 @@ void testDuplicateParameters() throws Exception { assertSecurityAnalysisParametersEntityAreEquals(createdParametersUuid, 10, 11, 12, 13, 14); //duplicate - mvcResult = mockMvc.perform(post("/" + VERSION + "/parameters").queryParam("duplicateFrom", createdParametersUuid.toString())) + mvcResult = mockMvc.perform(post("/" + VERSION + "/parameters").queryParam("duplicateFrom", createdParametersUuid.toString()) + .header(HEADER_USER_ID, USER_ID)) .andExpectAll( status().isOk(), content().contentType(MediaType.APPLICATION_JSON) @@ -300,7 +379,9 @@ void testDuplicateParameters() throws Exception { assertSecurityAnalysisParametersEntityAreEquals(duplicatedParametersUuid, 10, 11, 12, 13, 14); //duplicate not existing parameters and expect a 404 - mockMvc.perform(post("/" + VERSION + "/parameters").queryParam("duplicateFrom", UUID.randomUUID().toString())) + mockMvc.perform(post("/" + VERSION + "/parameters") + .header(HEADER_USER_ID, USER_ID) + .queryParam("duplicateFrom", UUID.randomUUID().toString())) .andExpectAll( status().isNotFound() ).andReturn(); @@ -319,6 +400,7 @@ void testRemoveParameters() throws Exception { .highVoltageAbsoluteThreshold(12) .highVoltageProportionalThreshold(13) .flowProportionalThreshold(14) + .contingencyListsInfos(contingencyListsInfos) .limitReductions(limitReductionService.createLimitReductions(limitReductions)) .build(); diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisProviderMock.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisProviderMock.java index 2201d464..fe3bc5f1 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisProviderMock.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisProviderMock.java @@ -22,6 +22,7 @@ import java.util.Comparator; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -36,10 +37,10 @@ public class SecurityAnalysisProviderMock implements SecurityAnalysisProvider { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityAnalysisProviderMock.class); - static final String CONTINGENCY_LIST_NAME = "list1"; - static final String CONTINGENCY_LIST2_NAME = "list2"; - static final String CONTINGENCY_LIST_ERROR_NAME = "listError"; - static final String CONTINGENCY_LIST_NAME_VARIANT = "listVariant"; + static final UUID CONTINGENCY_LIST_UUID = UUID.fromString("f7e8f79b-7626-4f46-939d-3bfdc895b0dd"); + static final UUID CONTINGENCY_LIST2_UUID = UUID.fromString("99fdbba0-0f30-4f24-b206-5481b00ad910"); + static final UUID CONTINGENCY_LIST_ERROR_UUID = UUID.fromString("d6e582ca-5084-4ad6-bf56-2ed3effdf9eb"); + static final UUID CONTINGENCY_LIST_UUID_VARIANT = UUID.fromString("98b6dcfc-0186-4aa6-a4e0-e4d04c4faab2"); static final List CONTINGENCIES = List.of( new ContingencyInfos(new Contingency("l1", new BranchContingency("l1")), Set.of("wrongId1, wrongId2"), Set.of()), diff --git a/src/test/java/org/gridsuite/securityanalysis/server/service/ActionsServiceTest.java b/src/test/java/org/gridsuite/securityanalysis/server/service/ActionsServiceTest.java index f3ba6b34..327f0746 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/service/ActionsServiceTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/service/ActionsServiceTest.java @@ -55,10 +55,10 @@ class ActionsServiceTest { private static final String VARIANT_ID = "variant_id"; - private static final String LIST_NAME = "myList"; - private static final String LIST_NAME_VARIANT = "myListVariant"; + private static final UUID LIST_UUID = UUID.fromString("e6bc6e4b-bbf1-4342-a8ae-49b213f0c85a"); + private static final UUID LIST_UUID_VARIANT = UUID.fromString("81bb191f-b899-4999-91e3-2d158d208764"); - private static final String VERY_LARGE_LIST_NAME = "veryLargelist"; + private static final UUID VERY_LARGE_LIST_UUID = UUID.fromString("4856bff4-9ebe-4ba2-b66d-0242f27fce7b"); public static final String WRONG_ID = "wrongID"; private static final ContingencyInfos CONTINGENCY = new ContingencyInfos(new Contingency("c1", new BranchContingency("b1")), Set.of(WRONG_ID), Set.of()); @@ -88,14 +88,14 @@ private String initMockWebServer(final MockWebServer server) throws IOException @Override public MockResponse dispatch(RecordedRequest request) { String requestPath = Objects.requireNonNull(request.getPath()); - if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&variantId=%s&ids=%s", NETWORK_UUID, VARIANT_ID, LIST_NAME))) { + if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&variantId=%s&ids=%s", NETWORK_UUID, VARIANT_ID, LIST_UUID))) { return new MockResponse(HttpStatus.OK.value(), Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), jsonVariantExpected); - } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s", NETWORK_UUID, LIST_NAME))) { + } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s", NETWORK_UUID, LIST_UUID))) { return new MockResponse(HttpStatus.OK.value(), Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), jsonExpected); - } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&variantId=%s&ids=%s", NETWORK_UUID, VARIANT_ID, VERY_LARGE_LIST_NAME)) - || requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s", NETWORK_UUID, VERY_LARGE_LIST_NAME))) { + } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&variantId=%s&ids=%s", NETWORK_UUID, VARIANT_ID, VERY_LARGE_LIST_UUID)) + || requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s", NETWORK_UUID, VERY_LARGE_LIST_UUID))) { return new MockResponse(HttpStatus.OK.value(), Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), veryLargeJsonExpected); - } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s&ids=%s", NETWORK_UUID, LIST_NAME, LIST_NAME_VARIANT))) { + } else if (requestPath.equals(String.format("/v1/contingency-lists/contingency-infos/export?networkUuid=%s&ids=%s&ids=%s", NETWORK_UUID, LIST_UUID, LIST_UUID_VARIANT))) { return new MockResponse(HttpStatus.OK.value(), Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), jsonExpectedForList); } else { return new MockResponse.Builder().code(HttpStatus.NOT_FOUND.value()).body("Path not supported: " + request.getPath()).build(); @@ -117,26 +117,26 @@ private static List createVeryLargeList() { @Test void test() { - List list = actionsService.getContingencyList(List.of(LIST_NAME), UUID.fromString(NETWORK_UUID), null); + List list = actionsService.getContingencyList(List.of(LIST_UUID), UUID.fromString(NETWORK_UUID), null); list.forEach(contingencyInfos -> assertArrayEquals(List.of(WRONG_ID).toArray(new Object[0]), contingencyInfos.getNotFoundElements().toArray(new String[0]))); assertEquals(Stream.of(CONTINGENCY).map(ContingencyInfos::getContingency).toList(), list.stream().map(ContingencyInfos::getContingency).collect(Collectors.toList())); - list = actionsService.getContingencyList(List.of(LIST_NAME), UUID.fromString(NETWORK_UUID), VARIANT_ID); + list = actionsService.getContingencyList(List.of(LIST_UUID), UUID.fromString(NETWORK_UUID), VARIANT_ID); assertEquals(Stream.of(CONTINGENCY_VARIANT).map(ContingencyInfos::getContingency).toList(), list.stream().map(ContingencyInfos::getContingency).collect(Collectors.toList())); } @Test void testVeryLargeList() { // DataBufferLimitException should not be thrown with this message : "Exceeded limit on max bytes to buffer : DATA_BUFFER_LIMIT" - List list = actionsService.getContingencyList(List.of(VERY_LARGE_LIST_NAME), UUID.fromString(NETWORK_UUID), null); + List list = actionsService.getContingencyList(List.of(VERY_LARGE_LIST_UUID), UUID.fromString(NETWORK_UUID), null); list.forEach(contingencyInfos -> assertArrayEquals(List.of().toArray(new Object[0]), contingencyInfos.getNotFoundElements().toArray(new String[0]))); assertEquals(createVeryLargeList().stream().map(ContingencyInfos::getContingency).collect(Collectors.toList()), list.stream().map(ContingencyInfos::getContingency).collect(Collectors.toList())); - list = actionsService.getContingencyList(List.of(VERY_LARGE_LIST_NAME), UUID.fromString(NETWORK_UUID), VARIANT_ID); + list = actionsService.getContingencyList(List.of(VERY_LARGE_LIST_UUID), UUID.fromString(NETWORK_UUID), VARIANT_ID); assertEquals(createVeryLargeList().stream().map(ContingencyInfos::getContingency).collect(Collectors.toList()), list.stream().map(ContingencyInfos::getContingency).collect(Collectors.toList())); } @Test void testGetContingenciesByListOfIds() { - List list = actionsService.getContingencyList(List.of(LIST_NAME, LIST_NAME_VARIANT), UUID.fromString(NETWORK_UUID), null); + List list = actionsService.getContingencyList(List.of(LIST_UUID, LIST_UUID_VARIANT), UUID.fromString(NETWORK_UUID), null); assertEquals(Stream.of(CONTINGENCY, CONTINGENCY_VARIANT).map(ContingencyInfos::getContingency).toList(), list.stream().map(ContingencyInfos::getContingency).collect(Collectors.toList())); } } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/service/DirectoryServiceTest.java b/src/test/java/org/gridsuite/securityanalysis/server/service/DirectoryServiceTest.java new file mode 100644 index 00000000..b39f01b2 --- /dev/null +++ b/src/test/java/org/gridsuite/securityanalysis/server/service/DirectoryServiceTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import org.gridsuite.securityanalysis.server.dto.ElementAttributes; +import org.gridsuite.securityanalysis.server.util.ContextConfigurationWithTestChannel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.util.ReflectionTestUtils; +import static org.gridsuite.computation.service.NotificationService.HEADER_USER_ID; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author Caroline Jeandat {@literal } + */ +@AutoConfigureMockMvc +@SpringBootTest +@ContextConfigurationWithTestChannel +class DirectoryServiceTest { + + private WireMockServer wireMockServer; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private DirectoryService directoryService; + + private static final String USER_ID = "userId"; + private static final UUID CONTINGENCY_LIST_ID_1 = UUID.fromString("3f7c9e2a-8b41-4d6a-a1f3-9c5b72e8d4af"); + private static final String CONTINGENCY_LIST_NAME_1 = "contingencyList1"; + private static final UUID CONTINGENCY_LIST_ID_2 = UUID.fromString("b8a4f2c1-6d3e-4a9b-92f7-1e5c8d7a3b60"); + private static final String CONTINGENCY_LIST_NAME_2 = "contingencyList2"; + + @BeforeEach + void setUp() { + wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); + wireMockServer.start(); + ReflectionTestUtils.setField(directoryService, "baseUri", wireMockServer.baseUrl()); + } + + @AfterEach + void tearDown() { + wireMockServer.stop(); + } + + @Test + void getElementNamesWithNullElementUuidsTest() { + assertThrows(NullPointerException.class, + () -> directoryService.getElementNames(null, USER_ID)); + } + + @Test + void getElementNamesWithEmptyElementUuidsTest() { + assertEquals(Map.of(), directoryService.getElementNames(List.of(), USER_ID)); + } + + @Test + void getElementNamesTest() throws JsonProcessingException { + List elementAttributes = List.of( + new ElementAttributes(CONTINGENCY_LIST_ID_1, CONTINGENCY_LIST_NAME_1), + new ElementAttributes(CONTINGENCY_LIST_ID_2, CONTINGENCY_LIST_NAME_2) + ); + wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/elements")) + .withQueryParam("ids", WireMock.equalTo(CONTINGENCY_LIST_ID_1.toString())) + .withQueryParam("ids", WireMock.equalTo(CONTINGENCY_LIST_ID_2.toString())) + .withQueryParam("strictMode", WireMock.equalTo("false")) + .withHeader(HEADER_USER_ID, WireMock.equalTo(USER_ID)) + .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(objectMapper.writeValueAsString(elementAttributes)))); + + Map elementNames = Map.of( + CONTINGENCY_LIST_ID_1, CONTINGENCY_LIST_NAME_1, + CONTINGENCY_LIST_ID_2, CONTINGENCY_LIST_NAME_2 + ); + assertEquals(elementNames, directoryService.getElementNames(List.of(CONTINGENCY_LIST_ID_1, CONTINGENCY_LIST_ID_2), USER_ID)); + } +} diff --git a/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersServiceTest.java b/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersServiceTest.java new file mode 100644 index 00000000..a9032e21 --- /dev/null +++ b/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisParametersServiceTest.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server.service; + +import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersDTO; +import org.gridsuite.securityanalysis.server.entities.ParametersContingencyListEntity; +import org.gridsuite.securityanalysis.server.entities.SecurityAnalysisParametersEntity; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Caroline Jeandat {@literal } + */ +@SpringBootTest +class SecurityAnalysisParametersServiceTest { + + @Autowired + SecurityAnalysisParametersService securityAnalysisParametersService; + + private static final UUID CONTINGENCY_LIST_ID = UUID.fromString("3f7c9e2a-8b41-4d6a-a1f3-9c5b72e8d4af"); + private static final UUID DEACTIVATED_CONTINGENCY_LIST_ID = UUID.fromString("b8a4f2c1-6d3e-4a9b-92f7-1e5c8d7a3b60"); + + @Test + void toSecurityAnalysisParametersWithContingencyListsTest() { + // result securityAnalysisParameters should only contain the "activated" contingencyLists + SecurityAnalysisParametersEntity entity = SecurityAnalysisParametersEntity.builder() + .contingencyLists(List.of( + new ParametersContingencyListEntity(List.of(CONTINGENCY_LIST_ID), "", true), + new ParametersContingencyListEntity(List.of(DEACTIVATED_CONTINGENCY_LIST_ID), "", false) + )) + .limitReductions(List.of()) + .build(); + SecurityAnalysisParametersDTO parameters = securityAnalysisParametersService.toSecurityAnalysisParameters(entity); + assertEquals(List.of(CONTINGENCY_LIST_ID), parameters.contingencyListUuids()); + + SecurityAnalysisParametersEntity entityWithoutContingencyLists = SecurityAnalysisParametersEntity.builder() + .contingencyLists(null) + .limitReductions(List.of()) + .build(); + SecurityAnalysisParametersDTO parametersWithoutContingencyLists = securityAnalysisParametersService.toSecurityAnalysisParameters(entityWithoutContingencyLists); + assertEquals(List.of(), parametersWithoutContingencyLists.contingencyListUuids()); + } +}