Commit 180e7076 authored by Nicola Fulvio Calabria's avatar Nicola Fulvio Calabria
Browse files

Changes to moveNode before implementing new tests

parent cd274712
Loading
Loading
Loading
Loading
+24 −11
Original line number Diff line number Diff line
@@ -12,10 +12,9 @@ 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.NodeDAO;
import java.util.List;
import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -58,6 +57,12 @@ public class MoveService {
            return;
        }
        
        //Check if destination is subpath of source
        // Linux-like: "cannot move to a subdirectory of itself" 
        if(destinationPath.startsWith(sourcePath)) {
            throw new IllegalArgumentException("Cannot move node to a subdirectory of its own path");
        }        

        try {
            // Get source node
            Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath);
@@ -71,14 +76,21 @@ public class MoveService {
                throw new PermissionDeniedException(sourcePath);
            }           

            Optional<String> destLtreePath = nodeDao.getLtreePath(destinationPath);
            Optional<ShortNodeDescriptor> destShortNodeDescriptor 
                    = nodeDao.getShortNodeDescriptor(destinationPath, user.getName(), user.getGroups());

            String parentDestLtreePath = null;
            if (destLtreePath.isPresent()) {
            String  destinationNodeLtreePath = null;
            if (destShortNodeDescriptor.isPresent()) {
                // When the destination is an existing ContainerNode, the source SHALL be placed under it (i.e., within the container)
                // TODO: check that this is a ContainerNode using a simple select, otherwise throw an error
                // maybe we could select the type together with the ltree returned by the previous query
                parentDestLtreePath = destLtreePath.get();
                ShortNodeDescriptor snd = destShortNodeDescriptor.get();
                
                if(snd.isBusy()) throw new NodeBusyException(destinationPath);
                // TODO: can split permission denied condition further.
                if(!snd.isWritable()) throw new PermissionDeniedException(destinationPath);
                if(!snd.isContainer()) throw new InternalFaultException("Existing destination is not a container: " + destinationPath);
                
                destinationNodeLtreePath = snd.getDestinationNodeLtreePath();
                
            } else {
                // Compare source and destination paths parents and see if it's just a rename        
                if (NodeUtils.getParentPath(sourcePath)
@@ -91,9 +103,10 @@ public class MoveService {
                }
            }

            if (parentDestLtreePath != null) {
                nodeDao.moveNodeBranch(sourceId, parentDestLtreePath);
            if (destinationNodeLtreePath != null) {
                nodeDao.moveNodeBranch(sourceId, destinationNodeLtreePath);
            }
            
        } catch (CannotSerializeTransactionException ex) {
            // Concurrent transactions attempted to modify this set of nodes            
            throw new NodeBusyException(sourcePath);
+73 −45
Original line number Diff line number Diff line
@@ -279,6 +279,45 @@ public class NodeDAO {
        }
    }

    public Optional<ShortNodeDescriptor> getShortNodeDescriptor(String nodeVosPath,
            String userId, List<String> userGroups) {

        String sql = "SELECT path,\n"
                + "NOT (n.async_trans OR n.sticky OR loc.location_type = 'async' \n"
                + "OR ( (SELECT COUNT(*) FROM (SELECT UNNEST(?) INTERSECT SELECT UNNEST(n.group_write))) = 0 AND\n"
                + "n.creator_id <> ?)) AS is_writable,\n"
                + "n.type = 'container' AS is_container\n"
                + "n.busy_state\n"
                + "FROM node n \n"
                + "JOIN node_vos_path p ON n.node_id = p.node_id \n"
                + "LEFT JOIN location loc ON loc.location_id = n.location_id\n"
                + "WHERE vos_path = ?\n";

        Optional<ShortNodeDescriptor> sndOpt = jdbcTemplate.query(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setArray(1, ps.getConnection().createArrayOf("varchar", userGroups.toArray(String[]::new)));
            ps.setString(2, userId);
            ps.setString(3, nodeVosPath);
            return ps;
        }, rs -> {
            if (!rs.next()) {
                return Optional.empty();
            }

            String nodePath = rs.getString("path");
            Boolean isContainer = rs.getBoolean(("is_container"));
            Boolean isWritable = rs.getBoolean("is_writable");
            Boolean isBusy = rs.getBoolean("busy_state");

            ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy);

            return Optional.of(result);
        });

        return sndOpt;

    }

    public Optional<Node> getNodeById(Long nodeId, boolean enforceTapeStoredCheck) {
        String sql = "SELECT os.vos_path, loc.location_type, n.node_id, type, async_trans, sticky, busy_state, creator_id, group_read, group_write,\n"
                + "is_public, content_length, created_on, last_modified, accept_views, provide_views\n"
@@ -366,54 +405,10 @@ public class NodeDAO {

    }

    /*
    public Optional<Long> getMoveDestinationNodeId(String sourceVosPath, String destVosPath, String userId, List<String> userGroups) {
        
        String destParentVosPath = NodeUtils.getParentPath(destVosPath);       
        
        String sql = "SELECT n.node_id from node n\n"
                + "JOIN node_vos_path os on n.node_id = os.node_id\n"
                + "LEFT JOIN location loc on loc.location_id = n.location_id\n"
                + "WHERE os.vos_path IN( ?, ?)\n"
                + "AND location_type <> 'async'\n"
                + "AND (creator_id = ? OR ? && group_write )\n"
                + "AND NOT n.busy_state AND NOT n.sticky\n"
                + "ORDER BY path DESC LIMIT 1";
        
        Long result = jdbcTemplate.query(sql, ps -> {
            ps.setString(1, destVosPath);
            ps.setString(2, destParentVosPath);
            ps.setArray(3, ps.getConnection().createArrayOf("varchar", userGroups.toArray(String[]::new)));
            ps.setString(4, userId);
        }, row -> {
            if (!row.next()) {
                throw new IllegalStateException("Expected one result");
            }
            return row.getLong(1);
        });
        
        return Optional.ofNullable(result);        
    }
    */

    public void moveNodeBranchNew(String sourceVosPath, 
            Long nodeDestId, String userId, List<String> userGroups) {
        
        String sql = "UPDATE node c SET "
                + "parent_path = (? || SUBPATH(c.path, (SELECT nlevel(parent_path) FROM node WHERE node_id = ?), -1))::ltree, "
                + "parent_relative_path = COALESCE(c.parent_relative_path, c.parent_path) " // not sure about this
                + "FROM node n "
                + "WHERE n.path @> c.path AND n.node_id = ?";
        
                
                        
    }
    

    public void moveNodeBranch(Long sourceRootId, String destParentLtreePath) {

        String sql = "UPDATE node c SET "
                + "parent_path = (? || SUBPATH(c.path, (SELECT nlevel(parent_path) FROM node WHERE node_id = ?), -1))::ltree, "
                + "parent_path = (? || SUBLTREE(c.path, (SELECT nlevel(parent_path) FROM node WHERE node_id = ?), nlevel(c.path)))::ltree, "
                + "parent_relative_path = COALESCE(c.parent_relative_path, c.parent_path) " // not sure about this
                + "FROM node n "
                + "WHERE n.path @> c.path AND n.node_id = ?";
@@ -709,6 +704,39 @@ public class NodeDAO {
        return paths;
    }

    public class ShortNodeDescriptor {

        private final String nodeLtreePath;

        private final boolean container;
        private final boolean writable;
        private final boolean busy;

        public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy) {
            this.nodeLtreePath = nodeLtreePath;
            this.container = container;
            this.writable = writable;
            this.busy = busy;
        }

        public String getDestinationNodeLtreePath() {
            return nodeLtreePath;
        }

        public boolean isContainer() {
            return container;
        }

        public boolean isWritable() {
            return writable;
        }

        public boolean isBusy() {
            return busy;
        }

    }

    private class NodePaths {

        private final String path;