diff --git a/src/main/java/org/gridsuite/study/server/controller/StudyController.java b/src/main/java/org/gridsuite/study/server/controller/StudyController.java index d572b17f6..6a0d09323 100644 --- a/src/main/java/org/gridsuite/study/server/controller/StudyController.java +++ b/src/main/java/org/gridsuite/study/server/controller/StudyController.java @@ -1636,6 +1636,7 @@ public ResponseEntity buildNode(@Parameter(description = "Study uuid") @Pa @Parameter(description = "nodeUuid") @PathVariable("nodeUuid") UUID nodeUuid, @RequestHeader(HEADER_USER_ID) String userId) { studyService.assertNoBlockedNodeInTree(nodeUuid, rootNetworkUuid); + studyService.assertCanBuildNode(rootNetworkUuid, nodeUuid); studyService.buildNode(studyUuid, nodeUuid, rootNetworkUuid, userId); return ResponseEntity.ok().build(); } diff --git a/src/main/java/org/gridsuite/study/server/repository/networkmodificationtree/NodeRepository.java b/src/main/java/org/gridsuite/study/server/repository/networkmodificationtree/NodeRepository.java index 38c3c9ce4..737234e49 100644 --- a/src/main/java/org/gridsuite/study/server/repository/networkmodificationtree/NodeRepository.java +++ b/src/main/java/org/gridsuite/study/server/repository/networkmodificationtree/NodeRepository.java @@ -62,6 +62,21 @@ public interface NodeRepository extends JpaRepository { "WHERE n.id_node IN (SELECT nh.id_node FROM NodeHierarchy nh) AND n.id_node != :nodeUuid") List findAllChildren(UUID nodeUuid); + @NativeQuery("WITH RECURSIVE NodeHierarchy (id_node, parent_node) AS ( " + + " SELECT n0.id_node, n0.parent_node" + + " FROM NODE n0 " + + " WHERE n0.id_node = :nodeUuid " + + + " UNION ALL " + + + " SELECT n.id_node, n.parent_node " + + " FROM NODE n " + + " INNER JOIN NodeHierarchy nh ON n.id_node = nh.parent_node AND n.type != 'ROOT'" + + ") " + + "SELECT cast(nh.id_node AS VARCHAR) " + + "FROM NodeHierarchy nh where nh.id_node != :nodeUuid ") + List findAllAncestorsUuids(UUID nodeUuid); + @NativeQuery("WITH RECURSIVE ancestors (id_node, parent_node) AS ( " + " SELECT n.id_node, n.parent_node " + " FROM NODE n " + diff --git a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java index 613e1f225..a862eee9a 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java @@ -341,6 +341,12 @@ public List getNodeTreeUuids(UUID parentUuid) { return nodesUuids; } + public List getNodeBranchUuids(UUID nodeUuid) { + List nodesUuids = nodesRepository.findAllAncestorsUuids(nodeUuid); + nodesUuids.addAll(getNodeTreeUuids(nodeUuid)); + return nodesUuids; + } + public List getAllChildrenUuids(UUID parentUuid) { return nodesRepository.findAllChildrenUuids(parentUuid); } diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index 1d1bd28ea..1fbbf6f18 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -1140,6 +1140,12 @@ public void assertCanUpdateNodeInStudy(UUID studyUuid, UUID nodeUuid) { ); } + @Transactional(readOnly = true) + public void assertCanBuildNode(UUID rootNetworkUuid, UUID nodeUuid) { + List nodesUuids = networkModificationTreeService.getNodeBranchUuids(nodeUuid); + rootNetworkNodeInfoService.assertNoBuildingNode(rootNetworkUuid, nodesUuids); + } + private void assertNoBuildNoComputationInTree(UUID rootNetworkUuid, List nodesUuids) { // TODO modify computations endpoints to test multiple uuids nodesUuids.forEach(uuid -> rootNetworkNodeInfoService.assertComputationNotRunning(uuid, rootNetworkUuid)); @@ -1809,7 +1815,7 @@ private void buildNode(@NonNull UUID studyUuid, @NonNull UUID nodeUuid, @NonNull if (networkModificationTreeService.getNodeBuildStatus(nodeUuid, rootNetworkUuid).isBuilt()) { return; } - assertCanBuildNode(studyUuid, rootNetworkUuid, userId); + assertNoMaxBuilds(studyUuid, rootNetworkUuid, userId); BuildInfos buildInfos = networkModificationTreeService.getBuildInfos(nodeUuid, rootNetworkUuid); // Store all reports (inherited + new) for this node @@ -1859,7 +1865,7 @@ private long getAllowedBuildNodesUpToQuota(@NonNull UUID studyUuid, @NonNull UUI }).orElse(Long.MAX_VALUE); } - public void assertCanBuildNode(@NonNull UUID studyUuid, @NonNull UUID rootNetworkUuid, @NonNull String userId) { + public void assertNoMaxBuilds(@NonNull UUID studyUuid, @NonNull UUID rootNetworkUuid, @NonNull String userId) { // check restrictions on node builds number userAdminService.getUserMaxAllowedBuilds(userId).ifPresent(maxBuilds -> { long nbBuiltNodes = networkModificationTreeService.countBuiltNodes(studyUuid, rootNetworkUuid); diff --git a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java index 27ea698b0..35fb86588 100644 --- a/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java +++ b/src/test/java/org/gridsuite/study/server/NetworkModificationTest.java @@ -561,6 +561,100 @@ void testLocalBuildValue() throws Exception { assertEquals(BuildStatus.BUILT_WITH_ERROR, networkModificationTreeService.getNodeBuildStatus(modificationNode2Uuid, rootNetworkUuid).getGlobalBuildStatus()); } + @Test + void testBuildAsserts() throws Exception { + String userId = USER_ID; + StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, "UCTE"); + UUID rootNetworkUuid = studyEntity.getFirstRootNetwork().getId(); + UUID studyNameUserIdUuid = studyEntity.getId(); + UUID rootNodeUuid = getRootNode(studyNameUserIdUuid).getId(); + + // Branch 1 + NetworkModificationNode modificationNode1 = createNetworkModificationNode(studyNameUserIdUuid, + rootNodeUuid, UUID.randomUUID(), "variant_1", "node 1", userId); + NetworkModificationNode modificationNode2 = createNetworkModificationNode(studyNameUserIdUuid, + modificationNode1.getId(), UUID.randomUUID(), "variant_2", "node 2", userId); + + // Branch 2 + NetworkModificationNode modificationNode3 = createNetworkModificationNode(studyNameUserIdUuid, + rootNodeUuid, UUID.randomUUID(), "variant_3", "node 3", userId); + NetworkModificationNode modificationNode4 = createNetworkModificationNode(studyNameUserIdUuid, + modificationNode3.getId(), UUID.randomUUID(), "variant_4", "node 4", userId); + NetworkModificationNode modificationNode5 = createNetworkModificationNode(studyNameUserIdUuid, + modificationNode4.getId(), UUID.randomUUID(), "variant_5", "node 5", userId); + + /* + root + | \ + modificationNode1 modificationNode3 + | \ + modificationNode2 modificationNode4 + \ + modificationNode5 + */ + + // just test asserts + doNothing().when(studyService).buildNode(any(), any(), any(), any()); + + // Build all nodes is ok + testBuildAsserts(studyNameUserIdUuid, rootNetworkUuid, + Set.of(modificationNode1.getId(), modificationNode2.getId(), modificationNode3.getId(), modificationNode4.getId(), modificationNode5.getId()), + Set.of()); + + // Mark the node 1 status as building + RootNetworkNodeInfoEntity rootNetworkNodeInfo1Entity = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(modificationNode1.getId(), studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid)).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); + rootNetworkNodeInfo1Entity.setNodeBuildStatus(NodeBuildStatusEmbeddable.from(BuildStatus.BUILDING)); + rootNetworkNodeInfoRepository.save(rootNetworkNodeInfo1Entity); + + // Build all nodes branch1 is not ok + // Build all nodes branch2 is ok + testBuildAsserts(studyNameUserIdUuid, rootNetworkUuid, + Set.of(modificationNode3.getId(), modificationNode4.getId(), modificationNode5.getId()), + Set.of(modificationNode1.getId(), modificationNode2.getId()) + ); + + // Mark the node 2 status as building + RootNetworkNodeInfoEntity rootNetworkNodeInfo2Entity = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(modificationNode2.getId(), studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid)).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); + rootNetworkNodeInfo1Entity.setNodeBuildStatus(NodeBuildStatusEmbeddable.from(BuildStatus.NOT_BUILT)); + rootNetworkNodeInfo2Entity.setNodeBuildStatus(NodeBuildStatusEmbeddable.from(BuildStatus.BUILDING)); + rootNetworkNodeInfoRepository.saveAll(List.of(rootNetworkNodeInfo1Entity, rootNetworkNodeInfo2Entity)); + + // Build all nodes branch1 is not ok + // Build all nodes branch2 is ok + testBuildAsserts(studyNameUserIdUuid, rootNetworkUuid, + Set.of(modificationNode3.getId(), modificationNode4.getId(), modificationNode5.getId()), + Set.of(modificationNode1.getId(), modificationNode2.getId()) + ); + + // Mark the node 4 status as building + RootNetworkNodeInfoEntity rootNetworkNodeInfo4Entity = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(modificationNode4.getId(), studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid)).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); + rootNetworkNodeInfo2Entity.setNodeBuildStatus(NodeBuildStatusEmbeddable.from(BuildStatus.NOT_BUILT)); + rootNetworkNodeInfo4Entity.setNodeBuildStatus(NodeBuildStatusEmbeddable.from(BuildStatus.BUILDING)); + rootNetworkNodeInfoRepository.saveAll(List.of(rootNetworkNodeInfo2Entity, rootNetworkNodeInfo4Entity)); + + // Build all nodes branch1 is ok + // Build all nodes branch2 is not ok + testBuildAsserts(studyNameUserIdUuid, rootNetworkUuid, + Set.of(modificationNode1.getId(), modificationNode2.getId()), + Set.of(modificationNode3.getId(), modificationNode4.getId(), modificationNode5.getId()) + ); + + verify(rootNetworkNodeInfoService, times(20)).assertNoBuildingNode(any(), any()); + } + + private void testBuildAsserts(UUID studyUuid, UUID rootNetworkUuid, Set nodesUuidsOk, Set nodesUuidsNotOk) throws Exception { + for (UUID nodeUuid : nodesUuidsOk) { + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/build", studyUuid, rootNetworkUuid, nodeUuid) + .header(USER_ID_HEADER, USER_ID)) + .andExpect(status().isOk()); + } + for (UUID nodeUuid : nodesUuidsNotOk) { + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/build", studyUuid, rootNetworkUuid, nodeUuid) + .header(USER_ID_HEADER, USER_ID)) + .andExpect(status().isForbidden()); + } + } + @Test void testNetworkModificationSwitch() throws Exception { reportServerStubs.stubDeleteReport();