Commit d3f861c1 authored by Nicola Fulvio Calabria's avatar Nicola Fulvio Calabria
Browse files

Implemented copyBranch in nodeDAO + test

parent afb31aa3
Loading
Loading
Loading
Loading
+2 −16
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ public class CopyService {
        // check source branch for read and lock it
        nodeBranchService.checkBranchForReadAndLock(sourcePath, jobId);
        
        copyNode(sourcePath, destinationPath, jobId);
        nodeDao.copyBranch(sourcePath, destinationPath, jobId);        

    }

@@ -76,18 +76,4 @@ public class CopyService {
        }
    }    

    private void copyNode(String vosSourceNode, String vosDestNode, String jobId) {
        String sourceName = NodeUtils.getNodeName(vosSourceNode);
        String destPath = vosDestNode + "/" + sourceName;

        nodeDao.copySingleNode(sourceName, destPath, jobId);
        List<String> children = nodeDao.listNodeChildren(sourceName);
        if (!children.isEmpty()) {
            for (String n : children) {
                this.copyNode(sourceName + "/" +n, destPath+"/"+n, jobId);
            }
        }

    }

}
+52 −45
Original line number Diff line number Diff line
@@ -339,46 +339,53 @@ public class NodeDAO {
        });
    }

    public void copySingleNode(String sourceVosPath,
            String destVosPath, String jobId) {

        // Select source node
        String selectSourceSQL = "SELECT\n"
                + "c.path, c.node_id, c.parent_path, c.parent_relative_path, c.name, c.os_name, c.tstamp_wrapper_dir,\n"
                + "c.type, c.location_id, c.format, c.async_trans, c.sticky, c.creator_id,\n"
                + "c.group_read, c.group_write, c.is_public, c.delta, c.content_type, c.content_encoding,\n"
                + "c.content_length, c.content_md5, c.accept_views, c.provide_views, c.protocols\n"
                + "FROM node c\n"
                + "WHERE c.node_id = id_from_vos_path(?)";

        // Select destination node necessary information
        String selectDestinationSQL = "SELECT\n"
                + "d.path, d.parent_path, d.parent_relative_path\n"
                + "FROM node d\n"
                + "WHERE d.node_id = id_from_vos_path(?)";

        // Insert branch 
        String insertSQL = "INSERT INTO node\n"
                + "(parent_path, parent_relative_path, job_id, name, os_name, tstamp_wrapper_dir,\n"
                + "type, location_id, format, async_trans, sticky, creator_id,\n"
                + "group_read, group_write, is_public, delta, content_type, content_encoding,\n"
                + "content_length, content_md5, accept_views, provide_views, protocols)\n"
                + "VALUES\n"
                + "(cte_dest.path, COALESCE(cte_dest.parent_relative_path, cte_dest.path), ?,\n"
                + "cte_source.name, cte_source.os_name, cte_source.tstamp_wrapper_dir,\n"
                + "cte_source.type, cte_source.location_id, cte_source.format, cte_source.async_trans, cte_source.sticky, cte_source.creator_id,\n"
                + "cte_source.group_read, cte_source.group_write, cte_source.is_public, cte_source.delta, cte_source.content_type, cte_source.content_encoding,\n"
                + "cte_source.content_length, cte_source.content_md5, cte_source.accept_views, cte_source.provide_views, cte_source.protocols)\n";

        String cteSQL = "WITH cte_source AS (" + selectSourceSQL + "),\n"
                + "cte_dest AS (" + selectDestinationSQL + ")\n"
                + insertSQL;
    public void copyBranch(String sourceVosPath, String destVosPath, String jobId) {
        
        String destVosParentPath = NodeUtils.getParentPath(destVosPath);
        String destName = NodeUtils.getNodeName(destVosPath);

        String parentInsert = "INSERT INTO node (node_id, parent_path, parent_relative_path, name, type, location_id, creator_id, group_write, group_read, is_public, job_id)\n";

        String ctePathPrefix = "SELECT CASE WHEN path::varchar = '' THEN '' ELSE (path::varchar || '.') END AS prefix\n"
                + "FROM node WHERE node_id = id_from_vos_path(?)";

        String cteCopiedNodes = "SELECT nextval('node_node_id_seq') AS new_node_id,\n"
                + "((SELECT prefix FROM path_prefix) || currval('node_node_id_seq'))::ltree AS new_path,\n"
                + "path, relative_path, parent_path, parent_relative_path, ? AS name,\n"
                + "type, location_id, creator_id, group_write, group_read, is_public\n"
                + "FROM node WHERE node_id = id_from_vos_path(?)\n"
                + "UNION ALL\n"
                + "SELECT nextval('node_node_id_seq') AS new_node_id,\n"
                + "(p.new_path::varchar || '.' || currval('node_node_id_seq'))::ltree,\n"
                + "n.path, n.relative_path, n.parent_path, n.parent_relative_path, n.name,\n"
                + "n.type, n.location_id, n.creator_id, n.group_write, n.group_read, n.is_public\n"
                + "FROM node n\n"
                + "JOIN copied_nodes p ON p.path = n.parent_path";

        String cteCopiedNodesPaths = "SELECT subpath(new_path, 0, nlevel(new_path) - 1) AS new_parent_path,\n"
                + "nlevel(parent_path) - nlevel(parent_relative_path) AS rel_offset, * FROM copied_nodes";

        String parentSelect = "SELECT\n"
                + "new_node_id, new_parent_path,\n"
                + "CASE WHEN nlevel(new_parent_path) = rel_offset THEN ''::ltree ELSE subpath(new_parent_path, rel_offset) END new_parent_relative_path,\n"
                + "name, type, location_id, creator_id, group_write, group_read, is_public, ?\n"
                + "FROM copied_nodes_paths";

        String sql = parentInsert
                + "WITH RECURSIVE path_prefix AS ("
                + ctePathPrefix + "),\n"
                + "copied_nodes AS ("
                + cteCopiedNodes + "),\n"
                + "copied_nodes_paths AS ("
                + cteCopiedNodesPaths + ")\n"
                + parentSelect;
        
        jdbcTemplate.update(conn -> {
            PreparedStatement ps = conn.prepareStatement(cteSQL);
            ps.setString(1, sourceVosPath);
            ps.setString(2, destVosPath);
            ps.setString(3, jobId);
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, destVosParentPath);
            ps.setString(2, destName);
            ps.setString(3, sourceVosPath);
            ps.setString(4, jobId);
            return ps;
        });      
        
+76 −0
Original line number Diff line number Diff line
/*
 * This file is part of vospace-rest
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package it.inaf.oats.vospace;

import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.exception.NodeBusyException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.persistence.DataSourceConfigSingleton;
import it.inaf.oats.vospace.persistence.NodeDAO;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.Transfer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@AutoConfigureMockMvc
@ContextConfiguration(classes = {DataSourceConfigSingleton.class, CopyServiceTest.TestConfig.class})
@TestPropertySource(locations = "classpath:test.properties", properties = {"vospace-authority=example.com!vospace", "file-service-url=http://file-service"})
@TestMethodOrder(OrderAnnotation.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class CopyServiceTest {

    @Value("${vospace-authority}")
    private String authority;
    
    @Autowired
    private MoveService moveService;
    
    @Autowired
    private NodeDAO nodeDao;

    @Autowired
    private HttpServletRequest servletRequest;
       
    @TestConfiguration
    public static class TestConfig {

        /**
         * Necessary because MockBean doesn't work with HttpServletRequest.
         */
        @Bean
        @Primary
        public HttpServletRequest servletRequest() {
            HttpServletRequest request = mock(HttpServletRequest.class);
            User user = new User().setUserId("anonymous");
            when(request.getUserPrincipal()).thenReturn(user);
            return request;
        }
    }
    
    // Stub Test Class
}
+32 −4
Original line number Diff line number Diff line
@@ -226,7 +226,7 @@ public class NodeDAOTest {
        dao.moveNodeBranch(optSourceId.get(), snd.getDestinationNodeLtreePath());

        Optional<Long> optResultId = dao.getNodeId("/test3/group1/m1");
        assertTrue(optSourceId.isPresent());
        assertTrue(optResultId.isPresent());
        optSnd = dao.getShortNodeDescriptor("/test3/group1/m1", "user3", List.of("group1"));
        assertEquals("9.17.10", optSnd.get().getDestinationNodeLtreePath());

@@ -237,6 +237,34 @@ public class NodeDAOTest {

    }

    @Test
    public void testCopyNodeBranch() {
        // Let's copy /test3/m1 to /test3/group1
        Optional<Long> optSourceId = dao.getNodeId("/test3/m1");
        assertTrue(optSourceId.isPresent());

        Optional<Long> optSourceChildId = dao.getNodeId("/test3/m1/m2");
        assertTrue(optSourceChildId.isPresent());

        Optional<Long> optDestParentId = dao.getNodeId("/test3/group1");
        assertTrue(optDestParentId.isPresent());

        dao.copyBranch("/test3/m1", "/test3/group1/copy_of_m1", "pippo");

        Optional<Long> resultId = dao.getNodeId("/test3/group1/copy_of_m1");
        assertTrue(resultId.isPresent());

        Optional<Long> recheckSource = dao.getNodeId("/test3/m1");
        assertTrue(recheckSource.isPresent());

        Optional<Long> resultIdChild = dao.getNodeId("/test3/group1/copy_of_m1/m2");
        assertTrue(resultIdChild.isPresent());

        Optional<Long> recheckSourceChild = dao.getNodeId("/test3/m1/m2");
        assertTrue(recheckSourceChild.isPresent());

    }

    @Test
    public void testRenameNode() {
        String oldPath = "/test1/f1";