From 15ac58000f718442df0d435b47f81cf0b4365d3d Mon Sep 17 00:00:00 2001 From: Ayoub LABIDI <117761394+ayolab@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:20:26 +0100 Subject: [PATCH] Add loadflow parameters controller (#82) Signed-off-by: Ayoub LABIDI --- .../loadflow/server/LoadFlowController.java | 18 +- .../server/LoadFlowParametersController.java | 94 ++++++++ .../parameters/LoadFlowParametersInfos.java | 29 +++ .../LoadFlowParametersValues.java} | 18 +- .../parameters/LoadFlowParametersEntity.java | 209 ++++++++++++++++++ .../LoadFlowSpecificParameterEntity.java | 47 ++++ .../LoadFlowParametersRepository.java | 21 ++ .../server/service/LoadFlowResultContext.java | 6 +- .../server/service/LoadFlowRunContext.java | 19 +- .../server/service/LoadFlowService.java | 15 +- .../server/service/ReportService.java | 2 - .../parameters/LoadFlowParametersService.java | 77 +++++++ .../changesets/changelog_20240126T122251Z.xml | 84 +++++++ .../db/changelog/db.changelog-master.yaml | 4 + .../server/LoadFlowControllerTest.java | 26 ++- .../server/LoadFlowParametersTest.java | 197 +++++++++++++++++ .../loadflow/utils/assertions/Assertions.java | 21 ++ .../loadflow/utils/assertions/DTOAssert.java | 37 ++++ 18 files changed, 874 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/gridsuite/loadflow/server/LoadFlowParametersController.java create mode 100644 src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersInfos.java rename src/main/java/org/gridsuite/loadflow/server/dto/{LoadFlowParametersInfos.java => parameters/LoadFlowParametersValues.java} (57%) create mode 100644 src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java create mode 100644 src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java create mode 100644 src/main/java/org/gridsuite/loadflow/server/repositories/parameters/LoadFlowParametersRepository.java create mode 100644 src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java create mode 100644 src/main/resources/db/changelog/changesets/changelog_20240126T122251Z.xml create mode 100644 src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java create mode 100644 src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java create mode 100644 src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java diff --git a/src/main/java/org/gridsuite/loadflow/server/LoadFlowController.java b/src/main/java/org/gridsuite/loadflow/server/LoadFlowController.java index 33082e22..d3fc74f0 100644 --- a/src/main/java/org/gridsuite/loadflow/server/LoadFlowController.java +++ b/src/main/java/org/gridsuite/loadflow/server/LoadFlowController.java @@ -12,13 +12,11 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.gridsuite.loadflow.server.dto.LimitViolationInfos; -import org.gridsuite.loadflow.server.dto.LoadFlowParametersInfos; import org.gridsuite.loadflow.server.dto.LoadFlowResult; import org.gridsuite.loadflow.server.dto.LoadFlowStatus; import org.gridsuite.loadflow.server.service.LoadFlowRunContext; import org.gridsuite.loadflow.server.service.LoadFlowService; import org.gridsuite.loadflow.server.utils.ReportContext; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -39,8 +37,11 @@ @Tag(name = "loadflow-server") public class LoadFlowController { - @Autowired - private LoadFlowService loadFlowService; + private final LoadFlowService loadFlowService; + + public LoadFlowController(LoadFlowService loadFlowService) { + this.loadFlowService = loadFlowService; + } @PostMapping(value = "/networks/{networkUuid}/run-and-save", produces = APPLICATION_JSON_VALUE) @Operation(summary = "Run a load flow on a network") @@ -53,21 +54,18 @@ public ResponseEntity run(@Parameter(description = "Network UUID") @PathVa @Parameter(description = "reporterId") @RequestParam(name = "reporterId", required = false) String reportName, @Parameter(description = "The type name for the report") @RequestParam(name = "reportType", required = false, defaultValue = "LoadFlow") String reportType, @Parameter(description = "The limit reduction") @RequestParam(name = "limitReduction", required = false, defaultValue = "0.8F") Float limitReduction, - @RequestHeader(HEADER_USER_ID) String userId, - @RequestBody(required = false) LoadFlowParametersInfos loadflowParams + @Parameter(description = "parametersUuid") @RequestParam(name = "parametersUuid", required = false) UUID parametersUuid, + @RequestHeader(HEADER_USER_ID) String userId ) { - String providerToUse = provider != null ? provider : loadFlowService.getDefaultProvider(); LoadFlowRunContext loadFlowRunContext = LoadFlowRunContext.builder() .networkUuid(networkUuid) .variantId(variantId) .receiver(receiver) - .provider(providerToUse) - .parameters(loadflowParams) .reportContext(ReportContext.builder().reportId(reportId).reportName(reportName).reportType(reportType).build()) .userId(userId) .limitReduction(limitReduction) .build(); - UUID resultUuid = loadFlowService.runAndSaveResult(loadFlowRunContext); + UUID resultUuid = loadFlowService.runAndSaveResult(loadFlowRunContext, provider, parametersUuid); return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(resultUuid); } diff --git a/src/main/java/org/gridsuite/loadflow/server/LoadFlowParametersController.java b/src/main/java/org/gridsuite/loadflow/server/LoadFlowParametersController.java new file mode 100644 index 00000000..1002fc1b --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/LoadFlowParametersController.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2024, 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.loadflow.server; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.service.parameters.LoadFlowParametersService; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +/** + * @author Ayoub LABIDI + */ +@RestController +@RequestMapping(value = "/" + LoadFlowApi.API_VERSION + "/parameters") +@Tag(name = "LoadFlow parameters") +public class LoadFlowParametersController { + + private final LoadFlowParametersService parametersService; + + public LoadFlowParametersController(LoadFlowParametersService parametersService) { + this.parametersService = parametersService; + } + + @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Create parameters") + @ApiResponse(responseCode = "200", description = "parameters were created") + public ResponseEntity createParameters( + @RequestBody LoadFlowParametersInfos parametersInfos) { + return ResponseEntity.ok(parametersService.createParameters(parametersInfos)); + } + + @PostMapping(value = "/default") + @Operation(summary = "Create default parameters") + @ApiResponse(responseCode = "200", description = "Default parameters were created") + public ResponseEntity createDefaultParameters() { + return ResponseEntity.ok(parametersService.createDefaultParameters()); + } + + @PostMapping(value = "/{sourceParametersUuid}") + @Operation(summary = "Duplicate parameters") + @ApiResponse(responseCode = "200", description = "parameters were duplicated") + public ResponseEntity duplicateParameters( + @Parameter(description = "source parameters UUID") @PathVariable("sourceParametersUuid") UUID sourceParametersUuid) { + return ResponseEntity.of(parametersService.duplicateParameters(sourceParametersUuid)); + } + + @GetMapping(value = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get parameters") + @ApiResponse(responseCode = "200", description = "parameters were returned") + @ApiResponse(responseCode = "404", description = "parameters were not found") + public ResponseEntity getParameters( + @Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid) { + return ResponseEntity.of(parametersService.getParameters(parametersUuid)); + } + + @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get all parameters") + @ApiResponse(responseCode = "200", description = "The list of all parameters was returned") + public ResponseEntity> getAllParameters() { + return ResponseEntity.ok().body(parametersService.getAllParameters()); + } + + @PutMapping(value = "/{uuid}", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Update parameters") + @ApiResponse(responseCode = "200", description = "parameters were updated") + public ResponseEntity updateParameters( + @Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid, + @RequestBody LoadFlowParametersInfos parametersInfos) { + parametersService.updateParameters(parametersUuid, parametersInfos); + return ResponseEntity.ok().build(); + } + + @DeleteMapping(value = "/{uuid}") + @Operation(summary = "Delete parameters") + @ApiResponse(responseCode = "200", description = "parameters were deleted") + public ResponseEntity deleteParameters( + @Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid) { + parametersService.deleteParameters(parametersUuid); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersInfos.java b/src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersInfos.java new file mode 100644 index 00000000..2ac0842c --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersInfos.java @@ -0,0 +1,29 @@ +/** + Copyright (c) 2024, 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.loadflow.server.dto.parameters; + +import com.powsybl.loadflow.LoadFlowParameters; + +import lombok.Builder; + +import java.util.Map; +import java.util.UUID; + +import org.gridsuite.loadflow.server.entities.parameters.LoadFlowParametersEntity; + +/** + * @author Ayoub LABIDI + */ +@Builder +public record LoadFlowParametersInfos( + UUID uuid, + LoadFlowParameters commonParameters, + Map> specificParametersPerProvider) { + public LoadFlowParametersEntity toEntity() { + return new LoadFlowParametersEntity(this); + } +} diff --git a/src/main/java/org/gridsuite/loadflow/server/dto/LoadFlowParametersInfos.java b/src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersValues.java similarity index 57% rename from src/main/java/org/gridsuite/loadflow/server/dto/LoadFlowParametersInfos.java rename to src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersValues.java index f8cf7237..d60967b9 100644 --- a/src/main/java/org/gridsuite/loadflow/server/dto/LoadFlowParametersInfos.java +++ b/src/main/java/org/gridsuite/loadflow/server/dto/parameters/LoadFlowParametersValues.java @@ -4,26 +4,18 @@ 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.loadflow.server.dto; +package org.gridsuite.loadflow.server.dto.parameters; import com.powsybl.loadflow.LoadFlowParameters; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; import java.util.Map; /** * @author David Braquart */ -@Getter -@NoArgsConstructor -@AllArgsConstructor @Builder -public class LoadFlowParametersInfos { - - private LoadFlowParameters commonParameters; - - private Map specificParameters; -} +public record LoadFlowParametersValues( + LoadFlowParameters commonParameters, + Map specificParameters +) { } diff --git a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java new file mode 100644 index 00000000..a5d9b2f0 --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2024, 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.loadflow.server.entities.parameters; + +import com.powsybl.iidm.network.Country; +import com.powsybl.loadflow.LoadFlowParameters; +import lombok.*; + +import jakarta.persistence.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersValues; + +/** + * @author Ayoub LABIDI + */ +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "loadFlowParameters") +public class LoadFlowParametersEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private UUID id; + + @Column(name = "voltageInitMode") + @Enumerated(EnumType.STRING) + private LoadFlowParameters.VoltageInitMode voltageInitMode; + + @Column(name = "transformerVoltageControlOn", columnDefinition = "boolean default false", nullable = false) + private boolean transformerVoltageControlOn = false; + + @Column(name = "useReactiveLimits", columnDefinition = "boolean default true", nullable = false) + private boolean useReactiveLimits = true; + + @Column(name = "phaseShifterRegulationOn", columnDefinition = "boolean default false", nullable = false) + private boolean phaseShifterRegulationOn = false; + + @Column(name = "twtSplitShuntAdmittance", columnDefinition = "boolean default false", nullable = false) + private boolean twtSplitShuntAdmittance = false; + + @Column(name = "shuntCompensatorVoltageControlOn", columnDefinition = "boolean default false", nullable = false) + private boolean shuntCompensatorVoltageControlOn = false; + + @Column(name = "readSlackBus", columnDefinition = "boolean default true", nullable = false) + private boolean readSlackBus = true; + + @Column(name = "writeSlackBus", columnDefinition = "boolean default false", nullable = false) + private boolean writeSlackBus = false; + + @Column(name = "dc", columnDefinition = "boolean default false", nullable = false) + private boolean dc = false; + + @Column(name = "distributedSlack", columnDefinition = "boolean default true", nullable = false) + private boolean distributedSlack = true; + + @Column(name = "balanceType") + @Enumerated(EnumType.STRING) + private LoadFlowParameters.BalanceType balanceType; + + @Column(name = "dcUseTransformerRatio", columnDefinition = "boolean default true", nullable = false) + private boolean dcUseTransformerRatio = true; + + @Column(name = "countriesToBalance") + @ElementCollection + @CollectionTable(foreignKey = @ForeignKey(name = "loadFlowParametersEntity_countriesToBalance_fk1"), + indexes = {@Index(name = "loadFlowParametersEntity_countriesToBalance_idx1", + columnList = "load_flow_parameters_entity_id")}) + private Set countriesToBalance; + + @Column(name = "connectedComponentMode") + @Enumerated(EnumType.STRING) + private LoadFlowParameters.ConnectedComponentMode connectedComponentMode; + + @Column(name = "hvdcAcEmulation", columnDefinition = "boolean default true", nullable = false) + private boolean hvdcAcEmulation = true; + + @Column(name = "dcPowerFactor", columnDefinition = "double default 1.0", nullable = false) + private double dcPowerFactor = 1.0; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "load_flow_parameters_id", foreignKey = @ForeignKey(name = "loadFlowParametersEntity_specificParameters_fk")) + private List specificParameters; + + public LoadFlowParametersEntity(LoadFlowParametersInfos loadFlowParametersInfos) { + assignAttributes(loadFlowParametersInfos); + } + + public void update(LoadFlowParametersInfos loadFlowParametersInfos) { + assignAttributes(loadFlowParametersInfos); + } + + public void assignAttributes(LoadFlowParametersInfos loadFlowParametersInfos) { + LoadFlowParameters allCommonValues; + List allSpecificValuesEntities = new ArrayList<>(List.of()); + if (loadFlowParametersInfos == null) { + allCommonValues = LoadFlowParameters.load(); + } else { + allCommonValues = loadFlowParametersInfos.commonParameters(); + if (loadFlowParametersInfos.specificParametersPerProvider() != null) { + loadFlowParametersInfos.specificParametersPerProvider().forEach((provider, paramsMap) -> { + if (paramsMap != null) { + paramsMap.forEach((paramName, paramValue) -> { + if (paramValue != null) { + allSpecificValuesEntities.add(new LoadFlowSpecificParameterEntity( + null, + provider, + paramName, + paramValue)); + } + }); + } + }); + } + } + assignCommonValues(allCommonValues); + assignSpecificValues(allSpecificValuesEntities); + } + + private void assignCommonValues(LoadFlowParameters allCommonValues) { + voltageInitMode = allCommonValues.getVoltageInitMode(); + transformerVoltageControlOn = allCommonValues.isTransformerVoltageControlOn(); + useReactiveLimits = allCommonValues.isUseReactiveLimits(); + phaseShifterRegulationOn = allCommonValues.isPhaseShifterRegulationOn(); + twtSplitShuntAdmittance = allCommonValues.isTwtSplitShuntAdmittance(); + shuntCompensatorVoltageControlOn = allCommonValues.isShuntCompensatorVoltageControlOn(); + readSlackBus = allCommonValues.isReadSlackBus(); + writeSlackBus = allCommonValues.isWriteSlackBus(); + dc = allCommonValues.isDc(); + distributedSlack = allCommonValues.isDistributedSlack(); + balanceType = allCommonValues.getBalanceType(); + dcUseTransformerRatio = allCommonValues.isDcUseTransformerRatio(); + countriesToBalance = allCommonValues.getCountriesToBalance().stream().map(Country::toString).collect(Collectors.toSet()); + connectedComponentMode = allCommonValues.getConnectedComponentMode(); + hvdcAcEmulation = allCommonValues.isHvdcAcEmulation(); + dcPowerFactor = allCommonValues.getDcPowerFactor(); + } + + private void assignSpecificValues(List allSpecificValuesEntities) { + if (specificParameters == null) { + specificParameters = allSpecificValuesEntities; + } else { + specificParameters.clear(); + if (!allSpecificValuesEntities.isEmpty()) { + specificParameters.addAll(allSpecificValuesEntities); + } + } + } + + public LoadFlowParameters toLoadFlowParameters() { + return LoadFlowParameters.load() + .setVoltageInitMode(this.getVoltageInitMode()) + .setTransformerVoltageControlOn(this.isTransformerVoltageControlOn()) + .setUseReactiveLimits(this.isUseReactiveLimits()) + .setPhaseShifterRegulationOn(this.isPhaseShifterRegulationOn()) + .setTwtSplitShuntAdmittance(this.isTwtSplitShuntAdmittance()) + .setShuntCompensatorVoltageControlOn(this.isShuntCompensatorVoltageControlOn()) + .setReadSlackBus(this.isReadSlackBus()) + .setWriteSlackBus(this.isWriteSlackBus()) + .setDc(this.isDc()) + .setDistributedSlack(this.isDistributedSlack()) + .setBalanceType(this.getBalanceType()) + .setDcUseTransformerRatio(this.isDcUseTransformerRatio()) + .setCountriesToBalance(this.getCountriesToBalance().stream().map(Country::valueOf).collect(Collectors.toSet())) + .setConnectedComponentMode(this.getConnectedComponentMode()) + .setHvdcAcEmulation(this.isHvdcAcEmulation()) + .setDcPowerFactor(this.getDcPowerFactor()); + } + + public LoadFlowParametersInfos toLoadFlowParametersInfos() { + return LoadFlowParametersInfos.builder() + .uuid(id) + .commonParameters(toLoadFlowParameters()) + .specificParametersPerProvider(specificParameters.stream() + .collect(Collectors.groupingBy(LoadFlowSpecificParameterEntity::getProvider, + Collectors.toMap(LoadFlowSpecificParameterEntity::getName, + LoadFlowSpecificParameterEntity::getValue)))) + .build(); + } + + public LoadFlowParametersValues toLoadFlowParametersValues(String provider) { + return LoadFlowParametersValues.builder() + .commonParameters(toLoadFlowParameters()) + .specificParameters(specificParameters.stream() + .filter(p -> p.getProvider().equalsIgnoreCase(provider)) + .collect(Collectors.toMap(LoadFlowSpecificParameterEntity::getName, + LoadFlowSpecificParameterEntity::getValue))) + .build(); + } + + public LoadFlowParametersEntity copy() { + return toLoadFlowParametersInfos().toEntity(); + } +} diff --git a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java new file mode 100644 index 00000000..3abbff68 --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2024, 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.loadflow.server.entities.parameters; + +import java.util.UUID; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * @author Ayoub LABIDI + */ + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Entity +@Table(name = "loadFlowSpecificParameters", indexes = { + @Index(name = "loadFlowParametersEntity_specificParameters_index", columnList = "load_flow_parameters_id") +}) +public class LoadFlowSpecificParameterEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", nullable = false) + private UUID id; + + @Column(name = "provider") + private String provider; + + @Column(name = "name") + private String name; + + @Column(name = "value_") + private String value; +} diff --git a/src/main/java/org/gridsuite/loadflow/server/repositories/parameters/LoadFlowParametersRepository.java b/src/main/java/org/gridsuite/loadflow/server/repositories/parameters/LoadFlowParametersRepository.java new file mode 100644 index 00000000..b9a82ab2 --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/repositories/parameters/LoadFlowParametersRepository.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2024, 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.loadflow.server.repositories.parameters; + +import java.util.UUID; + +import org.gridsuite.loadflow.server.entities.parameters.LoadFlowParametersEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * @author Ayoub LABIDI + */ + +@Repository +public interface LoadFlowParametersRepository extends JpaRepository { +} diff --git a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowResultContext.java b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowResultContext.java index df3505a1..597c3a4e 100644 --- a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowResultContext.java +++ b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowResultContext.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; -import org.gridsuite.loadflow.server.dto.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersValues; import org.gridsuite.loadflow.server.utils.ReportContext; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; @@ -55,13 +55,13 @@ public static LoadFlowResultContext fromMessage(Message message, ObjectM String provider = (String) headers.get(HEADER_PROVIDER); String userId = (String) headers.get(HEADER_USER_ID); - LoadFlowParametersInfos parameters; + LoadFlowParametersValues parameters; try { // can't use the following line because jackson doesn't unwrap null in the rootname // -> '{"parameters": null}' throws instead returning null // MismatchedInputException: Cannot deserialize value of type `LoadFlowParametersInfos` from Null value (token `JsonToken.VALUE_NULL`) // parameters = objectMapper.reader().withRootName(MESSAGE_ROOT_NAME).readValue(message.getPayload(), LoadFlowParametersInfos.class); - parameters = objectMapper.treeToValue(objectMapper.readTree(message.getPayload()).get(MESSAGE_ROOT_NAME), LoadFlowParametersInfos.class); + parameters = objectMapper.treeToValue(objectMapper.readTree(message.getPayload()).get(MESSAGE_ROOT_NAME), LoadFlowParametersValues.class); } catch (JsonProcessingException e) { throw new UncheckedIOException(e); } diff --git a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowRunContext.java b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowRunContext.java index a0da409d..b8b5ab5a 100644 --- a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowRunContext.java +++ b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowRunContext.java @@ -12,7 +12,9 @@ import com.powsybl.loadflow.LoadFlowProvider; import lombok.Builder; import lombok.Getter; -import org.gridsuite.loadflow.server.dto.LoadFlowParametersInfos; +import lombok.Setter; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersValues; import org.gridsuite.loadflow.server.utils.ReportContext; import java.util.UUID; @@ -21,6 +23,7 @@ * @author Anis Touri */ @Getter +@Setter @Builder public class LoadFlowRunContext { @@ -30,9 +33,9 @@ public class LoadFlowRunContext { private final String receiver; - private final String provider; + private String provider; - private final LoadFlowParametersInfos parameters; + private LoadFlowParametersValues parameters; private final ReportContext reportContext; @@ -40,16 +43,16 @@ public class LoadFlowRunContext { private final Float limitReduction; - public static LoadFlowParameters buildParameters(LoadFlowParametersInfos parameters, String provider) { - LoadFlowParameters params = parameters == null || parameters.getSpecificParameters() == null ? - LoadFlowParameters.load() : parameters.getCommonParameters(); - if (parameters == null || parameters.getSpecificParameters() == null || parameters.getSpecificParameters().isEmpty()) { + public static LoadFlowParameters buildParameters(LoadFlowParametersValues parameters, String provider) { + LoadFlowParameters params = parameters == null || parameters.specificParameters() == null ? + LoadFlowParameters.load() : parameters.commonParameters(); + if (parameters == null || parameters.specificParameters() == null || parameters.specificParameters().isEmpty()) { return params; // no specific LF params } LoadFlowProvider lfProvider = LoadFlowProvider.findAll().stream() .filter(p -> p.getName().equals(provider)) .findFirst().orElseThrow(() -> new PowsyblException("LoadFLow provider not found " + provider)); - Extension extension = lfProvider.loadSpecificParameters(parameters.getSpecificParameters()) + Extension extension = lfProvider.loadSpecificParameters(parameters.specificParameters()) .orElseThrow(() -> new PowsyblException("Cannot add specific loadflow parameters with provider " + provider)); params.addExtension((Class) extension.getClass(), extension); return params; diff --git a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowService.java b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowService.java index ca35a80e..a888b805 100644 --- a/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowService.java +++ b/src/main/java/org/gridsuite/loadflow/server/service/LoadFlowService.java @@ -21,6 +21,7 @@ import org.gridsuite.loadflow.server.entities.LimitViolationsEntity; import org.gridsuite.loadflow.server.entities.LoadFlowResultEntity; import org.gridsuite.loadflow.server.repositories.LoadFlowResultRepository; +import org.gridsuite.loadflow.server.service.parameters.LoadFlowParametersService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -51,11 +52,14 @@ public class LoadFlowService { private UuidGeneratorService uuidGeneratorService; - public LoadFlowService(NotificationService notificationService, LoadFlowResultRepository resultRepository, ObjectMapper objectMapper, UuidGeneratorService uuidGeneratorService) { + private LoadFlowParametersService parametersService; + + public LoadFlowService(NotificationService notificationService, LoadFlowResultRepository resultRepository, ObjectMapper objectMapper, UuidGeneratorService uuidGeneratorService, LoadFlowParametersService parametersService) { this.notificationService = Objects.requireNonNull(notificationService); this.resultRepository = Objects.requireNonNull(resultRepository); this.objectMapper = Objects.requireNonNull(objectMapper); this.uuidGeneratorService = Objects.requireNonNull(uuidGeneratorService); + this.parametersService = Objects.requireNonNull(parametersService); } public static List getProviders() { @@ -83,13 +87,16 @@ public static Map> getSpecificLoadFlowParameters(String }).collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); } - public UUID runAndSaveResult(LoadFlowRunContext runContext) { - Objects.requireNonNull(runContext); + public UUID runAndSaveResult(LoadFlowRunContext loadFlowRunContext, String provider, UUID parametersUuid) { + String providerToUse = provider != null ? provider : getDefaultProvider(); + // set provider and parameters + loadFlowRunContext.setParameters(parametersService.getParametersValues(parametersUuid, providerToUse).orElse(null)); + loadFlowRunContext.setProvider(providerToUse); UUID resultUuid = uuidGeneratorService.generate(); // update status to running status setStatus(List.of(resultUuid), LoadFlowStatus.RUNNING); - notificationService.sendRunMessage(new LoadFlowResultContext(resultUuid, runContext).toMessage(objectMapper)); + notificationService.sendRunMessage(new LoadFlowResultContext(resultUuid, loadFlowRunContext).toMessage(objectMapper)); return resultUuid; } diff --git a/src/main/java/org/gridsuite/loadflow/server/service/ReportService.java b/src/main/java/org/gridsuite/loadflow/server/service/ReportService.java index 96136a88..8eae7300 100644 --- a/src/main/java/org/gridsuite/loadflow/server/service/ReportService.java +++ b/src/main/java/org/gridsuite/loadflow/server/service/ReportService.java @@ -11,7 +11,6 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.reporter.Reporter; import com.powsybl.commons.reporter.ReporterModelJsonModule; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -40,7 +39,6 @@ public class ReportService { private ObjectMapper objectMapper; - @Autowired public ReportService(ObjectMapper objectMapper, @Value("${gridsuite.services.report-server.base-uri:http://report-server/}") String reportServerBaseUri, RestTemplate restTemplate) { diff --git a/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java b/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java new file mode 100644 index 00000000..6b550806 --- /dev/null +++ b/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, 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.loadflow.server.service.parameters; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersValues; +import org.gridsuite.loadflow.server.entities.parameters.LoadFlowParametersEntity; +import org.gridsuite.loadflow.server.repositories.parameters.LoadFlowParametersRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.powsybl.loadflow.LoadFlowParameters; + +/** + * @author Ayoub LABIDI + */ +@Service +public class LoadFlowParametersService { + + private final LoadFlowParametersRepository loadFlowParametersRepository; + + public LoadFlowParametersService(LoadFlowParametersRepository loadFlowParametersRepository) { + this.loadFlowParametersRepository = loadFlowParametersRepository; + } + + public UUID createParameters(LoadFlowParametersInfos parametersInfos) { + return loadFlowParametersRepository.save(parametersInfos.toEntity()).getId(); + } + + public Optional getParameters(UUID parametersUuid) { + return loadFlowParametersRepository.findById(parametersUuid).map(LoadFlowParametersEntity::toLoadFlowParametersInfos); + } + + public Optional getParametersValues(UUID parametersUuid, String provider) { + return loadFlowParametersRepository.findById(parametersUuid) + .map(entity -> entity.toLoadFlowParametersValues(provider)); + } + + public List getAllParameters() { + return loadFlowParametersRepository.findAll().stream().map(LoadFlowParametersEntity::toLoadFlowParametersInfos).toList(); + } + + @Transactional + public void updateParameters(UUID parametersUuid, LoadFlowParametersInfos parametersInfos) { + loadFlowParametersRepository.findById(parametersUuid).orElseThrow().update(parametersInfos); + } + + public void deleteParameters(UUID parametersUuid) { + loadFlowParametersRepository.deleteById(parametersUuid); + } + + @Transactional + public Optional duplicateParameters(UUID sourceParametersUuid) { + return loadFlowParametersRepository.findById(sourceParametersUuid) + .map(LoadFlowParametersEntity::copy) + .map(loadFlowParametersRepository::save) + .map(LoadFlowParametersEntity::getId); + } + + public UUID createDefaultParameters() { + //default parameters + LoadFlowParametersInfos defaultParametersInfos = LoadFlowParametersInfos.builder() + .commonParameters(LoadFlowParameters.load()) + .specificParametersPerProvider(Map.of()) + .build(); + return createParameters(defaultParametersInfos); + } +} diff --git a/src/main/resources/db/changelog/changesets/changelog_20240126T122251Z.xml b/src/main/resources/db/changelog/changesets/changelog_20240126T122251Z.xml new file mode 100644 index 00000000..f6533d1e --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20240126T122251Z.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 2fab3fa4..949e7781 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -9,3 +9,7 @@ databaseChangeLog: - include: file: changesets/changelog_20231115T10528Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20240126T122251Z.xml + relativeToChangelogFile: true + diff --git a/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java b/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java index 9eaf4696..dec4f923 100644 --- a/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java +++ b/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java @@ -28,12 +28,13 @@ import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.loadflow.server.dto.ComponentResult; import org.gridsuite.loadflow.server.dto.LimitViolationInfos; -import org.gridsuite.loadflow.server.dto.LoadFlowParametersInfos; import org.gridsuite.loadflow.server.dto.LoadFlowStatus; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersValues; import org.gridsuite.loadflow.server.service.LoadFlowWorkerService; import org.gridsuite.loadflow.server.service.LoadFlowExecutionService; import org.gridsuite.loadflow.server.service.ReportService; import org.gridsuite.loadflow.server.service.UuidGeneratorService; +import org.gridsuite.loadflow.server.service.parameters.LoadFlowParametersService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -45,6 +46,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.cloud.stream.binder.test.OutputDestination; import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; import org.springframework.http.MediaType; @@ -69,6 +71,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; 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; @@ -86,6 +89,7 @@ public class LoadFlowControllerTest { private static final UUID RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5d"); private static final UUID OTHER_RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5a"); private static final UUID REPORT_UUID = UUID.fromString("762b7298-8c0f-11ed-a1eb-0242ac120002"); + private static final UUID PARAMETERS_UUID = UUID.fromString("762b7298-8c0f-11ed-a1eb-0242ac120003"); private static final String VARIANT_1_ID = "variant_1"; private static final String VARIANT_2_ID = "variant_2"; @@ -123,6 +127,9 @@ private static final class LimitViolationsMock { @Autowired private LoadFlowExecutionService loadFlowExecutionService; + @SpyBean + private LoadFlowParametersService loadFlowParametersService; + @MockBean private UuidGeneratorService uuidGeneratorService; private final RestTemplateConfig restTemplateConfig = new RestTemplateConfig(); @@ -217,7 +224,7 @@ public void runTest() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -263,15 +270,14 @@ public void testGetLimitViolations() throws Exception { LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); loadFlowParameters.setDc(true); - LoadFlowParametersInfos loadFlowParametersInfos = LoadFlowParametersInfos.builder() + LoadFlowParametersValues loadFlowParametersInfos = LoadFlowParametersValues.builder() .commonParameters(loadFlowParameters) .specificParameters(Collections.emptyMap()) .build(); - String jsonLoadFlowParameters = mapper.writeValueAsString(loadFlowParametersInfos); + doReturn(Optional.of(loadFlowParametersInfos)).when(loadFlowParametersService).getParametersValues(any(), any()); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "&limitReduction=0.7", NETWORK_UUID) - .content(jsonLoadFlowParameters).contentType(MediaType.APPLICATION_JSON) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID + "&limitReduction=0.7", NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -304,7 +310,7 @@ public void testDeleteResults() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -342,7 +348,7 @@ public void stopTest() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -393,7 +399,7 @@ public void runWithReportTest() { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "user")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -412,7 +418,7 @@ public void runWithDefaultVariant() { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "user")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) diff --git a/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java b/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java new file mode 100644 index 00000000..d715a882 --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2024, 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.loadflow.server; + +import static org.gridsuite.loadflow.utils.assertions.Assertions.*; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.entities.parameters.LoadFlowParametersEntity; +import org.gridsuite.loadflow.server.repositories.parameters.LoadFlowParametersRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +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.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.http.MediaType; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.loadflow.LoadFlowParameters; + +/** + * @author Ayoub LABIDI + */ +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class LoadFlowParametersTest { + + private static final String URI_PARAMETERS_BASE = "/v1/parameters"; + + private static final String URI_PARAMETERS_GET_PUT = URI_PARAMETERS_BASE + "/"; + + @Autowired + MockMvc mockMvc; + + @Autowired + ObjectMapper mapper; + + @Autowired + LoadFlowParametersRepository parametersRepository; + + @AfterEach + public void clean() { + parametersRepository.deleteAll(); + } + + @Test + void testCreate() throws Exception { + + LoadFlowParametersInfos parametersToCreate = buildParameters(); + String parametersToCreateJson = mapper.writeValueAsString(parametersToCreate); + + mockMvc.perform(post(URI_PARAMETERS_BASE).content(parametersToCreateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + LoadFlowParametersInfos createdParameters = parametersRepository.findAll().get(0).toLoadFlowParametersInfos(); + + assertThat(createdParameters).recursivelyEquals(parametersToCreate); + } + + @Test + void testCreateWithDefaultValues() throws Exception { + LoadFlowParametersInfos defaultParameters = LoadFlowParametersInfos.builder() + .commonParameters(LoadFlowParameters.load()) + .specificParametersPerProvider(Map.of()) + .build(); + + mockMvc.perform(post(URI_PARAMETERS_BASE + "/default")) + .andExpect(status().isOk()).andReturn(); + + LoadFlowParametersInfos createdParameters = parametersRepository.findAll().get(0).toLoadFlowParametersInfos(); + + assertThat(createdParameters).recursivelyEquals(defaultParameters); + } + + @Test + void testRead() throws Exception { + + LoadFlowParametersInfos parametersToRead = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToRead); + + MvcResult mvcResult = mockMvc.perform(get(URI_PARAMETERS_GET_PUT + parametersUuid)) + .andExpect(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + LoadFlowParametersInfos receivedParameters = mapper.readValue(resultAsString, new TypeReference<>() { + }); + + assertThat(receivedParameters).recursivelyEquals(parametersToRead); + } + + @Test + void testUpdate() throws Exception { + + LoadFlowParametersInfos parametersToUpdate = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToUpdate); + + parametersToUpdate = buildParametersUpdate(); + + String parametersToUpdateJson = mapper.writeValueAsString(parametersToUpdate); + + mockMvc.perform(put(URI_PARAMETERS_GET_PUT + parametersUuid).content(parametersToUpdateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + LoadFlowParametersInfos updatedParameters = parametersRepository.findById(parametersUuid).get().toLoadFlowParametersInfos(); + + assertThat(updatedParameters).recursivelyEquals(parametersToUpdate); + } + + @Test + void testDelete() throws Exception { + + LoadFlowParametersInfos parametersToDelete = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToDelete); + + mockMvc.perform(delete(URI_PARAMETERS_GET_PUT + parametersUuid)).andExpect(status().isOk()).andReturn(); + + List storedParameters = parametersRepository.findAll(); + + assertThat(storedParameters).isEmpty(); + } + + @Test + void testDuplicate() throws Exception { + LoadFlowParametersInfos parametersToDuplicate = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToDuplicate); + + mockMvc.perform(post(URI_PARAMETERS_BASE + "/" + parametersUuid)) + .andExpect(status().isOk()).andReturn(); + + List storedParameters = parametersRepository.findAll(); + + assertThat(storedParameters).hasSize(2); + } + + @Test + void testGetWithInvalidId() throws Exception { + mockMvc.perform(get(URI_PARAMETERS_GET_PUT + UUID.randomUUID())) + .andExpect(status().isNotFound()).andReturn(); + } + + @Test + void testGetAll() throws Exception { + LoadFlowParametersInfos parameters1 = buildParameters(); + + LoadFlowParametersInfos parameters2 = buildParametersUpdate(); + + saveAndRetunId(parameters1); + + saveAndRetunId(parameters2); + + MvcResult mvcResult = mockMvc.perform(get(URI_PARAMETERS_BASE)) + .andExpect(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + List receivedParameters = mapper.readValue(resultAsString, new TypeReference<>() { + }); + + assertThat(receivedParameters).hasSize(2); + } + + /** Save parameters into the repository and return its UUID. */ + protected UUID saveAndRetunId(LoadFlowParametersInfos parametersInfos) { + parametersRepository.save(parametersInfos.toEntity()); + return parametersRepository.findAll().get(0).getId(); + } + + protected LoadFlowParametersInfos buildParameters() { + return LoadFlowParametersInfos.builder() + .commonParameters(LoadFlowParameters.load()) + .specificParametersPerProvider(Map.of()) + .build(); + } + + protected LoadFlowParametersInfos buildParametersUpdate() { + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setDc(true); + return LoadFlowParametersInfos.builder() + .commonParameters(loadFlowParameters) + .specificParametersPerProvider(Map.of()) + .build(); + } +} diff --git a/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java b/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java new file mode 100644 index 00000000..66f4bc7b --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2024, 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.loadflow.utils.assertions; + +import org.assertj.core.util.CheckReturnValue; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; + +/** + * @author Ayoub LABIDI + * {@link org.assertj.core.api.Assertions Assertions} completed with our custom assertions classes. + */ +public class Assertions extends org.assertj.core.api.Assertions { + @CheckReturnValue + public static DTOAssert assertThat(LoadFlowParametersInfos actual) { + return new DTOAssert<>(actual); + } +} diff --git a/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java b/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java new file mode 100644 index 00000000..98a7728d --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2024, 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.loadflow.utils.assertions; + +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; + +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.UUID; + +/** + * @author Ayoub LABIDI + */ +public class DTOAssert extends AbstractAssert, T> { + public DTOAssert(T actual) { + super(actual, DTOAssert.class); + } + + public DTOAssert recursivelyEquals(T other) { + isNotNull(); + usingRecursiveComparison(this.getRecursiveConfiguration()).isEqualTo(other); + return myself; + } + + private RecursiveComparisonConfiguration getRecursiveConfiguration() { + return RecursiveComparisonConfiguration.builder() + .withIgnoreAllOverriddenEquals(true) // For equals test, need specific tests + .withIgnoredFieldsOfTypes(UUID.class, Date.class, ZonedDateTime.class) // For these types, need specific tests (uuid from db for example) + .withIgnoreCollectionOrder(true) // For collection order test, need specific tests + .build(); + } +}