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

added collection utilities

parent a51cc46e
Loading
Loading
Loading
Loading
+51 −0
Original line number Original line Diff line number Diff line
@@ -8,6 +8,7 @@ package it.inaf.oats.vospace;
import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeCollectionsWrapper;
import it.inaf.oats.vospace.datamodel.collections.NodeCollectionsWrapper;
import it.inaf.oats.vospace.datamodel.collections.NodeDetailsWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.LoggerFactory;
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestController;


/**
/**
@@ -73,4 +75,53 @@ public class CollectionsController {
                
                
    }
    }
    
    
    // add node to collection
    @PutMapping(value = "/addnode/{collectionId}")
    public ResponseEntity<String> addNodeToCollection(
            @PathVariable("collectionId") Long collectionId,
            @RequestParam("vosPath") String vosPath,
            User principal) {
        LOG.debug("add node {} to collection {} called for user {}",
                vosPath, collectionId, principal.getName()
                );
        
        collectionsService.addNodeToCollection(vosPath, collectionId, 
                principal.getName(), principal.getGroups());
        
        return ResponseEntity.ok("Node added");
    }
    
    // add node to collection
    @DeleteMapping(value = "/removenode/{collectionId}")
    public ResponseEntity<String> removeNodeFromCollection(
            @PathVariable("collectionId") Long collectionId,
            @RequestParam("vosPath") String vosPath,
            User principal) {
        LOG.debug("remove node {} from collection {} called for user {}",
                vosPath, collectionId, principal.getName()
                );
        
        collectionsService.removeNodeFromCollection(vosPath, collectionId, 
                principal.getName(), principal.getGroups());
        
        return ResponseEntity.ok("Node added");
    }
    
    @GetMapping(value = "/getnodesincollection/{collectionId}")
    public ResponseEntity<NodeDetailsWrapper> getNodesInCollection(
            @PathVariable("collectionId") Long collectionId,
            User principal)
    {
        LOG.debug("get nodes in collection {} called for user {}", 
                collectionId, principal.getName());
        
        NodeDetailsWrapper ndw = new NodeDetailsWrapper();
        
        ndw.setNodeDetails(
                collectionsService.getNodeDetailsInCollection(
                        collectionId, principal.getName()));
        
        return ResponseEntity.ok(ndw);
    }
    
}
}
+114 −21
Original line number Original line Diff line number Diff line
@@ -7,12 +7,21 @@ package it.inaf.oats.vospace;


import it.inaf.oats.vospace.persistence.CollectionsDAO;
import it.inaf.oats.vospace.persistence.CollectionsDAO;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeDetails;
import it.inaf.oats.vospace.exception.InvalidArgumentException;
import it.inaf.oats.vospace.exception.NodeBusyException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;


/**
/**
 *
 *
@@ -25,37 +34,122 @@ public class CollectionsService {
    @Autowired
    @Autowired
    private CollectionsDAO collectionsDAO;
    private CollectionsDAO collectionsDAO;


    @Autowired
    private NodeDAO nodeDAO;

    public List<NodeCollection> listCollections(String userId) {
    public List<NodeCollection> listCollections(String userId) {


        List<NodeCollection> result = new ArrayList<>();
        this.doCheckUserAuth(userId);


        // If user is not authenticated simply return an empty list
        List<NodeCollection> result = new ArrayList<>();
        if (isUserAuthenticated(userId)) {
        result.addAll(collectionsDAO.getUserNodeCollections(userId));
            result.addAll(
                    collectionsDAO.getUserNodeCollections(userId));
        } else {
            throw new PermissionDeniedException("Authentication required");
        }


        return result;
        return result;


    }
    }


    public void createNewCollection(String collectionTitle, String userId) {
    public void createNewCollection(String collectionTitle, String userId) {
        if (isUserAuthenticated(userId)) {
        this.doCheckUserAuth(userId);
        collectionsDAO.createNewCollection(collectionTitle, userId);
        collectionsDAO.createNewCollection(collectionTitle, userId);
        } else {

            throw new PermissionDeniedException("Authentication required");
        }
    }
    }


    public void deleteCollectionById(Long collectionId, String userId) {
    public void deleteCollectionById(Long collectionId, String userId) {
        if(isUserAuthenticated(userId))
        this.doCheckUserAuth(userId);
        {

            collectionsDAO.deleteCollection(collectionId, userId);
        if (collectionsDAO.deleteCollection(collectionId, userId) != 1) {
            // TODO: throw exceptions if collection not found or deletion requested
            throw new InvalidArgumentException("Collection not found");
            // by someone who is not the owner
        }
        } else {
    }

    @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ)
    public List<NodeDetails> getNodeDetailsInCollection(Long collectionId, String userId) {
        this.doCheckUserAuth(userId);

        Optional<NodeCollection> maybeNc
                = collectionsDAO.getNodeCollectionById(collectionId);

        NodeCollection nc = maybeNc.orElseThrow(
                () -> {
                    return new InvalidArgumentException("Collection not found");
                });

        if (!nc.getOwnerId().equals(userId)) {
            throw new PermissionDeniedException("User doesn't own collection");
        }

        return collectionsDAO.getNodeDetailsInCollection(nc.getId());

    }

    @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ)
    public void addNodeToCollection(String vosPath, Long collectionId,
            String userId, List<String> userGroups) {
        this.doCheckUserAuth(userId);

        this.checkCollectionForOperations(collectionId, userId);

        Long nodeId = this.checkNodeForOperations(vosPath, userId, userGroups);

        collectionsDAO.addNodeToCollection(nodeId, collectionId);

    }

    @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ)
    public void removeNodeFromCollection(String vosPath, Long collectionId,
            String userId, List<String> userGroups) {
        this.doCheckUserAuth(userId);

        this.checkCollectionForOperations(collectionId, userId);

        Long nodeId = checkNodeForOperations(vosPath, userId, userGroups);

        if (collectionsDAO
                .removeNodeFromCollection(nodeId, collectionId) != 1) {
            throw new InvalidArgumentException("Node is not in collection");
        }

    }

    private void checkCollectionForOperations(Long collectionId, String userId) {
        Optional<NodeCollection> maybeNc
                = collectionsDAO.getNodeCollectionById(collectionId);

        NodeCollection nc = maybeNc
                .orElseThrow(() -> {
                    return new InvalidArgumentException("collection doesn't exist");
                });

        if (!nc.getOwnerId().equals(userId)) {
            throw new PermissionDeniedException("User doesn't own collection");
        }
    }

    private Long checkNodeForOperations(String vosPath,
            String userId, List<String> userGroups) {
        Optional<ShortNodeDescriptor> maybeSnd
                = nodeDAO.getShortNodeDescriptor(vosPath, userId, userGroups);

        ShortNodeDescriptor snd = maybeSnd
                .orElseThrow(() -> {
                    return new NodeNotFoundException(vosPath);
                });

        // check if node is owned by the user and not busy
        if (!snd.getCreatorId().equals(userId)) {
            throw new PermissionDeniedException(
                    "User doesn't own node");
        }

        if (snd.isBusy()) {
            throw new NodeBusyException(vosPath);
        }

        return snd.getNodeId();
    }

    private void doCheckUserAuth(String userId) {
        if (!isUserAuthenticated(userId)) {
            throw new PermissionDeniedException("Authentication required");
            throw new PermissionDeniedException("Authentication required");
        }
        }
    }
    }
@@ -64,5 +158,4 @@ public class CollectionsService {
        return userId != null
        return userId != null
                && !userId.equals("anonymous");
                && !userId.equals("anonymous");
    }
    }

}
}
+32 −11
Original line number Original line Diff line number Diff line
@@ -6,6 +6,7 @@
package it.inaf.oats.vospace.persistence;
package it.inaf.oats.vospace.persistence;


import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeDetails;
import java.sql.PreparedStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLException;
@@ -66,6 +67,32 @@ public class CollectionsDAO {


    }
    }
    
    
    public List<NodeDetails> getNodeDetailsInCollection(Long collectionId) {
        String sql = "SELECT node_id, get_vos_path(node_id) as node_vos_path\n"
                + "FROM collections_node WHERE collection_id = ?";
        
        return jdbcTemplate.query(                
                conn-> {
                    PreparedStatement ps = conn.prepareStatement(sql);
                    ps.setLong(1, collectionId);
                    return ps;
                },                
                (row, index) -> {
                    return this.getNodeDetailsFromResultset(row);                    
                });
                
    }
    
    private NodeDetails getNodeDetailsFromResultset(ResultSet rs) 
            throws SQLException
    {
        NodeDetails nd = new NodeDetails();
        nd.setNodeId(rs.getLong("node_id"));
        nd.setNodeVosPath(rs.getString("node_vos_path"));
        
        return nd;
    }
    
    public List<NodeCollection> getUserNodeCollections(String userId) {
    public List<NodeCollection> getUserNodeCollections(String userId) {
        String sql = "SELECT collection_id, title, owner_id FROM collections\n"
        String sql = "SELECT collection_id, title, owner_id FROM collections\n"
                + "WHERE owner_id = ?";
                + "WHERE owner_id = ?";
@@ -96,23 +123,17 @@ public class CollectionsDAO {
        });
        });
    }
    }
    
    
    public void removeNodeFromCollection(Long nodeId, Long collectionId) {
    public int removeNodeFromCollection(Long nodeId, Long collectionId) {
        String sql = "DELETE FROM collections_node WHERE collection_id = ? and node_id = ?";
        String sql = "DELETE FROM collections_node WHERE collection_id = ? and node_id = ?";
        
        
        jdbcTemplate.update(sql, collectionId, nodeId);
        // Returns the number of deleted nodes: must be 1
    }
        return jdbcTemplate.update(sql, collectionId, nodeId);
    
    // TODO complete stub
    public List<NodeCollection> getCollectionsOfNode(Long nodeId) {
        throw new UnsupportedOperationException();        
    }
    }
    
    
    // TODO complete stub: add list of nodes in collection (path list?)    
    public int deleteCollection(Long collectionId, String userId) {       
    
    public void deleteCollection(Long collectionId, String userId) {       
        String sql  = "DELETE FROM collections WHERE collection_id = ? AND owner_id = ?";
        String sql  = "DELETE FROM collections WHERE collection_id = ? AND owner_id = ?";
        
        
        jdbcTemplate.update(sql, collectionId, userId);
        return jdbcTemplate.update(sql, collectionId, userId);
    }
    }


    private NodeCollection getNodeCollectionFromResultset(ResultSet rs)
    private NodeCollection getNodeCollectionFromResultset(ResultSet rs)
+38 −2
Original line number Original line Diff line number Diff line
@@ -246,11 +246,14 @@ public class NodeDAO {
            String userId, List<String> userGroups) {
            String userId, List<String> userGroups) {


        String sql = "SELECT path,\n"
        String sql = "SELECT path,\n"
                + "n.node_id AS node_id\n"
                + "n.creator_id as creator_id\n"
                + "NOT (n.async_trans OR COALESCE(location_type = 'async', FALSE)) AS is_writable,\n"
                + "NOT (n.async_trans OR COALESCE(location_type = 'async', FALSE)) AS is_writable,\n"
                + "n.sticky AS is_sticky,\n"
                + "n.sticky AS is_sticky,\n"
                + "((SELECT COUNT(*) FROM (SELECT UNNEST(?) INTERSECT SELECT UNNEST(n.group_write)) AS allowed_groups ) = 0 AND\n"
                + "((SELECT COUNT(*) FROM (SELECT UNNEST(?) INTERSECT SELECT UNNEST(n.group_write)) AS allowed_groups ) = 0 AND\n"
                + "n.creator_id <> ?) AS is_permission_denied,\n"
                + "n.creator_id <> ?) AS is_permission_denied,\n"
                + "n.type = 'container' AS is_container,\n"
                + "n.type = 'container' AS is_container,\n"
                + "n.type = 'link' AS is_link,\n"
                + "n.job_id IS NOT NULL AS busy_state,\n"
                + "n.job_id IS NOT NULL AS busy_state,\n"
                + "n.immutable AS is_immutable\n"
                + "n.immutable AS is_immutable\n"
                + "FROM node n \n"
                + "FROM node n \n"
@@ -277,14 +280,20 @@ public class NodeDAO {
            }
            }


            String nodePath = rs.getString("path");
            String nodePath = rs.getString("path");
            Long nodeId = rs.getLong("node_id");
            String creatorId = rs.getString(("creator_id"));
            Boolean isContainer = rs.getBoolean(("is_container"));
            Boolean isContainer = rs.getBoolean(("is_container"));
            Boolean isLink = rs.getBoolean(("is_link"));
            Boolean isWritable = rs.getBoolean("is_writable");
            Boolean isWritable = rs.getBoolean("is_writable");
            Boolean isBusy = rs.getBoolean("busy_state");
            Boolean isBusy = rs.getBoolean("busy_state");
            Boolean isPermissionDenied = rs.getBoolean("is_permission_denied");
            Boolean isPermissionDenied = rs.getBoolean("is_permission_denied");
            Boolean isSticky = rs.getBoolean("is_sticky");
            Boolean isSticky = rs.getBoolean("is_sticky");
            Boolean isImmutable = rs.getBoolean("is_immutable");
            Boolean isImmutable = rs.getBoolean("is_immutable");


            ShortNodeDescriptor result = new ShortNodeDescriptor(nodePath, isContainer, isWritable, isBusy, isPermissionDenied, isSticky, isImmutable);
            ShortNodeDescriptor result = 
                    new ShortNodeDescriptor(nodePath, nodeId, creatorId, 
                    isContainer, isLink, isWritable, isBusy, isPermissionDenied, 
                    isSticky, isImmutable);


            return Optional.of(result);
            return Optional.of(result);
        });
        });
@@ -757,17 +766,32 @@ public class NodeDAO {
    public class ShortNodeDescriptor {
    public class ShortNodeDescriptor {


        private final String nodeLtreePath;
        private final String nodeLtreePath;
        private final Long nodeId;
        
        private final String creatorId;


        private final boolean container;
        private final boolean container;
        private final boolean link;
        private final boolean writable;
        private final boolean writable;
        private final boolean busy;
        private final boolean busy;
        private final boolean permissionDenied;
        private final boolean permissionDenied;
        private final boolean sticky;
        private final boolean sticky;
        private final boolean immutable;
        private final boolean immutable;


        public ShortNodeDescriptor(String nodeLtreePath, boolean container, boolean writable, boolean busy, boolean permissionDenied, boolean sticky, boolean immutable) {
        public ShortNodeDescriptor(
                String nodeLtreePath, Long nodeId,
                String creatorId,
                boolean container, boolean link, boolean writable, boolean busy, 
                boolean permissionDenied, boolean sticky, boolean immutable) 
        {
            
            this.nodeLtreePath = nodeLtreePath;
            this.nodeLtreePath = nodeLtreePath;
            this.nodeId = nodeId;
            
            this.creatorId = creatorId;
            
            this.container = container;
            this.container = container;
            this.link = link;
            this.writable = writable;
            this.writable = writable;
            this.busy = busy;
            this.busy = busy;
            this.permissionDenied = permissionDenied;
            this.permissionDenied = permissionDenied;
@@ -779,10 +803,22 @@ public class NodeDAO {
            return nodeLtreePath;
            return nodeLtreePath;
        }
        }
        
        
        public Long getNodeId() {
            return this.nodeId;
        }
        
        public String getCreatorId() {
            return creatorId;
        }

        public boolean isContainer() {
        public boolean isContainer() {
            return container;
            return container;
        }
        }
        
        
        public boolean isLink() {
            return link;
        }

        public boolean isImmutable() {
        public boolean isImmutable() {
            return immutable;
            return immutable;
        }
        }