Skip to content
Snippets Groups Projects
Commit ec726009 authored by Nicola Fulvio Calabria's avatar Nicola Fulvio Calabria
Browse files

added collection utilities

parent a51cc46e
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,7 @@ package it.inaf.oats.vospace;
import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeCollectionsWrapper;
import it.inaf.oats.vospace.datamodel.collections.NodeDetailsWrapper;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
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);
}
}
......@@ -7,12 +7,21 @@ package it.inaf.oats.vospace;
import it.inaf.oats.vospace.persistence.CollectionsDAO;
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.NodeNotFoundException;
import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
/**
*
......@@ -25,44 +34,128 @@ public class CollectionsService {
@Autowired
private CollectionsDAO collectionsDAO;
@Autowired
private NodeDAO nodeDAO;
public List<NodeCollection> listCollections(String userId) {
List<NodeCollection> result = new ArrayList<>();
this.doCheckUserAuth(userId);
// If user is not authenticated simply return an empty list
if (isUserAuthenticated(userId)) {
result.addAll(
collectionsDAO.getUserNodeCollections(userId));
} else {
throw new PermissionDeniedException("Authentication required");
}
List<NodeCollection> result = new ArrayList<>();
result.addAll(collectionsDAO.getUserNodeCollections(userId));
return result;
}
public void createNewCollection(String collectionTitle, String userId) {
if (isUserAuthenticated(userId)) {
collectionsDAO.createNewCollection(collectionTitle, userId);
} else {
throw new PermissionDeniedException("Authentication required");
}
this.doCheckUserAuth(userId);
collectionsDAO.createNewCollection(collectionTitle, userId);
}
public void deleteCollectionById(Long collectionId, String userId) {
if(isUserAuthenticated(userId))
{
collectionsDAO.deleteCollection(collectionId, userId);
// TODO: throw exceptions if collection not found or deletion requested
// by someone who is not the owner
} else {
this.doCheckUserAuth(userId);
if (collectionsDAO.deleteCollection(collectionId, userId) != 1) {
throw new InvalidArgumentException("Collection not found");
}
}
@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");
}
}
private boolean isUserAuthenticated(String userId) {
return userId != null
&& !userId.equals("anonymous");
}
}
......@@ -6,6 +6,7 @@
package it.inaf.oats.vospace.persistence;
import it.inaf.oats.vospace.datamodel.collections.NodeCollection;
import it.inaf.oats.vospace.datamodel.collections.NodeDetails;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
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) {
String sql = "SELECT collection_id, title, owner_id FROM collections\n"
+ "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 = ?";
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 void deleteCollection(Long collectionId, String userId) {
public int deleteCollection(Long collectionId, String userId) {
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)
......
......@@ -246,11 +246,14 @@ public class NodeDAO {
String userId, List<String> userGroups) {
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"
+ "n.sticky AS is_sticky,\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.type = 'container' AS is_container,\n"
+ "n.type = 'link' AS is_link,\n"
+ "n.job_id IS NOT NULL AS busy_state,\n"
+ "n.immutable AS is_immutable\n"
+ "FROM node n \n"
......@@ -277,14 +280,20 @@ public class NodeDAO {
}
String nodePath = rs.getString("path");
Long nodeId = rs.getLong("node_id");
String creatorId = rs.getString(("creator_id"));
Boolean isContainer = rs.getBoolean(("is_container"));
Boolean isLink = rs.getBoolean(("is_link"));
Boolean isWritable = rs.getBoolean("is_writable");
Boolean isBusy = rs.getBoolean("busy_state");
Boolean isPermissionDenied = rs.getBoolean("is_permission_denied");
Boolean isSticky = rs.getBoolean("is_sticky");
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);
});
......@@ -757,17 +766,32 @@ public class NodeDAO {
public class ShortNodeDescriptor {
private final String nodeLtreePath;
private final Long nodeId;
private final String creatorId;
private final boolean container;
private final boolean link;
private final boolean writable;
private final boolean busy;
private final boolean permissionDenied;
private final boolean sticky;
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.nodeId = nodeId;
this.creatorId = creatorId;
this.container = container;
this.link = link;
this.writable = writable;
this.busy = busy;
this.permissionDenied = permissionDenied;
......@@ -778,10 +802,22 @@ public class NodeDAO {
public String getDestinationNodeLtreePath() {
return nodeLtreePath;
}
public Long getNodeId() {
return this.nodeId;
}
public String getCreatorId() {
return creatorId;
}
public boolean isContainer() {
return container;
}
public boolean isLink() {
return link;
}
public boolean isImmutable() {
return immutable;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment