Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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.study.server.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.gridsuite.study.server.StudyApi;
import org.gridsuite.study.server.service.WorkspaceService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping(value = "/" + StudyApi.API_VERSION + "/studies/{studyUuid}/workspaces")
@Tag(name = "Study server - Workspaces")
public class WorkspaceController {
private final WorkspaceService workspaceService;

public WorkspaceController(WorkspaceService workspaceService) {
this.workspaceService = workspaceService;
}

@GetMapping("")
@Operation(summary = "Get workspaces metadata")
@ApiResponse(responseCode = "200", description = "Workspaces metadata retrieved")
@ApiResponse(responseCode = "404", description = "Study not found")
public ResponseEntity<String> getWorkspaces(
@PathVariable("studyUuid") UUID studyUuid) {
return ResponseEntity.ok(workspaceService.getWorkspaces(studyUuid));
}

@GetMapping("/{workspaceId}")
@Operation(summary = "Get a specific workspace")
@ApiResponse(responseCode = "200", description = "Workspace retrieved")
@ApiResponse(responseCode = "404", description = "Study or workspace not found")
public ResponseEntity<String> getWorkspace(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable UUID workspaceId) {
return ResponseEntity.ok(workspaceService.getWorkspace(studyUuid, workspaceId));
}

@PutMapping("/{workspaceId}/name")
@Operation(summary = "Rename a workspace")
@ApiResponse(responseCode = "204", description = "Workspace renamed")
@ApiResponse(responseCode = "404", description = "Study or workspace not found")
public ResponseEntity<Void> renameWorkspace(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable UUID workspaceId,
@RequestBody String name) {
workspaceService.renameWorkspace(studyUuid, workspaceId, name);
return ResponseEntity.noContent().build();
}

@GetMapping("/{workspaceId}/panels")
@Operation(summary = "Get panels from a workspace")
@ApiResponse(responseCode = "200", description = "Panels retrieved")
@ApiResponse(responseCode = "404", description = "Study or workspace not found")
public ResponseEntity<String> getPanels(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable UUID workspaceId,
@RequestParam(required = false) List<String> ids) {
return ResponseEntity.ok(workspaceService.getWorkspacePanels(studyUuid, workspaceId, ids));
}

@PostMapping("/{workspaceId}/panels")
@Operation(summary = "Create or update panels in a workspace")
@ApiResponse(responseCode = "204", description = "Panels created or updated")
@ApiResponse(responseCode = "404", description = "Study or workspace not found")
public ResponseEntity<Void> createOrUpdatePanels(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable UUID workspaceId,
@RequestBody String panelsDto) {
workspaceService.createOrUpdateWorkspacePanels(studyUuid, workspaceId, panelsDto);
return ResponseEntity.noContent().build();
}

@DeleteMapping("/{workspaceId}/panels")
@Operation(summary = "Delete panels from a workspace")
@ApiResponse(responseCode = "204", description = "Panels deleted")
@ApiResponse(responseCode = "404", description = "Study or workspace not found")
public ResponseEntity<Void> deletePanels(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable UUID workspaceId,
@RequestBody String panelIds) {
workspaceService.deleteWorkspacePanels(studyUuid, workspaceId, panelIds);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class NotificationService {
public static final String HEADER_MODIFICATION_DATE = "modificationDate";
public static final String HEADER_ELEMENT_UUID = "elementUuid";
public static final String HEADER_EXPORT_UUID = "exportUuid";
public static final String HEADER_WORKSPACE_UUID = "workspaceUuid";
public static final String NETWORK_EXPORT_FINISHED = "networkExportFinished";

public static final String UPDATE_TYPE_BUILD_CANCELLED = "buildCancelled";
Expand Down Expand Up @@ -96,6 +97,10 @@ public class NotificationService {
public static final String UPDATE_SPREADSHEET_TAB = "spreadsheetTabUpdated";
public static final String UPDATE_SPREADSHEET_COLLECTION = "spreadsheetCollectionUpdated";
public static final String UPDATE_SPREADSHEET_PARAMETERS = "spreadsheetParametersUpdated";
public static final String UPDATE_WORKSPACE_RENAMED = "workspaceRenamed";
public static final String UPDATE_WORKSPACE_PANELS = "workspacePanelsUpdated";
public static final String DELETE_WORKSPACE_PANELS = "workspacePanelsDeleted";
public static final String UPDATE_WORKSPACE_NAD_CONFIG = "workspaceNadConfigUpdated";

public static final String MODIFICATIONS_CREATING_IN_PROGRESS = "creatingInProgress";
public static final String MODIFICATIONS_STASHING_IN_PROGRESS = "stashingInProgress";
Expand Down Expand Up @@ -204,6 +209,28 @@ public void emitSpreadsheetCollectionChanged(UUID studyUuid, UUID collectionUuid
sendStudyUpdateMessage(studyUuid, UPDATE_SPREADSHEET_COLLECTION, MessageBuilder.withPayload(collectionUuid.toString()));
}

@PostCompletion
public void emitWorkspaceUpdated(UUID studyUuid, UUID workspaceId) {
sendStudyUpdateMessage(studyUuid, UPDATE_WORKSPACE_RENAMED, MessageBuilder.withPayload(workspaceId.toString()));
}

@PostCompletion
public void emitWorkspacePanelsUpdated(UUID studyUuid, UUID workspaceId, String panelIds) {
sendStudyUpdateMessage(studyUuid, UPDATE_WORKSPACE_PANELS, MessageBuilder.withPayload(panelIds)
.setHeader(HEADER_WORKSPACE_UUID, workspaceId.toString()));
}

@PostCompletion
public void emitWorkspacePanelsDeleted(UUID studyUuid, UUID workspaceId, String panelIds) {
sendStudyUpdateMessage(studyUuid, DELETE_WORKSPACE_PANELS, MessageBuilder.withPayload(panelIds)
.setHeader(HEADER_WORKSPACE_UUID, workspaceId.toString()));
}

@PostCompletion
public void emitWorkspaceNadConfigUpdated(UUID studyUuid, UUID workspaceNadConfigUuid) {
sendStudyUpdateMessage(studyUuid, UPDATE_WORKSPACE_NAD_CONFIG, MessageBuilder.withPayload(workspaceNadConfigUuid.toString()));
}

@PostCompletion
public void emitSpreadsheetParametersChange(UUID studyUuid) {
sendStudyUpdateMessage(studyUuid, UPDATE_SPREADSHEET_PARAMETERS, MessageBuilder.withPayload(""));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ public class StudyEntity extends AbstractManuallyAssignedIdentifierEntity<UUID>
@Column(name = "diagramGridLayoutUuid")
private UUID diagramGridLayoutUuid;

@Column(name = "workspacesConfigUuid")
private UUID workspacesConfigUuid;

@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "study_voltage_init_parameters_id",
foreignKey = @ForeignKey(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,12 @@ private void insertStudy(UUID studyUuid, String userId, NetworkInfos networkInfo
UUID pccMinParametersUuid = createDefaultPccMinParameters();
UUID spreadsheetConfigCollectionUuid = createDefaultSpreadsheetConfigCollection(userId, userProfileInfos);
UUID diagramGridLayoutUuid = studyService.createGridLayoutFromNadDiagram(userId, userProfileInfos);
UUID workspacesConfigUuid = createDefaultWorkspacesConfig();

studyService.insertStudy(studyUuid, userId, networkInfos, caseInfos, loadFlowParametersUuid,
shortCircuitParametersUuid, DynamicSimulationService.toEntity(dynamicSimulationParameters, objectMapper),
voltageInitParametersUuid, securityAnalysisParametersUuid, sensitivityAnalysisParametersUuid,
networkVisualizationParametersUuid, dynamicSecurityAnalysisParametersUuid, stateEstimationParametersUuid, pccMinParametersUuid, spreadsheetConfigCollectionUuid, diagramGridLayoutUuid,
networkVisualizationParametersUuid, dynamicSecurityAnalysisParametersUuid, stateEstimationParametersUuid, pccMinParametersUuid, spreadsheetConfigCollectionUuid, diagramGridLayoutUuid, workspacesConfigUuid,
importParameters, importReportUuid);
}

Expand Down Expand Up @@ -480,6 +481,15 @@ private UUID createDefaultSpreadsheetConfigCollection(String userId, UserProfile
}
}

private UUID createDefaultWorkspacesConfig() {
try {
return studyConfigService.createDefaultWorkspacesConfig();
} catch (final Exception e) {
LOGGER.error("Error while creating default workspace collection", e);
return null;
}
}

@Bean
public Consumer<Message<String>> consumeCaseImportFailed() {
return message -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.gridsuite.study.server.dto.diagramgridlayout.nad.NadConfigInfos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gridsuite.study.server.notification.NotificationService;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -23,11 +22,10 @@
@RequiredArgsConstructor
public class NadConfigService {

private static final Logger LOGGER = LoggerFactory.getLogger(NadConfigService.class);

private final SingleLineDiagramService singleLineDiagramService;
private final NotificationService notificationService;

public UUID saveNadConfig(@NonNull NadConfigInfos nadConfigInfos) {
public UUID saveNadConfig(@NonNull NadConfigInfos nadConfigInfos, UUID studyUuid) {
UUID configUuid = nadConfigInfos.getId();

if (configUuid == null) {
Expand All @@ -37,6 +35,8 @@ public UUID saveNadConfig(@NonNull NadConfigInfos nadConfigInfos) {
singleLineDiagramService.updateNadConfig(nadConfigInfos);
}

notificationService.emitWorkspaceNadConfigUpdated(studyUuid, nadConfigInfos.getId());

return nadConfigInfos.getId();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class StudyConfigService {
private static final String DIAGRAM_GRID_LAYOUT_URI = "/diagram-grid-layout";
private static final String DIAGRAM_GRID_LAYOUT_WITH_ID_URI = DIAGRAM_GRID_LAYOUT_URI + UUID_PARAM;

private static final String WORKSPACES_CONFIG_URI = "/workspaces-configs";
private static final String WORKSPACES_CONFIG_WITH_ID_URI = WORKSPACES_CONFIG_URI + UUID_PARAM;

private static final DiagramPosition DEFAULT_DIAGRAM_POSITION = new DiagramPosition(2, 2, 0, 0);

private final RestTemplate restTemplate;
Expand Down Expand Up @@ -360,4 +363,92 @@ public UUID createGridLayoutFromNadDiagram(UUID sourceNadConfigUuid, UUID cloned
HttpEntity<DiagramGridLayout> httpEntity = new HttpEntity<>(diagramGridLayout, headers);
return restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.POST, httpEntity, UUID.class).getBody();
}

// Workspace Collection
public UUID createDefaultWorkspacesConfig() {
var path = UriComponentsBuilder
.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_URI + "/default")
.buildAndExpand()
.toUriString();
return restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.POST, null, UUID.class).getBody();
}

public void deleteWorkspacesConfig(UUID uuid) {
Objects.requireNonNull(uuid);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI)
.buildAndExpand(uuid)
.toUriString();
restTemplate.delete(studyConfigServerBaseUri + path);
}

public UUID duplicateWorkspacesConfig(UUID sourceUuid) {
Objects.requireNonNull(sourceUuid);
var path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_URI)
.queryParam("duplicateFrom", sourceUuid)
.buildAndExpand().toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Void> httpEntity = new HttpEntity<>(null, headers);
return restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.POST, httpEntity, UUID.class).getBody();
}

// Workspace methods
public String getWorkspaces(UUID collectionUuid) {
Objects.requireNonNull(collectionUuid);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces")
.buildAndExpand(collectionUuid).toUriString();
return restTemplate.getForObject(studyConfigServerBaseUri + path, String.class);
}

public String getWorkspace(UUID collectionUuid, UUID workspaceId) {
Objects.requireNonNull(collectionUuid);
Objects.requireNonNull(workspaceId);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces/{workspaceId}")
.buildAndExpand(collectionUuid, workspaceId).toUriString();
return restTemplate.getForObject(studyConfigServerBaseUri + path, String.class);
}

public void renameWorkspace(UUID collectionUuid, UUID workspaceId, String name) {
Objects.requireNonNull(collectionUuid);
Objects.requireNonNull(workspaceId);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces/{workspaceId}/name")
.buildAndExpand(collectionUuid, workspaceId).toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> httpEntity = new HttpEntity<>(name, headers);
restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.PUT, httpEntity, Void.class);
}

public String getWorkspacePanels(UUID collectionUuid, UUID workspaceId, List<String> ids) {
Objects.requireNonNull(collectionUuid);
Objects.requireNonNull(workspaceId);
UriComponentsBuilder builder = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces/{workspaceId}/panels");
if (ids != null && !ids.isEmpty()) {
builder.queryParam("ids", ids.toArray());
}
String path = builder.buildAndExpand(collectionUuid, workspaceId).toUriString();
return restTemplate.getForObject(studyConfigServerBaseUri + path, String.class);
}

public void createOrUpdateWorkspacePanels(UUID collectionUuid, UUID workspaceId, String panelsDto) {
Objects.requireNonNull(collectionUuid);
Objects.requireNonNull(workspaceId);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces/{workspaceId}/panels")
.buildAndExpand(collectionUuid, workspaceId).toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> httpEntity = new HttpEntity<>(panelsDto, headers);
restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.POST, httpEntity, Void.class);
}

public void deleteWorkspacePanels(UUID collectionUuid, UUID workspaceId, String panelIds) {
Objects.requireNonNull(collectionUuid);
Objects.requireNonNull(workspaceId);
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_CONFIG_API_VERSION + WORKSPACES_CONFIG_WITH_ID_URI + "/workspaces/{workspaceId}/panels")
.buildAndExpand(collectionUuid, workspaceId).toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> httpEntity = new HttpEntity<>(panelIds, headers);
restTemplate.exchange(studyConfigServerBaseUri + path, HttpMethod.DELETE, httpEntity, Void.class);
}
}
Loading
Loading