Skip to content
Snippets Groups Projects
Commit 7456a69e authored by Sara Bertocco's avatar Sara Bertocco
Browse files

Aligning with master

parents 7eeb9e8f 6456e46e
No related branches found
No related tags found
No related merge requests found
Showing
with 681 additions and 60 deletions
package it.inaf.oats.vospace; package it.inaf.oats.vospace;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.persistence.JobDAO; import it.inaf.oats.vospace.persistence.JobDAO;
import net.ivoa.xml.uws.v1.ExecutionPhase; import net.ivoa.xml.uws.v1.ExecutionPhase;
import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.vospace.v2.Protocol;
import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -43,35 +45,48 @@ public class JobService { ...@@ -43,35 +45,48 @@ public class JobService {
private void startJob(JobSummary job) { private void startJob(JobSummary job) {
switch (getJobType(job)) { Transfer transfer = uriService.getTransfer(job);
switch (getJobType(transfer)) {
case pullToVoSpace: case pullToVoSpace:
handlePullToVoSpace(job); handlePullToVoSpace(job, transfer);
break; break;
case pullFromVoSpace: case pullFromVoSpace:
case pushToVoSpace: case pushToVoSpace:
handleVoSpaceUrlsListResult(job); handleVoSpaceUrlsListResult(job, transfer);
break; break;
default: default:
throw new UnsupportedOperationException("Not implemented yet"); throw new UnsupportedOperationException("Not implemented yet");
} }
} }
private void handlePullToVoSpace(JobSummary job) { private void handlePullToVoSpace(JobSummary job, Transfer transfer) {
// TODO: check protocol
for (Protocol protocol : transfer.getProtocols()) {
switch (protocol.getUri()) {
case "ia2:async-recall":
asyncTransfService.startJob(job); asyncTransfService.startJob(job);
return;
case "ivo://ivoa.net/vospace/core#httpget":
String nodeUri = transfer.getTarget();
String contentUri = protocol.getEndpoint();
uriService.setNodeRemoteLocation(nodeUri, contentUri);
uriService.setTransferJobResult(job, transfer);
jobDAO.updateJob(job);
return;
default:
throw new InternalFaultException("Unsupported pullToVoSpace protocol: " + protocol.getUri());
}
}
} }
private void handleVoSpaceUrlsListResult(JobSummary job) { private void handleVoSpaceUrlsListResult(JobSummary job, Transfer transfer) {
job.setPhase(ExecutionPhase.EXECUTING); job.setPhase(ExecutionPhase.EXECUTING);
uriService.setTransferJobResult(job); uriService.setTransferJobResult(job, transfer);
jobDAO.updateJob(job); jobDAO.updateJob(job);
} }
private JobType getJobType(JobSummary job) { private JobType getJobType(Transfer transfer) {
// TODO: check types
Transfer transfer = (Transfer) job.getJobInfo().getAny().get(0);
return JobType.valueOf(transfer.getDirection()); return JobType.valueOf(transfer.getDirection());
} }
......
package it.inaf.oats.vospace;
import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.LinkFoundException;
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 javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SetNodeController extends BaseNodeController {
@Autowired
private NodeDAO nodeDao;
@Value("${vospace-authority}")
private String authority;
@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) {
String path = getPath();
//The service SHALL throw a HTTP 404 status code including a NodeNotFound
//fault in the entity-body if the target Node does not exist
Node toBeModifiedNode = nodeDao.listNode(path)
.orElseThrow(() -> new NodeNotFoundException(path));
// The service SHALL throw a HTTP 403 status code including a PermissionDenied fault
// in the entity-body if the user does not have permissions to perform the operation
if(!NodeUtils.checkIfWritable(toBeModifiedNode, principal.getName(), principal.getGroups())) {
throw new PermissionDeniedException(path);
}
// 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.
// 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.
// 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
// For example, given the URI path /a/b/c, the service must throw a HTTP 404 status
// code including a ContainerNotFound fault in the entity-body if either /a or /a/b
// do not exist.
List<String> pathComponents = NodeUtils.subPathComponents(path);
if (pathComponents.size() == 0) {
// Manage root node
throw new PermissionDeniedException("root");
} else {
// Manage all precursors in full path
for (int i = 0; i < pathComponents.size(); i++) {
String tmpPath = pathComponents.get(i);
Node mynode = nodeDao.listNode(tmpPath)
.orElseThrow(() -> new NodeNotFoundException(tmpPath));
if (mynode.getType().equals("vos:LinkNode") && i < pathComponents.size()-1) // a LinkNode leaf can be deleted
throw new LinkFoundException(tmpPath);
}
}
//The service SHOULD throw a HTTP 500 status code including an InternalFault fault
// in the entity-body if the operation fails
// 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;
}
}
...@@ -5,11 +5,18 @@ import it.inaf.ia2.aa.data.User; ...@@ -5,11 +5,18 @@ import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.rap.client.call.TokenExchangeRequest; import it.inaf.ia2.rap.client.call.TokenExchangeRequest;
import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeProperties;
import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath; import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.persistence.LocationDAO;
import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.model.Location;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.uws.v1.ResultReference; import net.ivoa.xml.uws.v1.ResultReference;
import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Node;
...@@ -31,21 +38,25 @@ public class UriService { ...@@ -31,21 +38,25 @@ public class UriService {
@Autowired @Autowired
private NodeDAO nodeDao; private NodeDAO nodeDao;
@Autowired
private LocationDAO locationDAO;
@Autowired @Autowired
private HttpServletRequest servletRequest; private HttpServletRequest servletRequest;
@Autowired @Autowired
private ServletRapClient rapClient; private ServletRapClient rapClient;
public void setTransferJobResult(JobSummary job) { public void setTransferJobResult(JobSummary job, Transfer transfer) {
List<ResultReference> results = new ArrayList<>(); List<ResultReference> results = new ArrayList<>();
ResultReference result = new ResultReference(); ResultReference result = new ResultReference();
result.setHref(getEndpoint(job)); result.setHref(getEndpoint(job, transfer));
results.add(result); results.add(result);
job.setResults(results); job.setResults(results);
job.setPhase(ExecutionPhase.COMPLETED);
} }
public void setSyncTransferEndpoints(JobSummary job) { public void setSyncTransferEndpoints(JobSummary job) {
...@@ -58,22 +69,35 @@ public class UriService { ...@@ -58,22 +69,35 @@ public class UriService {
&& !"ivo://ivoa.net/vospace/core#httpput".equals(protocol.getUri())) { && !"ivo://ivoa.net/vospace/core#httpput".equals(protocol.getUri())) {
throw new IllegalStateException("Unsupported protocol " + protocol.getUri()); throw new IllegalStateException("Unsupported protocol " + protocol.getUri());
} }
protocol.setEndpoint(getEndpoint(job)); protocol.setEndpoint(getEndpoint(job, transfer));
} }
private String getEndpoint(JobSummary job) { private String getEndpoint(JobSummary job, Transfer transfer) {
Transfer transfer = getTransfer(job);
String relativePath = transfer.getTarget().substring("vos://".length() + authority.length()); String relativePath = transfer.getTarget().substring("vos://".length() + authority.length());
Node node = nodeDao.listNode(relativePath).orElseThrow(() -> new NodeNotFoundException(relativePath)); Node node = nodeDao.listNode(relativePath).orElseThrow(() -> new NodeNotFoundException(relativePath));
// TODO build the path according to node type Location location = locationDAO.getNodeLocation(relativePath).orElseThrow(()
// -> new InternalFaultException("No registered location found for vos_path " + relativePath));
String endpoint = fileServiceUrl + urlEncodePath(relativePath) + "?jobId=" + job.getJobId();
if (!"true".equals(NodeProperties.getNodePropertiesListByURI(node, NodeProperties.PUBLIC_READ_URI))) { String endpoint;
switch (location.getType()) {
case PORTAL:
String fileName = nodeDao.getNodeOsName(relativePath);
endpoint = "http://" + location.getSource().getHostname() + location.getSource().getBasePath();
if (!endpoint.endsWith("/")) {
endpoint += "/";
}
endpoint += fileName;
break;
default:
endpoint = fileServiceUrl + urlEncodePath(relativePath);
}
endpoint += "?jobId=" + job.getJobId();
if (!"true".equals(NodeProperties.getNodePropertyAsListByURI(node, NodeProperties.PUBLIC_READ_URI))) {
endpoint += "&token=" + getEndpointToken(fileServiceUrl + relativePath); endpoint += "&token=" + getEndpointToken(fileServiceUrl + relativePath);
} }
...@@ -85,8 +109,7 @@ public class UriService { ...@@ -85,8 +109,7 @@ public class UriService {
String token = ((User) servletRequest.getUserPrincipal()).getAccessToken(); String token = ((User) servletRequest.getUserPrincipal()).getAccessToken();
if (token == null) { if (token == null) {
// TODO: use PermissionDenied VoSpaceException throw new PermissionDeniedException("Token is null");
throw new IllegalStateException("Token is null");
} }
TokenExchangeRequest exchangeRequest = new TokenExchangeRequest() TokenExchangeRequest exchangeRequest = new TokenExchangeRequest()
...@@ -97,8 +120,39 @@ public class UriService { ...@@ -97,8 +120,39 @@ public class UriService {
return rapClient.exchangeToken(exchangeRequest, servletRequest); return rapClient.exchangeToken(exchangeRequest, servletRequest);
} }
private Transfer getTransfer(JobSummary job) { public void setNodeRemoteLocation(String nodeUri, String contentUri) {
// TODO add checks on data type
URL url;
try {
url = new URL(contentUri);
} catch (MalformedURLException ex) {
throw new InternalFaultException(ex);
}
Location location = locationDAO.findPortalLocation(url.getHost()).orElseThrow(()
-> new InternalFaultException("No registered location found for host " + url.getHost()));
String vosPath = nodeUri.replaceAll("vos://[^/]+", "");
String fileName = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
nodeDao.setNodeLocation(vosPath, location.getId(), fileName);
}
public Transfer getTransfer(JobSummary job) {
List<Object> jobPayload = job.getJobInfo().getAny();
if (jobPayload.isEmpty()) {
throw new IllegalStateException("Empty job payload for job " + job.getJobId());
}
if (jobPayload.size() > 1) {
throw new IllegalStateException("Multiple objects in job payload not supported");
}
if (!(jobPayload.get(0) instanceof Transfer)) {
throw new IllegalStateException(jobPayload.get(0).getClass().getCanonicalName()
+ " not supported as job payload. Job id: " + job.getJobId());
}
return (Transfer) job.getJobInfo().getAny().get(0); return (Transfer) job.getJobInfo().getAny().get(0);
} }
} }
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package it.inaf.oats.vospace.exception; package it.inaf.oats.vospace.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) // Status code 500 @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) // Status code 500
public class InternalFaultException extends VoSpaceException { public class InternalFaultException extends VoSpaceException {
private static final Logger LOG = LoggerFactory.getLogger(InternalFaultException.class);
public InternalFaultException(String msg) { public InternalFaultException(String msg) {
super("InternalFaultException: " + msg); super("InternalFaultException: " + msg);
} }
public InternalFaultException(Throwable cause) {
super("InternalFaultException: " + getMessage(cause));
}
private static String getMessage(Throwable cause) {
LOG.error("Exception caught", cause);
return cause.getMessage() != null ? cause.getMessage() : cause.getClass().getCanonicalName();
}
} }
package it.inaf.oats.vospace.persistence;
import it.inaf.oats.vospace.persistence.model.Location;
import it.inaf.oats.vospace.persistence.model.LocationType;
import it.inaf.oats.vospace.persistence.model.Storage;
import it.inaf.oats.vospace.persistence.model.StorageType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class LocationDAO {
private final JdbcTemplate jdbcTemplate;
@Autowired
public LocationDAO(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public Optional<Location> findPortalLocation(String host) {
String sql = "SELECT location_id, location_type, storage_src_id, storage_dest_id,\n"
+ "storage_id, storage_type, base_path, hostname\n"
+ "FROM location l\n"
+ "JOIN storage s ON l.storage_src_id = s.storage_id OR l.storage_dest_id = s.storage_id\n"
+ "WHERE hostname = ?";
return jdbcTemplate.query(sql, ps -> {
ps.setString(1, host);
}, rs -> {
return getLocation(rs, () -> new IllegalStateException("Found multiple locations for the same hostname"));
});
}
public Optional<Location> getNodeLocation(String vosPath) {
String sql = "SELECT location_id, location_type, storage_src_id, storage_dest_id,\n"
+ "storage_id, storage_type, base_path, hostname\n"
+ "FROM location l\n"
+ "JOIN storage s ON l.storage_src_id = s.storage_id OR l.storage_dest_id = s.storage_id\n"
+ "WHERE location_id = (\n"
+ "SELECT location_id FROM node n\n"
+ "JOIN node_vos_path p ON n.node_id = p.node_id\n"
+ "WHERE p.vos_path = ?)";
return jdbcTemplate.query(sql, ps -> {
ps.setString(1, vosPath);
}, rs -> {
return getLocation(rs, () -> new IllegalStateException("Found multiple locations for the same vos_path"));
});
}
private Optional<Location> getLocation(ResultSet rs, Supplier<IllegalStateException> exceptionSupplier) throws SQLException {
List<Location> locations = getLocations(rs);
if (locations.isEmpty()) {
return Optional.empty();
}
if (locations.size() > 1) {
throw exceptionSupplier.get();
}
return Optional.of(locations.get(0));
}
private List<Location> getLocations(ResultSet rs) throws SQLException {
Map<Integer, Storage> storagesMap = new HashMap<>();
Map<Integer, Location> locationsMap = new HashMap<>();
while (rs.next()) {
int locationId = rs.getInt("location_id");
Location location = locationsMap.get(locationId);
if (location == null) {
location = new Location();
location.setId(locationId);
locationsMap.put(locationId, location);
}
location.setType(LocationType.parse(rs.getString("location_type")));
int storageId = rs.getInt("storage_id");
Storage storage = storagesMap.get(storageId);
if (storage == null) {
storage = new Storage();
storage.setId(storageId);
storagesMap.put(storageId, storage);
}
storage.setType(StorageType.parse(rs.getString("storage_type")));
storage.setBasePath(rs.getString("base_path"));
storage.setHostname(rs.getString("hostname"));
Storage storageSrc = storagesMap.get(rs.getInt("storage_src_id"));
if (storageSrc != null) {
location.setSource(storageSrc);
}
Storage storageDest = storagesMap.get(rs.getInt("storage_dest_id"));
if (storageDest != null) {
location.setDestination(storageDest);
}
}
return new ArrayList<>(locationsMap.values());
}
}
...@@ -2,6 +2,8 @@ package it.inaf.oats.vospace.persistence; ...@@ -2,6 +2,8 @@ package it.inaf.oats.vospace.persistence;
import it.inaf.oats.vospace.datamodel.NodeProperties; 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.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.InternalFaultException;
import java.sql.Array; import java.sql.Array;
import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Node;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
...@@ -242,6 +244,35 @@ public class NodeDAO { ...@@ -242,6 +244,35 @@ public class NodeDAO {
return jdbcTemplate.queryForObject(sql, args, types, Integer.class); return jdbcTemplate.queryForObject(sql, args, types, Integer.class);
} }
public String getNodeOsName(String vosPath) {
String sql = "SELECT \n"
+ "(CASE WHEN os_name IS NOT NULL THEN os_name ELSE name END) AS os_name\n"
+ "FROM node n\n"
+ "JOIN node_vos_path p ON n.node_id = p.node_id\n"
+ "WHERE p.vos_path = ?";
Object[] args = {vosPath};
int[] types = {Types.VARCHAR};
return jdbcTemplate.queryForObject(sql, args, types, String.class);
}
public void setNodeLocation(String vosPath, int locationId, String nodeOsName) {
String sql = "UPDATE node SET location_id = ?, os_name = ? WHERE node_id = "
+ "(SELECT node_id FROM node_vos_path WHERE vos_path = ?)";
int updated = jdbcTemplate.update(sql, ps -> {
ps.setInt(1, locationId);
ps.setString(2, nodeOsName);
ps.setString(3, vosPath);
});
if (updated != 1) {
throw new InternalFaultException("Unable to set node location for path " + vosPath);
}
}
private String getGroupsString(ResultSet rs, String column) throws SQLException { private String getGroupsString(ResultSet rs, String column) throws SQLException {
Array array = rs.getArray(column); Array array = rs.getArray(column);
if (array == null) { if (array == null) {
......
package it.inaf.oats.vospace.persistence.model;
public class Location {
private int id;
private LocationType type;
private Storage source;
private Storage destination;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public LocationType getType() {
return type;
}
public void setType(LocationType type) {
this.type = type;
}
public Storage getSource() {
return source;
}
public void setSource(Storage source) {
this.source = source;
}
public Storage getDestination() {
return destination;
}
public void setDestination(Storage destination) {
this.destination = destination;
}
}
package it.inaf.oats.vospace.persistence.model;
public enum LocationType {
ASYNC("async"),
PORTAL("portal"),
USER("user");
private final String name;
private LocationType(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public static LocationType parse(String value) {
for (LocationType type : LocationType.values()) {
if (type.name.equals(value)) {
return type;
}
}
throw new IllegalArgumentException("Invalid LocationType " + value);
}
}
package it.inaf.oats.vospace.persistence.model;
public class Storage {
private int id;
private StorageType type;
private String basePath;
private String hostname;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public StorageType getType() {
return type;
}
public void setType(StorageType type) {
this.type = type;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
}
package it.inaf.oats.vospace.persistence.model;
public enum StorageType {
COLD("cold"),
HOT("hot"),
LOCAL("local"),
PORTAL("portal");
private final String name;
private StorageType(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public static StorageType parse(String value) {
for (StorageType type : StorageType.values()) {
if (type.name.equals(value)) {
return type;
}
}
throw new IllegalArgumentException("Invalid StorageType " + value);
}
}
...@@ -187,11 +187,6 @@ public class DeleteNodeControllerTest { ...@@ -187,11 +187,6 @@ public class DeleteNodeControllerTest {
} }
private LinkNode getWritableLinkNode(String path) { private LinkNode getWritableLinkNode(String path) {
LinkNode myNode = new LinkNode(); LinkNode myNode = new LinkNode();
// Set parent node address at / // Set parent node address at /
......
...@@ -3,7 +3,11 @@ package it.inaf.oats.vospace; ...@@ -3,7 +3,11 @@ package it.inaf.oats.vospace;
import it.inaf.ia2.aa.data.User; import it.inaf.ia2.aa.data.User;
import static it.inaf.oats.vospace.VOSpaceXmlTestUtil.loadDocument; import static it.inaf.oats.vospace.VOSpaceXmlTestUtil.loadDocument;
import it.inaf.oats.vospace.persistence.JobDAO; import it.inaf.oats.vospace.persistence.JobDAO;
import it.inaf.oats.vospace.persistence.LocationDAO;
import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.model.Location;
import it.inaf.oats.vospace.persistence.model.LocationType;
import it.inaf.oats.vospace.persistence.model.Storage;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Timestamp; import java.sql.Timestamp;
...@@ -41,6 +45,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers. ...@@ -41,6 +45,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import static org.mockito.ArgumentMatchers.argThat;
@SpringBootTest @SpringBootTest
@AutoConfigureMockMvc @AutoConfigureMockMvc
...@@ -55,11 +62,32 @@ public class TransferControllerTest { ...@@ -55,11 +62,32 @@ public class TransferControllerTest {
private NodeDAO nodeDao; private NodeDAO nodeDao;
@MockBean @MockBean
private AsyncTransferService tapeService; private LocationDAO locationDao;
@MockBean
private AsyncTransferService asyncTransfService;
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@BeforeEach
public void init() {
Location asyncLocation = new Location();
asyncLocation.setType(LocationType.ASYNC);
asyncLocation.setId(1);
when(locationDao.getNodeLocation(eq("/mynode"))).thenReturn(Optional.of(asyncLocation));
Location portalLocation = new Location();
portalLocation.setType(LocationType.PORTAL);
portalLocation.setId(2);
Storage portalStorage = new Storage();
portalStorage.setHostname("archive.lbto.org");
portalStorage.setBasePath("/files");
portalLocation.setSource(portalStorage);
when(locationDao.getNodeLocation(eq("/portalnode"))).thenReturn(Optional.of(portalLocation));
when(locationDao.findPortalLocation(any())).thenReturn(Optional.of(portalLocation));
}
@Test @Test
public void testPullFromVoSpaceAsync() throws Exception { public void testPullFromVoSpaceAsync() throws Exception {
...@@ -99,9 +127,32 @@ public class TransferControllerTest { ...@@ -99,9 +127,32 @@ public class TransferControllerTest {
} }
@Test @Test
public void testPullToVoSpace() throws Exception { public void testPullToVoSpaceTape() throws Exception {
testPullToVoSpace("/mynode", getResourceFileContent("pullToVoSpace-tape.xml"));
verify(asyncTransfService, times(1)).startJob(any());
}
@Test
public void testPullToVoSpacePortal() throws Exception {
when(nodeDao.getNodeOsName(eq("/portalnode"))).thenReturn("file.fits");
String requestBody = getResourceFileContent("pullToVoSpace.xml"); testPullToVoSpace("/portalnode", getResourceFileContent("pullToVoSpace-portal.xml"));
verify(nodeDao, times(1)).setNodeLocation(eq("/portalnode"), eq(2), eq("lbcr.20130512.060722.fits.gz"));
verify(jobDao, times(1)).updateJob(argThat(j -> {
assertTrue(j.getResults().get(0).getHref().startsWith("http://archive.lbto.org"));
assertEquals(ExecutionPhase.COMPLETED, j.getPhase());
return true;
}));
}
private void testPullToVoSpace(String path, String requestBody) throws Exception {
Node node = mockPublicDataNode();
when(nodeDao.listNode(eq(path))).thenReturn(Optional.of(node));
String redirect = mockMvc.perform(post("/transfers?PHASE=RUN") String redirect = mockMvc.perform(post("/transfers?PHASE=RUN")
.content(requestBody) .content(requestBody)
...@@ -112,8 +163,6 @@ public class TransferControllerTest { ...@@ -112,8 +163,6 @@ public class TransferControllerTest {
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
assertThat(redirect, matchesPattern("^/transfers/.*")); assertThat(redirect, matchesPattern("^/transfers/.*"));
verify(tapeService, times(1)).startJob(any());
} }
@Test @Test
...@@ -210,7 +259,6 @@ public class TransferControllerTest { ...@@ -210,7 +259,6 @@ public class TransferControllerTest {
.andDo(print()) .andDo(print())
.andExpect(status().is4xxClientError()); .andExpect(status().is4xxClientError());
String xml2 = mockMvc.perform(get("/transfers") String xml2 = mockMvc.perform(get("/transfers")
.header("Authorization", "Bearer user1_token") .header("Authorization", "Bearer user1_token")
.accept(MediaType.APPLICATION_XML)) .accept(MediaType.APPLICATION_XML))
......
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package it.inaf.oats.vospace;
/**
*
* @author bertocco
*/
public class UpdateNodeController {
}
...@@ -2,7 +2,10 @@ package it.inaf.oats.vospace; ...@@ -2,7 +2,10 @@ package it.inaf.oats.vospace;
import it.inaf.ia2.aa.ServletRapClient; import it.inaf.ia2.aa.ServletRapClient;
import it.inaf.ia2.aa.data.User; import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.persistence.LocationDAO;
import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.model.Location;
import it.inaf.oats.vospace.persistence.model.LocationType;
import java.util.Optional; import java.util.Optional;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.uws.v1.JobSummary;
...@@ -11,11 +14,13 @@ import net.ivoa.xml.vospace.v2.Node; ...@@ -11,11 +14,13 @@ import net.ivoa.xml.vospace.v2.Node;
import net.ivoa.xml.vospace.v2.Property; import net.ivoa.xml.vospace.v2.Property;
import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
...@@ -32,7 +37,10 @@ import org.springframework.test.context.TestPropertySource; ...@@ -32,7 +37,10 @@ import org.springframework.test.context.TestPropertySource;
public class UriServiceTest { public class UriServiceTest {
@MockBean @MockBean
private NodeDAO dao; private NodeDAO nodeDAO;
@MockBean
private LocationDAO locationDAO;
@MockBean @MockBean
private ServletRapClient rapClient; private ServletRapClient rapClient;
...@@ -56,6 +64,13 @@ public class UriServiceTest { ...@@ -56,6 +64,13 @@ public class UriServiceTest {
} }
} }
@BeforeEach
public void init() {
Location location = new Location();
location.setType(LocationType.ASYNC);
when(locationDAO.getNodeLocation(any())).thenReturn(Optional.of(location));
}
@Test @Test
public void testPublicUrl() { public void testPublicUrl() {
...@@ -65,10 +80,10 @@ public class UriServiceTest { ...@@ -65,10 +80,10 @@ public class UriServiceTest {
property.setValue("true"); property.setValue("true");
node.getProperties().add(property); node.getProperties().add(property);
when(dao.listNode(eq("/mydata1"))).thenReturn(Optional.of(node)); when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));
JobSummary job = getJob(); JobSummary job = getJob();
uriService.setTransferJobResult(job); uriService.setTransferJobResult(job, uriService.getTransfer(job));
assertEquals("http://file-service/mydata1?jobId=job-id", job.getResults().get(0).getHref()); assertEquals("http://file-service/mydata1?jobId=job-id", job.getResults().get(0).getHref());
} }
...@@ -78,7 +93,7 @@ public class UriServiceTest { ...@@ -78,7 +93,7 @@ public class UriServiceTest {
Node node = new DataNode(); Node node = new DataNode();
when(dao.listNode(eq("/mydata1"))).thenReturn(Optional.of(node)); when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node));
User user = mock(User.class); User user = mock(User.class);
when(user.getAccessToken()).thenReturn("<token>"); when(user.getAccessToken()).thenReturn("<token>");
...@@ -92,11 +107,27 @@ public class UriServiceTest { ...@@ -92,11 +107,27 @@ public class UriServiceTest {
}), any())).thenReturn("<new-token>"); }), any())).thenReturn("<new-token>");
JobSummary job = getJob(); JobSummary job = getJob();
uriService.setTransferJobResult(job); uriService.setTransferJobResult(job, uriService.getTransfer(job));
assertEquals("http://file-service/mydata1?jobId=job-id&token=<new-token>", job.getResults().get(0).getHref()); assertEquals("http://file-service/mydata1?jobId=job-id&token=<new-token>", job.getResults().get(0).getHref());
} }
@Test
public void setNodeRemoteLocationTest() {
String nodeUri = "vos://example.com!vospace/test/f1/lbtfile.fits";
String contentUri = "http://archive.lbto.org/files/lbtfile.fits";
Location location = new Location();
location.setId(5);
when(locationDAO.findPortalLocation(eq("archive.lbto.org"))).thenReturn(Optional.of(location));
uriService.setNodeRemoteLocation(nodeUri, contentUri);
verify(nodeDAO).setNodeLocation(eq("/test/f1/lbtfile.fits"), eq(5), eq("lbtfile.fits"));
}
private JobSummary getJob() { private JobSummary getJob() {
Transfer transfer = new Transfer(); Transfer transfer = new Transfer();
......
package it.inaf.oats.vospace.persistence;
import it.inaf.oats.vospace.persistence.model.Location;
import java.util.Optional;
import javax.sql.DataSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {DataSourceConfig.class})
@TestPropertySource(locations = "classpath:test.properties")
public class LocationDAOTest {
@Autowired
private DataSource dataSource;
private LocationDAO dao;
@BeforeEach
public void init() {
dao = new LocationDAO(dataSource);
}
@Test
public void testGetPortalLocation() {
String hostname = "archive.lbto.org";
Optional<Location> optLocation = dao.findPortalLocation(hostname);
assertTrue(optLocation.isPresent());
Location location = optLocation.get();
assertEquals(hostname, location.getSource().getHostname());
}
@Test
public void testInexistentLocation() {
assertTrue(dao.findPortalLocation("foo").isEmpty());
}
@Test
public void testGetRootLocation() {
dao.getNodeLocation("/");
}
}
package it.inaf.oats.vospace.persistence; package it.inaf.oats.vospace.persistence;
import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeProperties;
import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.exception.InternalFaultException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.sql.DataSource; import javax.sql.DataSource;
...@@ -56,7 +56,7 @@ public class NodeDAOTest { ...@@ -56,7 +56,7 @@ public class NodeDAOTest {
ContainerNode root = (ContainerNode) dao.listNode("/").get(); ContainerNode root = (ContainerNode) dao.listNode("/").get();
assertEquals(2, root.getNodes().size()); assertEquals(2, root.getNodes().size());
assertEquals("true", NodeProperties.getNodePropertiesListByURI(root, NodeProperties.PUBLIC_READ_URI).get(0)); assertEquals("true", NodeProperties.getNodePropertyAsListByURI(root, NodeProperties.PUBLIC_READ_URI).get(0));
assertEquals("group1 group2", getProperty(root.getNodes().get(0), "ivo://ivoa.net/vospace/core#groupread")); assertEquals("group1 group2", getProperty(root.getNodes().get(0), "ivo://ivoa.net/vospace/core#groupread"));
} }
...@@ -90,6 +90,27 @@ public class NodeDAOTest { ...@@ -90,6 +90,27 @@ public class NodeDAOTest {
} }
@Test
public void testSetNodeLocation() {
DataNode dataNode = new DataNode();
dataNode.setUri("vos://example.com!vospace/mydata2");
dao.createNode(dataNode);
dao.setNodeLocation("/mydata2", 1, "mydata2");
}
@Test
public void testSetNodeLocationFailure() {
boolean exception = false;
try {
dao.setNodeLocation("/foo", 1, "foo");
} catch (InternalFaultException ex) {
exception = true;
}
assertTrue(exception);
}
private String getProperty(Node node, String uri) { private String getProperty(Node node, String uri) {
for (Property property : node.getProperties()) { for (Property property : node.getProperties()) {
if (uri.equals(property.getUri())) { if (uri.equals(property.getUri())) {
......
<vos:transfer xmlns:vos="http://www.ivoa.net/xml/VOSpace/v2.0" version="2.1">
<vos:target>vos://example.com!vospace/portalnode</vos:target>
<vos:direction>pullToVoSpace</vos:direction>
<vos:protocol uri="ivo://ivoa.net/vospace/core#httpget">
<vos:endpoint>http://archive.lbto.org/files/lbcr.20130512.060722.fits.gz</vos:endpoint>
</vos:protocol>
</vos:transfer>
\ No newline at end of file
<vos:transfer xmlns:vos="http://www.ivoa.net/xml/VOSpace/v2.0" version="2.1"> <vos:transfer xmlns:vos="http://www.ivoa.net/xml/VOSpace/v2.0" version="2.1">
<vos:target>vos://example.com!vospace/mynode</vos:target> <vos:target>vos://example.com!vospace/mynode</vos:target>
<vos:direction>pullToVoSpace</vos:direction> <vos:direction>pullToVoSpace</vos:direction>
<vos:protocol uri="ivo://ivoa.net/vospace/core#httpget" /> <vos:protocol uri="ia2:async-recall" />
</vos:transfer> </vos:transfer>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment