Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an endpoint to create modifications without applying them and an endpoint to apply modifications on multiple network contexts #582

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.gridsuite.modification.server.dto.catalog.LineTypeInfos;
import org.gridsuite.modification.server.service.LineTypesCatalogService;
import org.gridsuite.modification.server.service.NetworkModificationService;
import org.springframework.data.util.Pair;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -71,13 +72,42 @@ public ResponseEntity<Integer> getNetworkModificationsCount(@Parameter(descripti
@PostMapping(value = "/groups")
@Operation(summary = "Create a modification group based on another group")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The group and its modifications have been duplicated")})
public ResponseEntity<Void> createModificationGroup(@RequestParam("groupUuid") UUID groupUuid,
@RequestParam("duplicateFrom") UUID sourceGroupUuid) {
networkModificationService.createModificationGroup(sourceGroupUuid, groupUuid);
public ResponseEntity<Void> duplicateGroup(@RequestParam("groupUuid") UUID groupUuid,
@RequestParam("duplicateFrom") UUID sourceGroupUuid) {
networkModificationService.duplicateGroup(sourceGroupUuid, groupUuid);
return ResponseEntity.ok().build();
}

@PutMapping(value = "/groups/{groupUuid}", produces = MediaType.APPLICATION_JSON_VALUE)
@PutMapping(value = "/groups/{groupUuid}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "For a list of network modifications passed in body, Move them before another one or at the end of the list, or Duplicate them at the end of the list")
@ApiResponse(responseCode = "200", description = "The modification list of the group has been updated.")
public ResponseEntity<List<Optional<NetworkModificationResult>>> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
@Parameter(description = "kind of modification", required = true) @RequestParam(value = "action") GroupModificationAction action,
@Parameter(description = "the modification Uuid to move before (MOVE option, empty means moving at the end)") @RequestParam(value = "before", required = false) UUID beforeModificationUuid,
@Parameter(description = "origin group UUID, where modifications are copied or cut") @RequestParam(value = "originGroupUuid", required = false) UUID originGroupUuid,
@Parameter(description = "destination node can be built (default is true)") @RequestParam(value = "build", required = false, defaultValue = "true") Boolean build,
@RequestBody Pair<List<UUID>, List<ModificationApplicationContext>> modificationContextInfos) {
return switch (action) {
case COPY ->
ResponseEntity.ok().body(networkModificationService.duplicateModifications(targetGroupUuid, originGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
case INSERT ->
ResponseEntity.ok().body(networkModificationService.insertCompositeModifications(targetGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
case MOVE -> {
UUID sourceGroupUuid = originGroupUuid == null ? targetGroupUuid : originGroupUuid;
boolean canBuildNode = build;
if (sourceGroupUuid.equals(targetGroupUuid)) {
canBuildNode = false;
}
yield ResponseEntity.ok().body(networkModificationService.moveModifications(targetGroupUuid, sourceGroupUuid, beforeModificationUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond(), canBuildNode));
}
};
}

/**
* TODO : Remove this endpoint after the final integration of root networks (used only for move and tests)
* Need to use the new endpoint with modificationContextInfos DTO (see above)
*/
@PutMapping(value = "/groups/{groupUuid}", params = {"networkUuid", "reportUuid", "reporterId"}, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "For a list of network modifications passed in body, Move them before another one or at the end of the list, or Duplicate them at the end of the list")
@ApiResponse(responseCode = "200", description = "The modification list of the group has been updated.")
public ResponseEntity<Optional<NetworkModificationResult>> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
Expand Down Expand Up @@ -123,18 +153,34 @@ public ResponseEntity<List<UUID>> getModificationGroups() {
return ResponseEntity.ok().body(networkModificationService.getModificationGroups());
}

@PostMapping(value = "/network-modifications", params = "groupUuid", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create a network modification")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The network modification was created"),
@ApiResponse(responseCode = "404", description = "The network or equipment was not found")})
public ResponseEntity<List<Optional<NetworkModificationResult>>> createNetworkModification(
@Parameter(description = "Group UUID") @RequestParam(name = "groupUuid") UUID groupUuid,
@RequestBody Pair<ModificationInfos, List<ModificationApplicationContext>> modificationContextInfos) {
modificationContextInfos.getFirst().check();
return ResponseEntity.ok().body(networkModificationService.createNetworkModification(groupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
}

/**
* TODO : Remove this endpoint after the final integration of root networks (all tests need to migrate before)
* Need to use tne new endpoint with modificationContextInfos DTO (see above)
*/
@PostMapping(value = "/network-modifications", params = {"networkUuid", "reportUuid", "reporterId"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create a network modification")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The network modification was created"),
@ApiResponse(responseCode = "404", description = "The network or equipment was not found")})
public ResponseEntity<Optional<NetworkModificationResult>> createNetworkModification(
@Parameter(description = "Network UUID") @RequestParam("networkUuid") UUID networkUuid,
@Parameter(description = "Variant ID") @RequestParam(name = "variantId", required = false) String variantId,
@Parameter(description = "Group UUID") @RequestParam(name = "groupUuid", required = false) UUID groupUuid,
@Parameter(description = "Report UUID") @RequestParam("reportUuid") UUID reportUuid,
@Parameter(description = "Reporter ID") @RequestParam("reporterId") String reporterId,
@RequestBody ModificationInfos modificationInfos) {
@Parameter(description = "Network UUID") @RequestParam("networkUuid") UUID networkUuid,
@Parameter(description = "Variant ID") @RequestParam(name = "variantId", required = false) String variantId,
@Parameter(description = "Group UUID") @RequestParam(name = "groupUuid", required = false) UUID groupUuid,
@Parameter(description = "Report UUID") @RequestParam("reportUuid") UUID reportUuid,
@Parameter(description = "Reporter ID") @RequestParam("reporterId") String reporterId,
@RequestBody ModificationInfos modificationInfos) {
modificationInfos.check();
return ResponseEntity.ok().body(networkModificationService.createNetworkModification(networkUuid, variantId, groupUuid, new ReportInfos(reportUuid, UUID.fromString(reporterId)), modificationInfos));
}
Expand Down Expand Up @@ -208,13 +254,6 @@ public ResponseEntity<Void> deleteLineTypesCatalog() {
return ResponseEntity.ok().build();
}

@PostMapping(value = "/groups/modification", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create a group containing a modification")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The group with the modification has been created")})
public ResponseEntity<UUID> createModificationInGroup(@RequestBody ModificationInfos modificationsInfos) {
return ResponseEntity.ok().body(networkModificationService.createModificationInGroup(modificationsInfos));
}

@PostMapping(value = "/network-composite-modifications", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create a network composite modification")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The composite modification has been created")})
Expand Down Expand Up @@ -267,18 +306,6 @@ public ResponseEntity<Void> updateNetworkModificationsActivationStatus(
return ResponseEntity.ok().build();
}

@PutMapping(value = "/groups/{groupUuid}/duplications", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Duplicate all modifications in a group and append them at the end of another modifications group")
@ApiResponse(responseCode = "200", description = "The modifications have been duplicated")
public ResponseEntity<Optional<NetworkModificationResult>> duplicateModificationsInGroup(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
@Parameter(description = "the network uuid", required = true) @RequestParam(value = "networkUuid") UUID networkUuid,
@Parameter(description = "the report uuid", required = true) @RequestParam(value = "reportUuid") UUID reportUuid,
@Parameter(description = "the reporter id", required = true) @RequestParam(value = "reporterId") UUID reporterId,
@Parameter(description = "the variant id", required = true) @RequestParam(value = "variantId") String variantId,
@Parameter(description = "origin group UUID, from where modifications are copied") @RequestParam(value = "duplicateFrom") UUID originGroupUuid) {
return ResponseEntity.ok().body(networkModificationService.duplicateModificationsInGroup(targetGroupUuid, networkUuid, variantId, new ReportInfos(reportUuid, reporterId), originGroupUuid));
}

@DeleteMapping(value = "/groups/{groupUuid}/stashed-modifications")
@Operation(summary = "Delete the stashed modifications in a group")
@ApiResponse(responseCode = "200", description = "Stashed modifications in the group deleted")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright (c) 2025, RTE (http://www.rte-france.com)
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.modification.server;

import com.powsybl.commons.PowsyblException;
import lombok.Getter;
import org.springframework.http.HttpStatus;

import java.util.Objects;

/**
* @author Slimane Amar <slimane.amar at rte-france.com>
*/
public class NetworkModificationServerException extends PowsyblException {
public enum Type {
DUPLICATION_ARGUMENT_INVALID(HttpStatus.BAD_REQUEST, "Invalid argument for duplication");

public final HttpStatus status;
private final String message;

Type(HttpStatus status, String message) {
this.status = status;
this.message = message;
}
}

@Getter
private final Type type;

public NetworkModificationServerException(Type type) {
super(Objects.requireNonNull(type.name()) + ((type.message == null) ? "" : " : " + type.message));
this.type = type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.gridsuite.modification.NetworkModificationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -20,14 +21,24 @@
public class RestResponseEntityExceptionHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
private static final String HANDLER_MESSAGE = "Caught in handler";

@ExceptionHandler(NetworkModificationException.class)
protected ResponseEntity<Object> handleNetworkModificationException(NetworkModificationException exception) {
protected ResponseEntity<Object> handleException(NetworkModificationException exception) {
return handleException(exception.getType().status, exception);
}

@ExceptionHandler(NetworkModificationServerException.class)
protected ResponseEntity<Object> handleException(NetworkModificationServerException exception) {
return handleException(exception.getType().status, exception);
}

private ResponseEntity<Object> handleException(HttpStatus status, Exception exception) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error(exception.getMessage());
LOGGER.error(HANDLER_MESSAGE, exception);
}
return ResponseEntity
.status(exception.getType().status)
.body(exception.getMessage());
.status(status)
.body(exception.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* 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.modification.server.dto;

import java.util.UUID;

public record ModificationApplicationContext(UUID networkUuid, String variantId, UUID reportUuid, UUID reporterId) { }
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,16 @@ public void deleteAll() {
// This method should be package-private and not used as API of the service as it uses ModificationEntity and
// we want to encapsulate the use of Entity related objects to this service.
// Nevertheless We have to keep it public for transactional annotation.
public void saveModifications(UUID groupUuid, List<? extends ModificationEntity> modifications) {
saveModificationsNonTransactional(groupUuid, modifications);
public List<ModificationEntity> saveModifications(UUID groupUuid, List<ModificationEntity> modifications) {
return saveModificationsNonTransactional(groupUuid, modifications);
}

@Transactional // To have all create in the same transaction (atomic)
// TODO Remove transaction when errors will no longer be sent to the front
public void saveModificationInfos(UUID groupUuid, List<? extends ModificationInfos> modifications) {
public List<ModificationEntity> saveModificationInfos(UUID groupUuid, List<ModificationInfos> modifications) {
List<ModificationEntity> entities = modifications.stream().map(ModificationEntity::fromDTO).toList();

saveModificationsNonTransactional(groupUuid, entities);
return saveModificationsNonTransactional(groupUuid, entities);
}

public UUID createNetworkCompositeModification(@NonNull List<UUID> modificationUuids) {
Expand All @@ -85,7 +85,7 @@ public UUID createNetworkCompositeModification(@NonNull List<UUID> modificationU
return modificationRepository.save(compositeEntity).getId();
}

private void saveModificationsNonTransactional(UUID groupUuid, List<? extends ModificationEntity> modifications) {
private List<ModificationEntity> saveModificationsNonTransactional(UUID groupUuid, List<ModificationEntity> modifications) {
int order = modificationRepository.countByGroupIdAndStashed(groupUuid, false);
var modificationGroupEntity = this.modificationGroupRepository
.findById(groupUuid)
Expand All @@ -95,7 +95,7 @@ private void saveModificationsNonTransactional(UUID groupUuid, List<? extends Mo
m.setModificationsOrder(order);
order++;
}
modificationRepository.saveAll(modifications);
return modificationRepository.saveAll(modifications);
}

@Transactional
Expand Down
Loading
Loading