diff --git a/src/main/java/org/gridsuite/studyconfig/server/configuration/DiagramLayoutJacksonConfiguration.java b/src/main/java/org/gridsuite/studyconfig/server/configuration/DiagramLayoutJacksonConfiguration.java new file mode 100644 index 0000000..3690ca4 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/configuration/DiagramLayoutJacksonConfiguration.java @@ -0,0 +1,35 @@ +/** + * 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.studyconfig.server.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.AbstractDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.AbstractDiagramLayoutJsonMapper; +import org.springframework.context.annotation.Configuration; + +import jakarta.annotation.PostConstruct; + +/** + * Jackson configuration for diagram layout polymorphic serialization. + * This external configuration avoids circular dependencies between the abstract base class + * and its subtypes by using a mixin approach with type mappings. + */ +@Configuration +public class DiagramLayoutJacksonConfiguration { + + private final ObjectMapper objectMapper; + + public DiagramLayoutJacksonConfiguration(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @PostConstruct + public void configureDiagramLayoutMixIn() { + objectMapper.addMixIn(AbstractDiagramLayout.class, AbstractDiagramLayoutJsonMapper.class); + } +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/controller/DiagramGridLayoutController.java b/src/main/java/org/gridsuite/studyconfig/server/controller/DiagramGridLayoutController.java new file mode 100644 index 0000000..1bb4d63 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/controller/DiagramGridLayoutController.java @@ -0,0 +1,64 @@ +/** + * 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.studyconfig.server.controller; + +import io.swagger.v3.oas.annotations.Operation; +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 lombok.RequiredArgsConstructor; +import org.gridsuite.studyconfig.server.StudyConfigApi; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.DiagramGridLayout; +import org.gridsuite.studyconfig.server.service.DiagramGridLayoutService; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@RestController +@RequestMapping(value = "/" + StudyConfigApi.API_VERSION + "/diagram-grid-layout") +@RequiredArgsConstructor +@Tag(name = "Diagram Grid Layout Config", description = "Diagram Grid Layout Configuration API") +public class DiagramGridLayoutController { + private final DiagramGridLayoutService diagramGridLayoutService; + + @PostMapping(consumes = APPLICATION_JSON_VALUE) + @Operation(summary = "Save diagram grid layout") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The newly created diagram grid layout UUID returned")}) + public ResponseEntity saveDiagramGridLayout(@RequestBody DiagramGridLayout diagramGridLayout) { + return ResponseEntity.ok().body(diagramGridLayoutService.saveDiagramGridLayout(diagramGridLayout)); + } + + @PutMapping(value = "/{id}", consumes = APPLICATION_JSON_VALUE) + @Operation(summary = "Update diagram grid layout") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The diagram grid layout has been updated")}) + public ResponseEntity updateDiagramGridLayout(@PathVariable("id") UUID diagramGridLayoutUuid, + @RequestBody DiagramGridLayout diagramGridLayout) { + diagramGridLayoutService.updateDiagramGridLayout(diagramGridLayoutUuid, diagramGridLayout); + return ResponseEntity.ok().build(); + } + + @GetMapping(value = "/{id}", produces = APPLICATION_JSON_VALUE) + @Operation(summary = "Get diagram grid layout") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The diagram grid layout is returned")}) + public ResponseEntity getDiagramGridLayout( + @PathVariable("id") UUID diagramGridLayoutUuid) { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(diagramGridLayoutService.getByDiagramGridLayoutUuid(diagramGridLayoutUuid)); + } + + @DeleteMapping(value = "/{id}") + @Operation(summary = "Delete diagram grid layout") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The diagram grid layout is deleted")}) + public ResponseEntity deleteDiagramGridLayout( + @PathVariable("id") UUID diagramGridLayoutUuid) { + diagramGridLayoutService.deleteDiagramGridLayout(diagramGridLayoutUuid); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/DiagramGridLayout.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/DiagramGridLayout.java new file mode 100644 index 0000000..3bb148a --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/DiagramGridLayout.java @@ -0,0 +1,22 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout; + +import lombok.*; + +import java.util.List; + +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.AbstractDiagramLayout; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class DiagramGridLayout { + List diagramLayouts; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayout.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayout.java new file mode 100644 index 0000000..ea5cf1f --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayout.java @@ -0,0 +1,27 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.Map; +import java.util.UUID; + +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public abstract class AbstractDiagramLayout { + UUID diagramUuid; + + Map diagramPositions; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayoutJsonMapper.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayoutJsonMapper.java new file mode 100644 index 0000000..15d51fc --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/AbstractDiagramLayoutJsonMapper.java @@ -0,0 +1,25 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * MixIn interface for AbstractDiagramLayout to define polymorphic serialization + * without creating circular dependencies in the class hierarchy. + * This approach avoids circular dependencies that would occur with @JsonSubTypes + * directly on the abstract class. + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = SubstationDiagramLayout.class, name = "substation"), + @JsonSubTypes.Type(value = VoltageLevelDiagramLayout.class, name = "voltage-level"), + @JsonSubTypes.Type(value = NetworkAreaDiagramLayout.class, name = "network-area-diagram") +}) +public interface AbstractDiagramLayoutJsonMapper { +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/DiagramPosition.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/DiagramPosition.java new file mode 100644 index 0000000..f558604 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/DiagramPosition.java @@ -0,0 +1,21 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Builder +@AllArgsConstructor +@Getter +public class DiagramPosition { + Integer w; + Integer h; + Integer x; + Integer y; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayout.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayout.java new file mode 100644 index 0000000..5b74e1a --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayout.java @@ -0,0 +1,27 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class NetworkAreaDiagramLayout extends AbstractDiagramLayout { + UUID originalNadConfigUuid; + UUID currentNadConfigUuid; + UUID filterUuid; + String name; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/SubstationDiagramLayout.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/SubstationDiagramLayout.java new file mode 100644 index 0000000..bce50ae --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/SubstationDiagramLayout.java @@ -0,0 +1,22 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class SubstationDiagramLayout extends AbstractDiagramLayout { + String substationId; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/VoltageLevelDiagramLayout.java b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/VoltageLevelDiagramLayout.java new file mode 100644 index 0000000..13d33d4 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/diagramgridlayout/diagramlayout/VoltageLevelDiagramLayout.java @@ -0,0 +1,22 @@ +/** + * 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.studyconfig.server.dto.diagramgridlayout.diagramlayout; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class VoltageLevelDiagramLayout extends AbstractDiagramLayout { + String voltageLevelId; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutEntity.java new file mode 100644 index 0000000..fab5f63 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutEntity.java @@ -0,0 +1,36 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout; + +import jakarta.persistence.*; +import lombok.*; + +import java.util.List; +import java.util.UUID; + +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.AbstractDiagramLayoutEntity; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Getter +@Setter +public class DiagramGridLayoutEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + UUID uuid; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "diagram_grid_layout_id", foreignKey = @ForeignKey(name = "fk_diagram_layout_grid_layout")) + List diagramLayouts; + + public void replaceAllDiagramLayouts(List diagramLayouts) { + this.diagramLayouts.clear(); + this.diagramLayouts.addAll(diagramLayouts); + } +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutRepository.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutRepository.java new file mode 100644 index 0000000..ea7c9e0 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/DiagramGridLayoutRepository.java @@ -0,0 +1,16 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface DiagramGridLayoutRepository extends JpaRepository { +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/AbstractDiagramLayoutEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/AbstractDiagramLayoutEntity.java new file mode 100644 index 0000000..882f262 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/AbstractDiagramLayoutEntity.java @@ -0,0 +1,35 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout.diagramlayout; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Map; +import java.util.UUID; + +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +public abstract class AbstractDiagramLayoutEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + UUID diagramUuid; + + @ElementCollection + @CollectionTable(foreignKey = @ForeignKey(name = "fk_diagram_positions_abstract_diagram")) + @MapKeyColumn(name = "grid_layout_key") + Map diagramPositions; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/DiagramPositionEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/DiagramPositionEntity.java new file mode 100644 index 0000000..64a12b6 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/DiagramPositionEntity.java @@ -0,0 +1,25 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout.diagramlayout; + +import jakarta.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Embeddable +@Getter +public class DiagramPositionEntity { + Integer width; + Integer height; + Integer xPosition; + Integer yPosition; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayoutEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayoutEntity.java new file mode 100644 index 0000000..559c8b2 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/NetworkAreaDiagramLayoutEntity.java @@ -0,0 +1,35 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout.diagramlayout; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +@Entity +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Getter +@PrimaryKeyJoinColumn(foreignKey = @ForeignKey(name = "fk_network_area_diagram_layout_abstract")) +public class NetworkAreaDiagramLayoutEntity extends AbstractDiagramLayoutEntity { + @Column(name = "original_nad_config_uuid") + UUID originalNadConfigUuid; + + @Column(name = "current_nad_config_uuid") + UUID currentNadConfigUuid; + + @Column(name = "filter_uuid") + UUID filterUuid; + + @Column(name = "name") + String name; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/SubstationDiagramLayoutEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/SubstationDiagramLayoutEntity.java new file mode 100644 index 0000000..2f23ba1 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/SubstationDiagramLayoutEntity.java @@ -0,0 +1,23 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout.diagramlayout; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Entity +@Getter +@PrimaryKeyJoinColumn(foreignKey = @ForeignKey(name = "fk_substation_diagram_layout_abstract")) +public class SubstationDiagramLayoutEntity extends AbstractDiagramLayoutEntity { + String substationId; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/VoltageLevelLayoutEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/VoltageLevelLayoutEntity.java new file mode 100644 index 0000000..bb9390d --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/diagramgridlayout/diagramlayout/VoltageLevelLayoutEntity.java @@ -0,0 +1,23 @@ +/** + * 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.studyconfig.server.entities.diagramgridlayout.diagramlayout; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Entity +@Getter +@PrimaryKeyJoinColumn(foreignKey = @ForeignKey(name = "fk_voltage_level_layout_abstract")) +public class VoltageLevelLayoutEntity extends AbstractDiagramLayoutEntity { + String voltageLevelId; +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/mapper/DiagramGridLayoutMapper.java b/src/main/java/org/gridsuite/studyconfig/server/mapper/DiagramGridLayoutMapper.java new file mode 100644 index 0000000..eba37d3 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/mapper/DiagramGridLayoutMapper.java @@ -0,0 +1,136 @@ +/* + 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.studyconfig.server.mapper; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.DiagramGridLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.AbstractDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.DiagramPosition; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.NetworkAreaDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.SubstationDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.VoltageLevelDiagramLayout; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.DiagramGridLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.AbstractDiagramLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.DiagramPositionEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.NetworkAreaDiagramLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.SubstationDiagramLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.diagramlayout.VoltageLevelLayoutEntity; + +public final class DiagramGridLayoutMapper { + + private DiagramGridLayoutMapper() { + } + + public static DiagramGridLayoutEntity toEntity(DiagramGridLayout dto) { + return Optional.ofNullable(dto) + .map(d -> DiagramGridLayoutEntity.builder() + .diagramLayouts(convertDiagramLayouts(d.getDiagramLayouts(), DiagramGridLayoutMapper::toDiagramLayoutEntity)) + .build()) + .orElse(null); + } + + public static DiagramGridLayout toDto(DiagramGridLayoutEntity entity) { + return Optional.ofNullable(entity) + .map(e -> DiagramGridLayout.builder() + .diagramLayouts(convertDiagramLayouts(e.getDiagramLayouts(), DiagramGridLayoutMapper::toDiagramLayoutDto)) + .build()) + .orElse(null); + } + + public static AbstractDiagramLayoutEntity toDiagramLayoutEntity(AbstractDiagramLayout dto) { + return switch (dto) { + case SubstationDiagramLayout s -> SubstationDiagramLayoutEntity.builder() + .diagramUuid(s.getDiagramUuid()) + .substationId(s.getSubstationId()) + .diagramPositions(convertDiagramPositionsMap(s.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionEntity)) + .build(); + case VoltageLevelDiagramLayout v -> VoltageLevelLayoutEntity.builder() + .diagramUuid(v.getDiagramUuid()) + .voltageLevelId(v.getVoltageLevelId()) + .diagramPositions(convertDiagramPositionsMap(v.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionEntity)) + .build(); + case NetworkAreaDiagramLayout v -> NetworkAreaDiagramLayoutEntity.builder() + .diagramUuid(v.getDiagramUuid()) + .originalNadConfigUuid(v.getOriginalNadConfigUuid()) + .currentNadConfigUuid(v.getCurrentNadConfigUuid()) + .filterUuid(v.getFilterUuid()) + .name(v.getName()) + .diagramPositions(convertDiagramPositionsMap(v.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionEntity)) + .build(); + case null -> null; + default -> throw new IllegalArgumentException("Unknown diagram layout DTO type: " + dto.getClass()); + }; + } + + public static AbstractDiagramLayout toDiagramLayoutDto(AbstractDiagramLayoutEntity entity) { + return switch (entity) { + case SubstationDiagramLayoutEntity s -> SubstationDiagramLayout.builder() + .diagramUuid(s.getDiagramUuid()) + .substationId(s.getSubstationId()) + .diagramPositions(convertDiagramPositionsMap(s.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionDto)) + .build(); + case VoltageLevelLayoutEntity v -> VoltageLevelDiagramLayout.builder() + .diagramUuid(v.getDiagramUuid()) + .voltageLevelId(v.getVoltageLevelId()) + .diagramPositions(convertDiagramPositionsMap(v.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionDto)) + .build(); + case NetworkAreaDiagramLayoutEntity n -> NetworkAreaDiagramLayout.builder() + .diagramUuid(n.getDiagramUuid()) + .originalNadConfigUuid(n.getOriginalNadConfigUuid()) + .currentNadConfigUuid(n.getCurrentNadConfigUuid()) + .filterUuid(n.getFilterUuid()) + .name(n.getName()) + .diagramPositions(convertDiagramPositionsMap(n.getDiagramPositions(), DiagramGridLayoutMapper::toDiagramPositionDto)) + .build(); + case null -> null; + default -> throw new IllegalArgumentException("Unknown diagram layout entity type: " + entity.getClass()); + }; + } + + public static DiagramPositionEntity toDiagramPositionEntity(DiagramPosition dto) { + return Optional.ofNullable(dto) + .map(d -> DiagramPositionEntity.builder() + .xPosition(d.getX()) + .yPosition(d.getY()) + .width(d.getW()) + .height(d.getH()) + .build()) + .orElse(null); + } + + public static DiagramPosition toDiagramPositionDto(DiagramPositionEntity entity) { + return Optional.ofNullable(entity) + .map(e -> DiagramPosition.builder() + .x(e.getXPosition()) + .y(e.getYPosition()) + .w(e.getWidth()) + .h(e.getHeight()) + .build()) + .orElse(null); + } + + private static List convertDiagramLayouts(List items, Function converter) { + return Optional.ofNullable(items) + .map(list -> list.stream().map(converter).toList()) + .orElse(List.of()); + } + + private static Map convertDiagramPositionsMap(Map sourceMap, Function converter) { + return Optional.ofNullable(sourceMap) + .map(map -> map.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> converter.apply(entry.getValue()) + ))) + .orElse(null); + } +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/service/DiagramGridLayoutService.java b/src/main/java/org/gridsuite/studyconfig/server/service/DiagramGridLayoutService.java new file mode 100644 index 0000000..6530f98 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/service/DiagramGridLayoutService.java @@ -0,0 +1,59 @@ +/** + * 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.studyconfig.server.service; + +import jakarta.persistence.EntityNotFoundException; + +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.DiagramGridLayout; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.DiagramGridLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.DiagramGridLayoutRepository; +import org.gridsuite.studyconfig.server.mapper.DiagramGridLayoutMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +@Service +public class DiagramGridLayoutService { + private final DiagramGridLayoutRepository diagramGridLayoutRepository; + + public DiagramGridLayoutService(DiagramGridLayoutRepository diagramGridLayoutRepository) { + this.diagramGridLayoutRepository = diagramGridLayoutRepository; + } + + @Transactional(readOnly = true) + public DiagramGridLayout getByDiagramGridLayoutUuid(UUID diagramGridLayoutUuid) { + DiagramGridLayoutEntity entity = diagramGridLayoutRepository + .findById(diagramGridLayoutUuid) + .orElseThrow(() -> new EntityNotFoundException("Diagram grid layout not found with id: " + diagramGridLayoutUuid)); + + return DiagramGridLayoutMapper.toDto(entity); + } + + @Transactional + public void deleteDiagramGridLayout(UUID diagramGridLayoutUuid) { + diagramGridLayoutRepository.deleteById(diagramGridLayoutUuid); + } + + @Transactional + public UUID saveDiagramGridLayout(DiagramGridLayout diagramGridLayout) { + DiagramGridLayoutEntity diagramGridLayoutEntity = diagramGridLayoutRepository.save(DiagramGridLayoutMapper.toEntity(diagramGridLayout)); + + return diagramGridLayoutEntity.getUuid(); + } + + @Transactional + public void updateDiagramGridLayout(UUID diagramGridLayoutUuid, DiagramGridLayout diagramGridLayout) { + DiagramGridLayoutEntity diagramGridLayoutEntity = diagramGridLayoutRepository.findById(diagramGridLayoutUuid).orElseThrow(() -> new EntityNotFoundException("Diagram grid layout not found with id: " + diagramGridLayoutUuid)); + + diagramGridLayoutEntity.replaceAllDiagramLayouts(diagramGridLayout.getDiagramLayouts().stream() + .map(DiagramGridLayoutMapper::toDiagramLayoutEntity) + .toList()); + + diagramGridLayoutRepository.save(diagramGridLayoutEntity); + } +} diff --git a/src/main/resources/db/changelog/changesets/changelog_20250722T151152Z.xml b/src/main/resources/db/changelog/changesets/changelog_20250722T151152Z.xml new file mode 100644 index 0000000..914bf78 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20250722T151152Z.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 5f2c487..3752796 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -62,3 +62,6 @@ databaseChangeLog: - include: file: changesets/changelog_20250619T162423Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20250722T151152Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/studyconfig/server/DiagramGridLayoutControllerTest.java b/src/test/java/org/gridsuite/studyconfig/server/DiagramGridLayoutControllerTest.java new file mode 100644 index 0000000..6a1cda3 --- /dev/null +++ b/src/test/java/org/gridsuite/studyconfig/server/DiagramGridLayoutControllerTest.java @@ -0,0 +1,152 @@ +/** + * 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.studyconfig.server; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.DiagramGridLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.DiagramPosition; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.NetworkAreaDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.SubstationDiagramLayout; +import org.gridsuite.studyconfig.server.dto.diagramgridlayout.diagramlayout.VoltageLevelDiagramLayout; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.DiagramGridLayoutEntity; +import org.gridsuite.studyconfig.server.entities.diagramgridlayout.DiagramGridLayoutRepository; +import org.gridsuite.studyconfig.server.mapper.DiagramGridLayoutMapper; +import org.gridsuite.studyconfig.server.service.DiagramGridLayoutService; +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.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@AutoConfigureMockMvc +class DiagramGridLayoutControllerTest { + @Autowired + MockMvc mockMvc; + @Autowired + private DiagramGridLayoutRepository diagramGridLayoutRepository; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private DiagramGridLayoutService diagramGridLayoutService; + + @Test + void testGetDiagramGridLayout() throws Exception { + DiagramGridLayoutEntity expectedResult = diagramGridLayoutRepository.save(DiagramGridLayoutMapper.toEntity(createDiagramGridLayout())); + MvcResult mockMvcResult = mockMvc.perform(get("/v1/diagram-grid-layout/{diagramGridLayoutUuid}", expectedResult.getUuid())) + .andExpect(status().isOk()) + .andReturn(); + + DiagramGridLayout result = objectMapper.readValue(mockMvcResult.getResponse().getContentAsString(), DiagramGridLayout.class); + assertThat(result).usingRecursiveComparison().isEqualTo(DiagramGridLayoutMapper.toDto(expectedResult)); + } + + @Test + void testDeleteDiagramGridLayout() throws Exception { + DiagramGridLayoutEntity expectedResult = diagramGridLayoutRepository.save(DiagramGridLayoutMapper.toEntity(createDiagramGridLayout())); + mockMvc.perform(delete("/v1/diagram-grid-layout/{diagramGridLayoutUuid}", expectedResult.getUuid())) + .andExpect(status().isOk()); + + assertTrue(diagramGridLayoutRepository.findById(expectedResult.getUuid()).isEmpty()); + } + + @Test + void testSaveDiagramGridLayout() throws Exception { + DiagramGridLayout diagramGridLayoutToSave = createDiagramGridLayout(); + + MvcResult mockMvcResult = mockMvc.perform(post("/v1/diagram-grid-layout") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(diagramGridLayoutToSave))) + .andExpect(status().isOk()) + .andReturn(); + + UUID diagramGridLayoutUUID = objectMapper.readValue(mockMvcResult.getResponse().getContentAsString(), UUID.class); + + DiagramGridLayout diagramGridLayoutToCheck = diagramGridLayoutService.getByDiagramGridLayoutUuid(diagramGridLayoutUUID); + assertThat(diagramGridLayoutToCheck).usingRecursiveComparison().isEqualTo(diagramGridLayoutToSave); + } + + @Test + void testUpdateDiagramGridLayout() throws Exception { + DiagramGridLayoutEntity existingDiagramGridLayout = diagramGridLayoutRepository.save(DiagramGridLayoutMapper.toEntity(createDiagramGridLayout())); + + UUID newDiagramLayoutUuid = UUID.randomUUID(); + DiagramGridLayout updatedDiagramGridLayout = createDiagramGridLayout(); + updatedDiagramGridLayout.getDiagramLayouts().add(SubstationDiagramLayout.builder() + .substationId("s1") + .diagramPositions(Map.of( + "lg", + DiagramPosition.builder().w(5) + .h(6) + .x(7) + .y(8) + .build() + )) + + .diagramUuid(newDiagramLayoutUuid) + .build()); + + updatedDiagramGridLayout.getDiagramLayouts().add(NetworkAreaDiagramLayout.builder() + .diagramUuid(UUID.randomUUID()) + .diagramPositions(Map.of( + "lg", + DiagramPosition.builder() + .w(10) + .h(20) + .x(30) + .y(40) + .build() + )) + .originalNadConfigUuid(UUID.randomUUID()) + .currentNadConfigUuid(UUID.randomUUID()) + .filterUuid(UUID.randomUUID()) + .name("Network Area Layout") + .build()); + + mockMvc.perform(put("/v1/diagram-grid-layout/{diagramLayoutUuid}", existingDiagramGridLayout.getUuid()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updatedDiagramGridLayout))) + .andExpect(status().isOk()) + .andReturn(); + + DiagramGridLayout diagramGridLayoutToCheck = diagramGridLayoutService.getByDiagramGridLayoutUuid(existingDiagramGridLayout.getUuid()); + assertThat(diagramGridLayoutToCheck).usingRecursiveComparison().isEqualTo(updatedDiagramGridLayout); + } + + private DiagramGridLayout createDiagramGridLayout() { + UUID diagramLayoutUuid = UUID.randomUUID(); + return DiagramGridLayout.builder() + .diagramLayouts(new ArrayList<>(List.of( + VoltageLevelDiagramLayout.builder() + .voltageLevelId("vl1") + .diagramPositions(Map.of( + "lg", + DiagramPosition.builder().w(1) + .h(2) + .x(3) + .y(4) + .build() + )) + + .diagramUuid(diagramLayoutUuid) + .build()))) + .build(); + } +}