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
Expand Up @@ -27,6 +27,7 @@
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;
Expand All @@ -44,6 +45,7 @@ public class StudyExportService {
private static final Logger LOGGER = LoggerFactory.getLogger(StudyExportService.class);
public static final String TREE_JSON_FILE_NAME = "tree.json";
public static final String CASES_FOLDER = "cases";
public static final String PARAMETERS_FOLDER = "parameters";

private final StudyService studyService;
private final CaseService caseService;
Expand All @@ -68,7 +70,7 @@ public InputStreamResource exportStudy(UUID studyUuid, String userId) {
Path tempDir = createTempWorkDir(studyUuid);
Path zipFile = null;
try {
zipFile = compressStudyToZip(studyUuid, tempDir);
zipFile = compressStudyToZip(studyUuid, userId, tempDir);
InputStream stream = Files.newInputStream(zipFile, StandardOpenOption.DELETE_ON_CLOSE);
zipFile = null;
return new InputStreamResource(stream);
Expand All @@ -93,7 +95,7 @@ public InputStreamResource exportStudy(UUID studyUuid, String userId) {
/**
* Build tree.json and the case files under tempDir, then compress them into a temp zip file
*/
private Path compressStudyToZip(UUID studyUuid, Path tempDir) throws IOException {
private Path compressStudyToZip(UUID studyUuid, String userId, Path tempDir) throws IOException {
TreeExportInfos treeExportInfos = studyService.buildTreeExport(studyUuid);
Path studyJsonPath = tempDir.resolve(TREE_JSON_FILE_NAME);
objectMapper.writerWithDefaultPrettyPrinter().writeValue(studyJsonPath.toFile(), treeExportInfos);
Expand All @@ -103,6 +105,7 @@ private Path compressStudyToZip(UUID studyUuid, Path tempDir) throws IOException
String caseName = rootNetworkInfos.caseInfos().getCaseName();
exportCaseFile(caseUuid, caseName, casesDir);
}
exportComputationParameters(studyUuid, userId, tempDir);
Path zipFile = createTempExportFile(studyUuid);
try (OutputStream fos = Files.newOutputStream(zipFile);
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
Expand All @@ -111,6 +114,17 @@ private Path compressStudyToZip(UUID studyUuid, Path tempDir) throws IOException
return zipFile;
}

private void exportComputationParameters(UUID studyUuid, String userId, Path tempDir) throws IOException {
Map<String, String> parametersByFileName = studyService.exportComputationParameters(studyUuid, userId);
if (parametersByFileName.isEmpty()) {
return;
}
Path parametersDir = Files.createDirectories(tempDir.resolve(PARAMETERS_FOLDER));
for (Map.Entry<String, String> entry : parametersByFileName.entrySet()) {
Files.writeString(parametersDir.resolve(entry.getKey()), entry.getValue());
}
}

private Path createTempWorkDir(UUID studyUuid) {
return createTempPath(studyUuid, "temp directory", "rwx------",
attr -> Files.createTempDirectory("study-export-" + studyUuid, attr));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3041,6 +3041,11 @@ public TreeExportInfos buildTreeExport(UUID studyUuid) {
return new TreeExportInfos(studyUuid, rootNetworks, nodeTree);
}

public Map<String, String> exportComputationParameters(UUID studyUuid, String userId) {
StudyEntity studyEntity = getStudy(studyUuid);
return computationParametersService.exportParameters(studyEntity, userId);
}

private RootNetworkExportInfos toRootNetworkExportInfos(RootNetworkInfos rootNetworkInfos, int index) {
return new RootNetworkExportInfos(
rootNetworkInfos.getName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
*/
package org.gridsuite.study.server.service.common;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.gridsuite.study.server.dto.ComputationType;
import org.gridsuite.study.server.dto.UserProfileInfos;
import org.gridsuite.study.server.dto.computation.ComputationParameterUUIDs;
import org.gridsuite.study.server.error.StudyException;
import org.gridsuite.study.server.repository.StudyEntity;
import org.gridsuite.study.server.service.*;
import org.gridsuite.study.server.service.dynamicmargincalculation.DynamicMarginCalculationRestService;
Expand All @@ -25,11 +28,17 @@
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

import static org.gridsuite.study.server.error.StudyBusinessErrorCode.EXPORT_STUDY_ERROR;

/**
* @author Abdelsalem HEDHILI <abdelsalem.hedhili at rte-france.com>
*/
Expand All @@ -40,15 +49,17 @@
private static final Logger LOGGER = LoggerFactory.getLogger(ComputationParametersService.class);

private final UserAdminService userAdminService;
private final ObjectMapper objectMapper;
private final List<ComputationParametersDefinition> computationParametersDefinitions;

// this is useful to avoid repetitive calls when doing operation on all computation types (duplicate, delete)
// this is useful to avoid repetitive calls when doing operation on all computation types (duplicate, delete, export)
private record ComputationParametersDefinition(
ComputationType type,
Function<StudyEntity, UUID> studyParameterGetter,
Function<UserProfileInfos, UUID> profileParameterGetter,
ComputationParameters service,
BiConsumer<ComputationParameterUUIDs.ComputationParameterUUIDsBuilder, UUID> parametersSetter
BiConsumer<ComputationParameterUUIDs.ComputationParameterUUIDsBuilder, UUID> parametersSetter,
BiFunction<UUID, String, ?> parametersFetcher
) {
}

Expand All @@ -62,70 +73,82 @@
DynamicMarginCalculationRestService dynamicMarginCalculationRestService,
StateEstimationRestService stateEstimationService,
PccMinRestService pccMinService,
UserAdminService userAdminService) {
UserAdminService userAdminService,
ObjectMapper objectMapper) {

this.userAdminService = userAdminService;
this.objectMapper = objectMapper;
this.computationParametersDefinitions = List.of(
new ComputationParametersDefinition(
ComputationType.LOAD_FLOW,
StudyEntity::getLoadFlowParametersUuid,
UserProfileInfos::getLoadFlowParameterId,
loadFlowRestService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::loadFlowParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::loadFlowParametersUuid,
(uuid, userId) -> loadFlowRestService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.SHORT_CIRCUIT,
StudyEntity::getShortCircuitParametersUuid,
UserProfileInfos::getShortcircuitParameterId,
shortCircuitService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::shortCircuitParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::shortCircuitParametersUuid,
(uuid, userId) -> shortCircuitService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.DYNAMIC_SIMULATION,
StudyEntity::getDynamicSimulationParametersUuid,
UserProfileInfos::getDynamicSimulationParameterId,
dynamicSimulationRestService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicSimulationParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicSimulationParametersUuid,
(uuid, userId) -> dynamicSimulationRestService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.VOLTAGE_INITIALIZATION,
StudyEntity::getVoltageInitParametersUuid,
UserProfileInfos::getVoltageInitParameterId,
voltageInitService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::voltageInitParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::voltageInitParametersUuid,
(uuid, userId) -> voltageInitService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.SECURITY_ANALYSIS,
StudyEntity::getSecurityAnalysisParametersUuid,
UserProfileInfos::getSecurityAnalysisParameterId,
securityAnalysisService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::securityAnalysisParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::securityAnalysisParametersUuid,
(uuid, userId) -> securityAnalysisService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.SENSITIVITY_ANALYSIS,
StudyEntity::getSensitivityAnalysisParametersUuid,
UserProfileInfos::getSensitivityAnalysisParameterId,
sensitivityAnalysisService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::sensitivityAnalysisParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::sensitivityAnalysisParametersUuid,
(uuid, userId) -> sensitivityAnalysisService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.DYNAMIC_SECURITY_ANALYSIS,
StudyEntity::getDynamicSecurityAnalysisParametersUuid,
UserProfileInfos::getDynamicSecurityAnalysisParameterId,
dynamicSecurityAnalysisRestService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicSecurityAnalysisParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicSecurityAnalysisParametersUuid,
(uuid, userId) -> dynamicSecurityAnalysisRestService.getParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.DYNAMIC_MARGIN_CALCULATION,
StudyEntity::getDynamicMarginCalculationParametersUuid,
UserProfileInfos::getDynamicMarginCalculationParameterId,
dynamicMarginCalculationRestService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicMarginCalculationParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::dynamicMarginCalculationParametersUuid,
dynamicMarginCalculationRestService::getParameters),
new ComputationParametersDefinition(
ComputationType.STATE_ESTIMATION,
StudyEntity::getStateEstimationParametersUuid,
userProfileInfos -> null,
stateEstimationService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::stateEstimationParametersUuid),
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::stateEstimationParametersUuid,
(uuid, userId) -> stateEstimationService.getStateEstimationParameters(uuid)),
new ComputationParametersDefinition(
ComputationType.PCC_MIN,
StudyEntity::getPccMinParametersUuid,
UserProfileInfos::getPccMinParameterId,
pccMinService,
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::pccMinParametersUuid)
ComputationParameterUUIDs.ComputationParameterUUIDsBuilder::pccMinParametersUuid,
(uuid, userId) -> pccMinService.getParameters(uuid))
);
}

Expand Down Expand Up @@ -220,4 +243,34 @@
}
}

public Map<String, String> exportParameters(StudyEntity studyEntity, String userId) {
Map<String, String> parametersByFileName = new LinkedHashMap<>();
computationParametersDefinitions.forEach(definition -> {
UUID parametersUuid = definition.studyParameterGetter().apply(studyEntity);
if (parametersUuid == null) {
return;
}
try {
Object parameters = definition.parametersFetcher().apply(parametersUuid, userId);
String fileName = toFileName(definition.type());
parametersByFileName.put(fileName, parameters instanceof String json ? json : writeAsJson(fileName, parameters));
} catch (Exception e) {
LOGGER.warn("Failed to fetch {} parameters (uuid={}) for study export", definition.type().getLabel(), parametersUuid, e);
}
});
return parametersByFileName;
}

private static String toFileName(ComputationType type) {
return type.name().toLowerCase(Locale.ROOT).replace('_', '-') + ".json";
}

private String writeAsJson(String fileName, Object parameters) {
try {
return objectMapper.writeValueAsString(parameters);
} catch (JsonProcessingException e) {

Check warning on line 271 in src/main/java/org/gridsuite/study/server/service/common/ComputationParametersService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Astudy-server&issues=AaAVFcC7BuamWNefmXKa&open=AaAVFcC7BuamWNefmXKa&pullRequest=1065
throw new StudyException(EXPORT_STUDY_ERROR, "Failed to serialize computation parameters " + fileName);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ void testExportStudy() throws Exception {
.willReturn(WireMock.aResponse().withStatus(200)
.withHeader("Content-Type", "application/octet-stream")
.withBody("dummy case content".getBytes())));
// Stub the computation parameters fetches
computationServerStubs.stubGetParametersAny("{}");
// Export as zip
MvcResult result = mockMvc.perform(get("/v1/studies/{studyUuid}/export/{studyName}", studyUuid, "studyName").header(HEADER_USER_ID, "testUser"))
.andExpect(status().isOk())
Expand Down Expand Up @@ -103,6 +105,8 @@ void testExportStudy() throws Exception {
// Verify the case content download call
WireMockUtilsCriteria.verifyGetRequest(wireMockServer, "/v1/cases/" + CASE_UUID, false, Map.of(), 1);
wireMockStubs.directoryServer.verifyCheckPermission(List.of(studyUuid), null, PermissionType.READ, false);
// Verify the computation parameters fetches
computationServerStubs.verifyParametersGetAny(10);
}

@Test
Expand All @@ -125,6 +129,7 @@ void testExportStudyFailToDeleteTempZipFile() throws Exception {
wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/cases/" + CASE_UUID))
.willReturn(WireMock.aResponse().withStatus(200).withHeader("Content-Type", "application/octet-stream")
.withBody("dummy case content".getBytes())));
computationServerStubs.stubGetParametersAny("{}");
// Capture the real zip file path as it is matched, so the test can clean it up itself:
// the service's own Files.deleteIfExists call on this path is mocked to fail below.
AtomicReference<Path> capturedZipFile = new AtomicReference<>();
Expand Down Expand Up @@ -156,5 +161,6 @@ void testExportStudyFailToDeleteTempZipFile() throws Exception {
}
WireMockUtilsCriteria.verifyGetRequest(wireMockServer, "/v1/cases/" + CASE_UUID, false, Map.of(), 1);
wireMockStubs.directoryServer.verifyCheckPermission(List.of(studyUuid), null, PermissionType.READ, false);
computationServerStubs.verifyParametersGetAny(10);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ public void verifyParametersGet(String paramUuid) {
WireMockUtilsCriteria.verifyGetRequest(wireMock, "/v1/parameters/" + paramUuid, Map.of());
}

public void stubGetParametersAny(String responseBody) {
wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/parameters/.*"))
.willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(responseBody)));
}

public void verifyParametersGetAny(int nbRequests) {
WireMockUtilsCriteria.verifyGetRequest(wireMock, "/v1/parameters/.*", true, Map.of(), nbRequests);
}

public void stubCreateParameter(String responseBody) {
wireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/parameters"))
.atPriority(10)
Expand Down
Loading