Skip to content
Merged
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 @@ -315,7 +315,18 @@ public ResponseEntity<Void> unshareModificationReference(
studyService.assertIsStudyAndNodeExist(studyUuid, nodeUuid);
studyService.assertIsNodeNotReadOnly(nodeUuid);
networkModificationActivityRunnerService.runWith(studyUuid, nodeUuid, userId,
() -> networkModificationService.unshareModificationReference(studyUuid, nodeUuid, modificationUuid, userId));
() -> networkModificationService.unshareModificationReferences(studyUuid, Map.of(nodeUuid, List.of(modificationUuid)), userId));
return ResponseEntity.ok().build();
}

@PutMapping(value = "/studies/{studyUuid}/unshare")
@Operation(summary = "Unshare all the reference modifications and copy the referenced composites in their place")
@ApiResponse(responseCode = "200", description = "All the reference modifications from the study have been replaced by copies of their referenced composites")
public ResponseEntity<Void> unshareAllModificationReferences(
@PathVariable("studyUuid") UUID studyUuid,
@RequestHeader(HEADER_USER_ID) String userId) {
Comment thread
souissimai marked this conversation as resolved.
Resolved
studyService.assertIsStudyExist(studyUuid);
networkModificationService.unshareAllReferencesInStudy(studyUuid, userId);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
public enum StudyBusinessErrorCode implements BusinessErrorCode {
NOT_FOUND("study.notFound"),
NODE_NOT_FOUND("study.nodeNotFound"),
COMPUTATION_RUNNING("study.computationRunning"),
LOADFLOW_ERROR("study.loadflowError"),
NOT_ALLOWED("study.notAllowed"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ protected StudyExceptionHandler(ServerNameProvider serverNameProvider) {
protected HttpStatus mapStatus(StudyBusinessErrorCode studyBusinessErrorCode) {
return switch (studyBusinessErrorCode) {
case NOT_FOUND,
NODE_NOT_FOUND,
NO_VOLTAGE_INIT_RESULTS_FOR_NODE
-> HttpStatus.NOT_FOUND;
case COMPUTATION_RUNNING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum NodeActivityType {
REIMPORT_CASE(UPDATING, true, false, false),
EDIT_TREE(UPDATING, true, true, true),
EDIT_MODIFICATIONS(UPDATING, true, true, true),
UNSHARE_MODIFICATIONS(UPDATING, false, true, true),
DELETE_NODES(DELETING, true, true, true),
EDIT_EVENTS(UPDATING, false, true, true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ public RootNodeInfoEntity getRootNodeInfoEntity(UUID nodeId) {
}

public NetworkModificationNodeInfoEntity getNetworkModificationNodeInfoEntity(UUID nodeId) {
return networkModificationNodeInfoRepository.findById(nodeId).orElseThrow(() -> new StudyException(NOT_FOUND, "Node not found"));
return networkModificationNodeInfoRepository.findById(nodeId).orElseThrow(() -> new StudyException(NODE_NOT_FOUND, "Node not found"));
}

private AbstractNodeInfoEntity getNodeInfoEntity(UUID nodeUuid) {
Expand Down Expand Up @@ -1008,6 +1008,10 @@ public List<NodeEntity> getAllNodes(UUID studyUuid) {
return nodesRepository.findAllByStudyId(studyUuid);
}

public List<NodeEntity> getAllNotStashedNetworkModificationNodes(UUID studyUuid) {
return nodesRepository.findAllByStudyIdAndTypeAndStashed(studyUuid, NodeType.NETWORK_MODIFICATION, false);
}

/**
* Gets or generates a modification report UUID for a node.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,9 @@ public void updateNetworkModification(UUID networkModificationUuid, String modif
restTemplate.exchange(getNetworkModificationServerURI(false) + path, HttpMethod.PUT, new HttpEntity<>(modificationInfos, headers), Void.class);
}

public void unshareModificationReference(UUID modificationReferenceUuid, String userId) {
String path = UriComponentsBuilder.fromPath(NETWORK_MODIFICATIONS_PATH + "/{uuid}/unshare").buildAndExpand(modificationReferenceUuid).toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HEADER_USER_ID, userId);
restTemplate.exchange(getNetworkModificationServerURI(false) + path, HttpMethod.PUT, new HttpEntity<>(headers), Void.class);
public void unshareModificationReferences(List<UUID> modificationReferenceUuids, String userId) {
String path = UriComponentsBuilder.fromPath(NETWORK_MODIFICATIONS_PATH + "/unshare").build().toUriString();
restTemplate.exchange(getNetworkModificationServerURI(false) + path, HttpMethod.PUT, userIdEntity(userId, modificationReferenceUuids), Void.class);
}

public void updateNetworkModificationsMetadata(List<UUID> networkModificationUuids, String metadata, String userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import org.gridsuite.study.server.dto.networkexport.PermissionType;
import org.gridsuite.study.server.error.StudyException;
import org.gridsuite.study.server.networkmodificationtree.dto.NodeBuildStatus;
import org.gridsuite.study.server.networkmodificationtree.entities.NodeEntity;
import org.gridsuite.study.server.nodeactivity.NodeActivityRunnerService;
import org.gridsuite.study.server.notification.NotificationService;
import org.gridsuite.study.server.repository.rootnetwork.RootNetworkEntity;
import org.gridsuite.study.server.service.DirectoryService;
Expand All @@ -30,6 +32,7 @@

import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_ALLOWED;
import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND;
import static org.gridsuite.study.server.nodeactivity.NodeActivityType.UNSHARE_MODIFICATIONS;

/**
* @author Slimane amar <slimane.amar at rte-france.com>
Expand All @@ -42,19 +45,22 @@ public class NetworkModificationService {
private final NetworkModificationRestService networkModificationRestService;
private final NotificationService notificationService;
private final DirectoryService directoryService;
private final NodeActivityRunnerService nodeActivityRunnerService;

public NetworkModificationService(NetworkModificationTreeService networkModificationTreeService,
RootNetworkService rootNetworkService,
RootNetworkNodeInfoService rootNetworkNodeInfoService,
NetworkModificationRestService networkModificationRestService,
NotificationService notificationService,
DirectoryService directoryService) {
DirectoryService directoryService,
NodeActivityRunnerService nodeActivityRunnerService) {
this.networkModificationTreeService = networkModificationTreeService;
this.rootNetworkService = rootNetworkService;
this.rootNetworkNodeInfoService = rootNetworkNodeInfoService;
this.networkModificationRestService = networkModificationRestService;
this.notificationService = notificationService;
this.directoryService = directoryService;
this.nodeActivityRunnerService = nodeActivityRunnerService;
}

public void updateNetworkModificationNameAndDescription(UUID studyUuid, UUID nodeUuid, UUID networkModificationUuid, String metadata, String userId) {
Expand Down Expand Up @@ -342,13 +348,35 @@ public NodeBuildStatus getNodeBuildStatus(UUID nodeUuid, UUID rootNetworkUuid) {
return networkModificationTreeService.getNodeBuildStatus(nodeUuid, rootNetworkUuid);
}

public void unshareModificationReference(
@NonNull final UUID studyUuid,
@NonNull final UUID nodeUuid,
@NonNull final UUID modificationUuid,
String userId) {
networkModificationRestService.unshareModificationReference(modificationUuid, userId);
notificationService.emitModificationsUpdated(studyUuid, nodeUuid, Optional.empty(), List.of(modificationUuid));
public void unshareAllReferencesInStudy(
@NonNull final UUID studyUuid,
String userId) {
// get the modification references of the whole study grouped by their node
Map<UUID, List<UUID>> referencesAffectedByNode = new HashMap<>();
networkModificationTreeService.getAllNotStashedNetworkModificationNodes(studyUuid).stream()
.map(NodeEntity::getIdNode)
.forEach(nodeUuid -> {
UUID groupUuid = networkModificationTreeService.getModificationGroupUuid(nodeUuid);
List<UUID> modificationReferences = networkModificationRestService.getModificationReferences(groupUuid)
Comment thread
souissimai marked this conversation as resolved.
Resolved
.stream().map(ModificationReference::modificationUuid).toList();
if (!modificationReferences.isEmpty()) {
referencesAffectedByNode.put(nodeUuid, modificationReferences);
}
});
if (referencesAffectedByNode.isEmpty()) {
return;
}

nodeActivityRunnerService.runWith(UNSHARE_MODIFICATIONS, studyUuid, new ArrayList<>(referencesAffectedByNode.keySet()),
() -> unshareModificationReferences(studyUuid, referencesAffectedByNode, userId));
}

public void unshareModificationReferences(@NonNull UUID studyUuid, Map<UUID, List<UUID>> referencesAffectedByNode, String userId) {
// a single remote call for all the references, then every affected node is notified
List<UUID> allModificationReferenceUuids = referencesAffectedByNode.values().stream().flatMap(List::stream).toList();
networkModificationRestService.unshareModificationReferences(allModificationReferenceUuids, userId);
referencesAffectedByNode.forEach((nodeUuid, modificationReferenceUuids) ->
notificationService.emitModificationsUpdated(studyUuid, nodeUuid, modificationReferenceUuids));
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.gridsuite.study.server.StudyConstants.HEADER_USER_ID;
import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NODE_NOT_FOUND;
import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND;
import static org.gridsuite.study.server.notification.NotificationService.*;
import static org.gridsuite.study.server.service.NetworkModificationTreeService.ROOT_NODE_NAME;
Expand Down Expand Up @@ -816,7 +817,7 @@ void testNodeModificationInfos() throws Exception {
RootNode root = getRootNode(rootStudyId, firstRootNetworkUuid);
UUID rootId = root.getId();

RootNodeInfoEntity rootInfos = rootNodeInfoRepository.findById(rootId).orElseThrow(() -> new StudyException(NOT_FOUND, "Node not found"));
RootNodeInfoEntity rootInfos = rootNodeInfoRepository.findById(rootId).orElseThrow(() -> new StudyException(NODE_NOT_FOUND, "Node not found"));
assertEquals(rootId, rootInfos.getId());

List<AbstractNode> children = root.getChildren();
Expand Down
75 changes: 67 additions & 8 deletions src/test/java/org/gridsuite/study/server/StudyServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
package org.gridsuite.study.server;

import org.gridsuite.study.server.dto.ComputationType;
import org.gridsuite.study.server.dto.ModificationReference;
import org.gridsuite.study.server.dto.QuotaState;
import org.gridsuite.study.server.dto.QuotaType;
import org.gridsuite.study.server.error.StudyException;
import org.gridsuite.study.server.networkmodificationtree.dto.BuildStatus;
import org.gridsuite.study.server.networkmodificationtree.dto.NodeBuildStatus;
import org.gridsuite.study.server.networkmodificationtree.entities.NodeEntity;
import org.gridsuite.study.server.networkmodificationtree.entities.NodeType;
import org.gridsuite.study.server.nodeactivity.NodeActivityRunnerService;
import org.gridsuite.study.server.notification.NotificationService;
import org.gridsuite.study.server.repository.networkmodificationtree.NodeRepository;
import org.gridsuite.study.server.service.NetworkModificationTreeService;
Expand All @@ -33,10 +35,10 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import static org.gridsuite.study.server.error.StudyBusinessErrorCode.MAX_OPERATION_TYPE_EXCEEDED;
import static org.gridsuite.study.server.nodeactivity.NodeActivityType.UNSHARE_MODIFICATIONS;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.*;
Expand All @@ -61,6 +63,8 @@
private NotificationService notificationService;
@MockitoBean
private RootNetworkService rootNetworkService;
@MockitoBean
private NodeActivityRunnerService nodeActivityRunnerService;

@AfterEach
void resetOperationQuotasFlag() {
Expand Down Expand Up @@ -278,11 +282,67 @@
UUID modificationUuid = UUID.randomUUID();
String userId = "userId";

networkModificationService.unshareModificationReference(studyUuid, nodeUuid, modificationUuid, userId);
networkModificationService.unshareModificationReferences(studyUuid, Map.of(nodeUuid, List.of(modificationUuid)), userId);

// the reference is replaced remotely by a copy of the shared composite, then the front is notified
verify(networkModificationRestService, times(1)).unshareModificationReference(modificationUuid, userId);
verify(notificationService, times(1)).emitModificationsUpdated(studyUuid, nodeUuid, Optional.empty(), List.of(modificationUuid));
verify(networkModificationRestService, times(1)).unshareModificationReferences(List.of(modificationUuid), userId);
verify(notificationService, times(1)).emitModificationsUpdated(studyUuid, nodeUuid, List.of(modificationUuid));
}

@Test
void testUnshareAllReferencesInStudy() {
UUID studyUuid = UUID.randomUUID();
String userId = "userId";

doReturn(List.of()).when(networkModificationTreeService).getAllNotStashedNetworkModificationNodes(studyUuid);

networkModificationService.unshareAllReferencesInStudy(studyUuid, userId);

verify(networkModificationTreeService, times(1)).getAllNotStashedNetworkModificationNodes(studyUuid);
verify(nodeActivityRunnerService, never()).runWith(any(), any(), any(), (Runnable) any());
verify(networkModificationRestService, never()).unshareModificationReferences(any(), any());
verify(notificationService, never()).emitModificationsUpdated(any(), any(), any());
}

@Test
void testUnshareAllReferencesInStudyWithReferences() {
UUID studyUuid = UUID.randomUUID();
UUID nodeUuid = UUID.randomUUID();
UUID modificationUuid = UUID.randomUUID();
String userId = "userId";

UUID nodeWithoutReferenceUuid = UUID.randomUUID();
doReturn(List.of(
new NodeEntity(nodeUuid, null, NodeType.NETWORK_MODIFICATION, null, false, null, List.of()),
new NodeEntity(nodeWithoutReferenceUuid, null, NodeType.NETWORK_MODIFICATION, null, false, null, List.of())
)).when(networkModificationTreeService).getAllNotStashedNetworkModificationNodes(studyUuid);

UUID groupUuid = UUID.randomUUID();
doReturn(groupUuid).when(networkModificationTreeService).getModificationGroupUuid(nodeUuid);
UUID groupWithoutReferenceUuid = UUID.randomUUID();
doReturn(groupWithoutReferenceUuid).when(networkModificationTreeService).getModificationGroupUuid(nodeWithoutReferenceUuid);
doReturn(List.of()).when(networkModificationRestService).getModificationReferences(groupWithoutReferenceUuid);

ModificationReference modificationReference = new ModificationReference(modificationUuid, UUID.randomUUID(), null);
doReturn(List.of(modificationReference)).when(networkModificationRestService).getModificationReferences(groupUuid);

// Mock nodeActivityRunnerService to bypass database checks
doAnswer(invocation -> {
Runnable runnable = invocation.getArgument(3);
runnable.run();
return null;
}).when(nodeActivityRunnerService).runWith(any(), any(), any(), (Runnable) any());

networkModificationService.unshareAllReferencesInStudy(studyUuid, userId);

verify(networkModificationTreeService, times(1)).getAllNotStashedNetworkModificationNodes(studyUuid);
verify(networkModificationTreeService, times(1)).getModificationGroupUuid(nodeUuid);
verify(networkModificationRestService, times(1)).getModificationReferences(groupUuid);
// only the node having references is held by the activity
verify(nodeActivityRunnerService, times(1)).runWith(eq(UNSHARE_MODIFICATIONS), eq(studyUuid), eq(List.of(nodeUuid)), (Runnable) any());
verify(networkModificationRestService, times(1)).unshareModificationReferences(List.of(modificationUuid), userId);
verify(notificationService, times(1)).emitModificationsUpdated(studyUuid, nodeUuid, List.of(modificationUuid));
verify(notificationService, never()).emitModificationsUpdated(eq(studyUuid), eq(nodeWithoutReferenceUuid), any());
}

@Test
Expand All @@ -293,12 +353,11 @@
String userId = "userId";

doThrow(new RuntimeException("network modification server error"))
.when(networkModificationRestService).unshareModificationReference(modificationUuid, userId);
.when(networkModificationRestService).unshareModificationReferences(List.of(modificationUuid), userId);

assertThrows(RuntimeException.class,

Check warning on line 358 in src/test/java/org/gridsuite/study/server/StudyServiceTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor the code of the lambda to have only one invocation possibly throwing a runtime exception.

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Astudy-server&issues=AaFkGpHas5GkW_ZvsyMk&open=AaFkGpHas5GkW_ZvsyMk&pullRequest=1145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue

() -> networkModificationService.unshareModificationReference(studyUuid, nodeUuid, modificationUuid, userId));
() -> networkModificationService.unshareModificationReferences(studyUuid, Map.of(nodeUuid, List.of(modificationUuid)), userId));

verify(notificationService, never()).emitModificationsUpdated(any(), any(), any(), any());
verify(notificationService, never()).emitModificationsUpdated(any(), any(), any());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private static boolean hasConflict(NodeActivityEntity activity, List<NodeActivit

@Test
void invalidatesChildrenIsClassified() {
List<NodeActivityType> touchOnlyTheirNode = List.of(BUILD, UNBUILD, COMPUTE, EDIT_EVENTS);
List<NodeActivityType> touchOnlyTheirNode = List.of(BUILD, UNBUILD, COMPUTE, EDIT_EVENTS, UNSHARE_MODIFICATIONS);
List<NodeActivityType> invalidateTheirChildren = List.of(UNBUILD_CHILDREN, UNBUILD_ALL,
COMPUTE_AND_UNBUILD_CHILDREN, REIMPORT_CASE, EDIT_TREE, EDIT_MODIFICATIONS, DELETE_NODES);

Expand All @@ -92,7 +92,7 @@ void affectsAllRootNetworksIsClassified() {
List<NodeActivityType> oneRootNetwork = List.of(BUILD, UNBUILD, UNBUILD_CHILDREN, COMPUTE,
COMPUTE_AND_UNBUILD_CHILDREN, REIMPORT_CASE);
List<NodeActivityType> allRootNetworks = List.of(UNBUILD_ALL, EDIT_TREE, EDIT_MODIFICATIONS,
DELETE_NODES, EDIT_EVENTS);
DELETE_NODES, EDIT_EVENTS, UNSHARE_MODIFICATIONS);

assertThat(oneRootNetwork).noneMatch(NodeActivityType::affectsAllRootNetworks);
assertThat(allRootNetworks).allMatch(NodeActivityType::affectsAllRootNetworks);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,14 @@ void testUpdateNetworkModificationsMetadata() {
@Test
void testUnshareModificationReference() {
UUID modificationReferenceUuid = UUID.randomUUID();
String expectedUrl = NETWORK_MODIFICATION_SERVER_URI + "/v1/network-modifications/" + modificationReferenceUuid + "/unshare";
String expectedUrl = NETWORK_MODIFICATION_SERVER_URI + "/v1/network-modifications/unshare";

networkModificationService.unshareModificationReference(modificationReferenceUuid, USER_ID);
networkModificationService.unshareModificationReferences(List.of(modificationReferenceUuid), USER_ID);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HEADER_USER_ID, USER_ID);
verify(restTemplate).exchange(expectedUrl, HttpMethod.PUT, new HttpEntity<>(headers), Void.class);
verify(restTemplate).exchange(expectedUrl, HttpMethod.PUT, new HttpEntity<>(List.of(modificationReferenceUuid), headers), Void.class);
}

@Test
Expand Down
Loading
Loading