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
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@
<artifactId>powsybl-iidm-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-case-datasource-client</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server;

import com.powsybl.security.SecurityAnalysisResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.gridsuite.securityanalysis.server.service.SecurityAnalysisOnCaseService;
import org.gridsuite.securityanalysis.server.service.SecurityAnalysisParametersService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

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

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@RestController
@RequestMapping(value = "/" + SecurityAnalysisApi.API_VERSION + "/cases")
@Tag(name = "Security analysis server on case")
public class SecurityAnalysisOnCaseController {
private final SecurityAnalysisOnCaseService securityAnalysisOnCaseService;

public SecurityAnalysisOnCaseController(SecurityAnalysisOnCaseService securityAnalysisOnCaseService, SecurityAnalysisParametersService securityAnalysisParametersService) {

Check warning on line 40 in src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisOnCaseController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused method parameter "securityAnalysisParametersService".

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Asecurity-analysis-server&issues=AZyQTJMvzsp57MRdPntc&open=AZyQTJMvzsp57MRdPntc&pullRequest=224
this.securityAnalysisOnCaseService = securityAnalysisOnCaseService;
}

@PostMapping(value = "/{caseUuid}/run-and-save", produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
@Operation(summary = "Run a security analysis on a case and store the result in the database")
@ApiResponses(value = {@ApiResponse(responseCode = "200",
description = "The security analysis has been performed and results have been saved to database",
content = {@Content(mediaType = APPLICATION_JSON_VALUE,
schema = @Schema(implementation = SecurityAnalysisResult.class))})})
public ResponseEntity<UUID> runAndSave(@Parameter(description = "Case UUID") @PathVariable("caseUuid") UUID caseUuid,
@Parameter(description = "Execution UUID") @RequestParam(name = "executionUuid", required = false) UUID executionUuid,
@Parameter(description = "Contingency list name") @RequestParam(name = "contingencyListName", required = false) List<String> contigencyListNames,
@Parameter(description = "parametersUuid") @RequestParam(name = "parametersUuid", required = false) UUID parametersUuid,
@Parameter(description = "loadFlow parameters uuid") @RequestParam(name = "loadFlowParametersUuid", required = false) UUID loadFlowParametersUuid) {
securityAnalysisOnCaseService.runAndSaveResult(caseUuid, executionUuid, contigencyListNames, parametersUuid, loadFlowParametersUuid);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import lombok.Getter;

import java.util.UUID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@Getter
public class CaseResultInfos {
private final UUID caseResultUuid;

private final UUID executionUuid;

private final UUID reportUuid;

private final UUID resultUuid;

private final String stepType;

private final String status;

public CaseResultInfos(UUID caseResultUuid, UUID executionUuid, UUID reportUuid, UUID resultUuid, String stepType, String status) {
this.caseResultUuid = caseResultUuid;
this.executionUuid = executionUuid;
this.reportUuid = reportUuid;
this.resultUuid = resultUuid;
this.stepType = stepType;
this.status = status;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import com.powsybl.cases.datasource.CaseDataSourceClient;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.iidm.network.Importer;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.NetworkFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Properties;
import java.util.UUID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@Service
public class NetworkConversionService {
private static final Logger LOGGER = LoggerFactory.getLogger(NetworkConversionService.class);

private final RestTemplate caseServerRest;

public NetworkConversionService(@Value("${powsybl.services.case-server.base-uri:http://case-server/}") String caseServerBaseUri,
RestTemplateBuilder restTemplateBuilder) {
this.caseServerRest = restTemplateBuilder.rootUri(caseServerBaseUri).build();
}

public Network createNetwork(UUID caseUuid, ReportNode reporter) {
LOGGER.info("Creating network");

CaseDataSourceClient dataSource = new CaseDataSourceClient(caseServerRest, caseUuid);

Importer importer = Importer.find(dataSource, LocalComputationManager.getDefault());
if (importer == null) {
throw new PowsyblException("No importer found");
} else {
return importer.importData(dataSource, NetworkFactory.findDefault(), new Properties(), reporter);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com
*/
@AllArgsConstructor
@Service
public class NotificationOnCaseService {
@Getter private final StreamBridge publisher;
@Getter private final String publishPrefix;

@Autowired
public NotificationOnCaseService(StreamBridge publisher) {
this(publisher, "publish");
}

public void sendMessage(Message<? extends Object> message, String bindingName) {
publisher.send(publishPrefix + bindingName, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.report.ReportNodeDeserializer;
import com.powsybl.commons.report.ReportNodeJsonModule;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.UUID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com
*/
@Service
public class ReportOnCaseService {
private static final String REPORT_API_VERSION = "v1";

private static final String DELIMITER = "/";

private String reportServerBaseUri;

private RestTemplate reportServerRest;

private final ObjectMapper objectMapper;

public ReportOnCaseService(@Value("${gridsuite.services.report-server.base-uri:http://report-server}") String reportServerURI,
ObjectMapper objectMapper,
RestTemplate restTemplate) {
this.reportServerBaseUri = reportServerURI;
this.objectMapper = objectMapper;
this.objectMapper.registerModule(new ReportNodeJsonModule());
this.objectMapper.setInjectableValues(new InjectableValues.Std().addValue(ReportNodeDeserializer.DICTIONARY_VALUE_ID, null));
this.reportServerRest = restTemplate;
}

private String getReportServerURI() {
return this.reportServerBaseUri + DELIMITER + REPORT_API_VERSION + DELIMITER + "reports" + DELIMITER;
}

public void sendReport(UUID reportUuid, ReportNode reportNode) {
var path = UriComponentsBuilder.fromPath("{reportUuid}")
.buildAndExpand(reportUuid)
.toUriString();
var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
try {
reportServerRest.exchange(this.getReportServerURI() + path, HttpMethod.PUT, new HttpEntity<>(objectMapper.writeValueAsString(reportNode), headers), ReportNode.class);
} catch (JsonProcessingException error) {
throw new PowsyblException("error creating report", error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

import java.io.UncheckedIOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@Getter
public class SecurityAnalysisCaseContext {
public final UUID caseUuid;
public final UUID executionUuid;
public final List<String> contigencyListNames;
public final UUID parametersUuid;
public final UUID loadFlowParametersUuid;

public SecurityAnalysisCaseContext(UUID caseUuid, UUID executionUuid, List<String> contigencyListNames, UUID parametersUuid, UUID loadFlowParametersUuid) {
this.caseUuid = caseUuid;
this.executionUuid = executionUuid;
this.contigencyListNames = contigencyListNames;
this.parametersUuid = parametersUuid;
this.loadFlowParametersUuid = loadFlowParametersUuid;
}

public static SecurityAnalysisCaseContext fromMessage(Message<String> message, ObjectMapper objectMapper) {
Objects.requireNonNull(message);
SecurityAnalysisCaseContext context;
try {
context = objectMapper.readValue(message.getPayload(), SecurityAnalysisCaseContext.class);
} catch (JsonProcessingException e) {
throw new UncheckedIOException(e);
}
return context;
}

public Message<String> toMessage(ObjectMapper objectMapper) {
String json;
try {
json = objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new UncheckedIOException(e);
}
return MessageBuilder.withPayload(json).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.securityanalysis.server.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@Service
public class SecurityAnalysisOnCaseService {
private final SecurityAnalysisResultService securityAnalysisResultService;

Check warning on line 22 in src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisOnCaseService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "securityAnalysisResultService" private field.

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Asecurity-analysis-server&issues=AZyQTJLKzsp57MRdPnta&open=AZyQTJLKzsp57MRdPnta&pullRequest=224
private final NotificationOnCaseService notificationOnCaseService;
private final ObjectMapper objectMapper;
private final String defaultProvider;

Check warning on line 25 in src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisOnCaseService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "defaultProvider" private field.

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Asecurity-analysis-server&issues=AZyQTJLKzsp57MRdPntZ&open=AZyQTJLKzsp57MRdPntZ&pullRequest=224

public SecurityAnalysisOnCaseService(SecurityAnalysisResultService securityAnalysisResultService,
NotificationOnCaseService notificationOnCaseService,
ObjectMapper objectMapper,
@Value("${security-analysis.default-provider}") String defaultProvider) {
this.securityAnalysisResultService = securityAnalysisResultService;
this.notificationOnCaseService = notificationOnCaseService;
this.objectMapper = objectMapper;
this.defaultProvider = defaultProvider;
}

@Transactional
public void runAndSaveResult(UUID caseUuid, UUID executionUuid, List<String> contigencyListNames, UUID parametersUuid, UUID loadFlowParametersUuid) {
notificationOnCaseService.sendMessage(
new SecurityAnalysisCaseContext(caseUuid, executionUuid, contigencyListNames, parametersUuid, loadFlowParametersUuid).toMessage(objectMapper),
"CaseRun-out-0");
}
}
Loading
Loading