Commit 832fbd87 authored by Sara Bertocco's avatar Sara Bertocco
Browse files

setNode added

parent f70c909f
Loading
Loading
Loading
Loading
+18 −8
Original line number Diff line number Diff line
@@ -10,9 +10,12 @@ import it.inaf.oats.vospace.persistence.NodeDAO;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@@ -20,6 +23,8 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class SetNodeController extends BaseNodeController {
    
    private static final Logger LOG = LoggerFactory.getLogger(SetNodeController.class);

    @Autowired
    private NodeDAO nodeDao;

@@ -29,9 +34,10 @@ public class SetNodeController extends BaseNodeController {
    @PostMapping(value = {"/nodes", "/nodes/**"},
            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public Node setNode(@RequestBody Node node, User principal, HttpServletRequest request) {
    public ResponseEntity<Node> setNode(@RequestBody Node node, User principal, HttpServletRequest request) {

        String path = getPath();
        LOG.debug("setNode called for path {}", path);
        
        //The service SHALL throw a HTTP 404 status code including a NodeNotFound 
        //fault in the entity-body if the target Node does not exist
@@ -48,10 +54,15 @@ public class SetNodeController extends BaseNodeController {
        // The service SHALL throw a HTTP 403 status code including a PermissionDenied fault 
        // in the entity-body if the request attempts to modify a read-only Property.
        // This method cannot be used to modify the Node type.
        String storedNodeType = toBeModifiedNode.getType();
        String newNodeType = node.getType();
        if(!storedNodeType.equals(newNodeType)) {
            LOG.debug("setNode trying to modify type. Stored ", storedNodeType + ", requested " + newNodeType);
           throw new PermissionDeniedException(path); 
        }
        // This method cannot be used to modify the accepts or provides list of Views for the Node.
        // This method cannot be used to create children of a container Node.
        
        
        // For this case, throws exception in NodeDAO
        // This method cannot be used to create children of a container Node. (Non capisco, Sara)        
        
        // If a parent node in the URI path does not exist then the service SHALL throw a 
        // HTTP 404 status code including a ContainerNotFound fault in the entity-body
@@ -79,16 +90,15 @@ public class SetNodeController extends BaseNodeController {
        }        
        
        
        
        
        //The service SHOULD throw a HTTP 500 status code including an InternalFault fault 
        // in the entity-body if the operation fails
        // Done in NodeDAO
       
        // to be fixed
        // A HTTP 200 status code and a Node representation in the entity-body The full 
        // expanded record for the node SHALL be returned, including any xsi:type 
        // specific extensions.
        return node;
        return ResponseEntity.ok(nodeDao.setNode(node));
        
    }    
    
+209 −13
Original line number Diff line number Diff line
package it.inaf.oats.vospace.persistence;

import it.inaf.oats.vospace.DeleteNodeController;
import it.inaf.oats.vospace.datamodel.NodeProperties;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import java.sql.Array;
import net.ivoa.xml.vospace.v2.Node;
import java.sql.PreparedStatement;
@@ -22,6 +25,8 @@ import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.Property;
import net.ivoa.xml.vospace.v2.StructuredDataNode;
import net.ivoa.xml.vospace.v2.View;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -30,6 +35,8 @@ import org.springframework.stereotype.Repository;
@Repository
public class NodeDAO {

    private static final Logger LOG = LoggerFactory.getLogger(DeleteNodeController.class);
    
    @Value("${vospace-authority}")
    private String authority;

@@ -46,17 +53,7 @@ public class NodeDAO {
        String path = nodeURI.replaceAll("vos://[^/]+", "");
        String parentPath = NodeUtils.getParentPath(path);

        String sql = "SELECT path, relative_path from "
                + "node n join node_vos_path p on n.node_id = p.node_id "
                + "where p.vos_path = ?";

        List<NodePaths> paths = jdbcTemplate.query(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, parentPath);
            return ps;
        }, (row, index) -> {
            return getPathsFromResultSet(row);
        });
        List<NodePaths> paths = getNodePathsFromDB(nodeURI);

        if (paths.isEmpty()) {
            throw new IllegalStateException("Unable to find parent node during node creation");
@@ -126,6 +123,101 @@ public class NodeDAO {
        return Optional.of(node);
    }

    
    public Node setNode(Node newNode) {       
        
        // Verify that the node is in the database
        String nodeURI = newNode.getUri();
        List<NodePaths> paths = getNodePathsFromDB(nodeURI);

        if (paths.isEmpty()) {
            throw new IllegalStateException("Unable to find node during node update");
        }
        if (paths.size() > 1) {
            throw new IllegalStateException("Multiple ltree parent paths  during node update");
        }      
            
        // This method cannot be used to modify the accepts or provides list of Views for the Node.
        // Only DataNodes has Views (see VOSpace Data Model
        // Check if trying to change views getting Views (if present) of the modified node 
        if (newNode instanceof DataNode) {
            DataNode dataNode = (DataNode)newNode;
            List<View> requestedAcceptedViews = dataNode.getAccepts();           
            List<View> savedAcceptedViews = getAcceptedViewsFromDB(paths.get(0).getPath());
            // Get the Views of the saved node
            List<View> requestedProvidedViews = dataNode.getProvides();
            List<View> savedProvidedViews = getProvidedViewsFromDB(paths.get(0).getPath());
            
            if (!requestedAcceptedViews.isEmpty()) {
                //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle
                // Check if requestedAcceptedViews are different from the already stored
                if (!checkIfViewsAreEquals(requestedAcceptedViews,savedAcceptedViews)) {
                    LOG.debug("setNode trying to modify accepted Views.");
                    throw new PermissionDeniedException("Trying to modify accepted views");
                }
            }
            
            if (!requestedProvidedViews.isEmpty()) {
                //se sono non nulle, devo fare i controlli, altrimenti di sicuro l'utente non sta chiedendo di cambiarle
                // Check if requestedAcceptedViews are different from the already stored
                if (!checkIfViewsAreEquals(requestedProvidedViews,savedProvidedViews)) {
                    LOG.debug("setNode trying to modify provided Views.");
                    throw new PermissionDeniedException("Trying to modify provided views");
                }     
            }
        }
        
        StringBuilder sb = new StringBuilder();
        
        sb.append("UPDATE node");
        sb.append(" SET owner_id = ?, group_read = ?, group_write = ?, is_public = ? " );
        sb.append(" FROM node_vos_path p WHERE p.vos_path = ? AND p.node_id = node.node_id ");

        jdbcTemplate.update(conn -> {
            PreparedStatement ps = conn.prepareStatement(sb.toString());
            int i = 0;
            ps.setString(++i, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("creator")));
            ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupread"))));
            ps.setArray(++i, fromPropertyToArray(ps, NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("groupwrite"))));
            ps.setBoolean(++i, Boolean.valueOf(NodeProperties.getPropertiesStringByURI(newNode, NodeProperties.getPropertyURI("publicread"))));
            ps.setString(++i, paths.get(0).getPath());
            return ps;
        });
        
        return newNode;
        
        
    }
    
    
    private Optional<Node> getNode(String path) {
        
        String sql = "SELECT 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"
                + "FROM node WHERE path = ?";
        
        List<Node> nodes = jdbcTemplate.query(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, path);
            return ps;
        }, (row, index) -> {
            return getNodeFromResultSet(row);
        });    

        if (nodes.isEmpty()) {
            return Optional.empty();
        }

        // Query returns the node (can be container or data)
        Node node = nodes.get(0);

        return Optional.of(node);
        
    }
    
    
    
    
    private String getFirstLevelChildrenSelector(String path) {
        String select = "(SELECT path FROM node WHERE node_id = (SELECT node_id FROM node_vos_path WHERE vos_path = ?))::varchar || '";

@@ -322,10 +414,11 @@ public class NodeDAO {
        return null;
    }

    private List<View> getViews(Array array) throws SQLException {
    private List<View> getViews(Array array) {
        if (array == null) {
            return null;
        }
        try {
            return Arrays.stream((String[]) array.getArray())
                .map(uri -> {
                    View view = new View();
@@ -333,6 +426,100 @@ public class NodeDAO {
                    return view;
                })
                .collect(Collectors.toList());
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
        
    }

    
    private boolean checkIfViewsAreEquals(List<View> viewsA, List<View> viewsB) {
        
        boolean viewsAreEqual = true;
        if ((viewsA == null || viewsA.isEmpty()) &&
                (viewsB == null || viewsB.isEmpty()))
            return true;
        
        if ((viewsA == null || viewsA.isEmpty()) ||
                (viewsB == null || viewsB.isEmpty()))
            return false;
        
        if( viewsA.size() != viewsB.size())
            return false;
        
        for(int i = 0; i < viewsA.size(); i++) {
            String viewUriA = viewsA.get(i).getUri();
            boolean found = false;
            for(int j = 0; j < viewsB.size(); j++) {
                String viewUriB = viewsB.get(i).getUri();            
                if (viewUriA.compareTo(viewUriB) == 0) {
                    found = true;
                }
            }
            
            if(found == false) {
                viewsAreEqual = false;
                break;
            }
        }
        
        return viewsAreEqual;
        
    }
    
    
    private List<View> getAcceptedViewsFromDB(String nodePath) {
        
        return getViewsFromDB(nodePath, "accept_views");
        
    }
    
    
    private List<View> getProvidedViewsFromDB(String nodePath) {
        
        return getViewsFromDB(nodePath, "provide_views");
        
    }
    
    
    private List<View> getViewsFromDB(String nodePath, String viewType) {
        
        String sql = "SELECT accept_views FROM node WHERE node_id = "
                + "(SELECT node_id FROM node_vos_path WHERE vos_path = ?)";

        Array results = jdbcTemplate.query(sql, ps -> {
            ps.setString(1, nodePath);
        }, (rs) -> {
            if(rs.next())
                return rs.getArray("accept_views");
            else 
                return null;
        });
        
        return getViews(results);
          
    }
    
          
    private List<NodePaths> getNodePathsFromDB(String nodeURI) {        
        
        //String nodeURI = myNode.getUri();
        String path = nodeURI.replaceAll("vos://[^/]+", "");
        String parentPath = NodeUtils.getParentPath(path);

        String sql = "SELECT path, relative_path from "
                + "node n join node_vos_path p on n.node_id = p.node_id "
                + "where p.vos_path = ?";

        List<NodePaths> paths = jdbcTemplate.query(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, parentPath);
            return ps;
        }, (row, index) -> {
            return getPathsFromResultSet(row);
        });
    
        return paths;
    }

    private class NodePaths {
@@ -350,5 +537,14 @@ public class NodeDAO {
        public String toString() {
            return relativePath + " " + path;
        }
        
        public String getPath() {
            return this.path;
        } 
        
        public String getRelativePath() {
            return this.relativePath;
        } 
        
    }
}